Android项目实战之仿网易新闻的页面(RecyclerView )
本文实例实现一个仿网易新闻的页面,上面是轮播的图片,下面是 recyclerview 显示新闻列表,具体内容如下
错误方法
<?xml version="1.0" encoding="utf-8"?> <linearlayout ...> <viewpager ... /> <android.support.v7.widget.recyclerview .../> </linearlayout>
这样布局 viewpager 在 recyclerview 的上面,如果不做特殊处理,当下滑 recyclerview 加载更多内容的时候,viewpager会固定不动。
正确的效果是下滑加载更多的时候,viewpager 会滑出页面,释放空间供其他内容展示。
一、解决思路
方法有两种
- viewpager作为 recyclerview 的第0项,也就是 header(本文采用该方法)
- 利用scrollview,重写一些方法解决滑动冲突
总xml布局
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.recyclerview android:id="@+id/rcv_article_latest" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </linearlayout>
很简单,一个recyclerview就行了
头部 viewpager 的viewholder_article_header.xml布局
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--viewpager 热门文章图片展示--> <framelayout android:layout_width="match_parent" android:layout_height="200dp" android:background="@color/gray_light"> <android.support.v4.view.viewpager android:id="@+id/vp_hottest" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorprimary" /> <linearlayout android:id="@+id/ll_hottest_indicator" android:layout_width="wrap_content" android:layout_height="20dp" android:layout_gravity="bottom|right" android:layout_marginbottom="5dp" android:layout_marginright="10dp" android:layout_margintop="5dp" android:gravity="center" android:orientation="horizontal" /> </framelayout> </linearlayout>
framelayout里面的viewpager和linearlayout是覆盖显示的,实现在图片的下方有个小圆点标记滑动到了第一张图片。
新闻项 viewholder_article_item.xml 布局
<android.support.v7.widget.cardview xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:fresco="http://schemas.android.com/apk/res-auto" android:id="@+id/cv_item" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardcornerradius="5dp" app:cardelevation="5dp" app:contentpadding="2dp"> <linearlayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <com.facebook.drawee.view.simpledraweeview android:id="@+id/rcv_article_photo" android:layout_width="100dp" android:layout_height="100dp" fresco:actualimagescaletype="centerinside" fresco:roundascircle="true" fresco:roundingbordercolor="@color/lightslategray" fresco:roundingborderwidth="1dp" /> <linearlayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical"> <textview android:id="@+id/rcv_article_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="10dp" android:layout_margintop="2dp" android:gravity="center" android:text="关于举办《经典音乐作品欣赏与人文审美》讲座的通知" android:textcolor="@color/primary_text" /> <!-- 新闻 发布时间 来源 阅读次数--> <linearlayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margintop="5dp" android:gravity="center" android:orientation="horizontal"> <textview android:id="@+id/rcv_article_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="10dp" android:layout_marginright="2dp" android:text="2015-01-09" /> <textview android:id="@+id/rcv_article_source" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="2dp" android:layout_marginright="2dp" android:text="科学研究院" /> <textview android:id="@+id/rcv_article_readtimes" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="2dp" android:layout_marginright="2dp" android:text="1129次" /> </linearlayout> <textview android:id="@+id/rcv_article_preview" android:layout_width="wrap_content" android:layout_height="0dp" android:layout_weight="1" android:layout_marginleft="10dp" android:layout_margintop="5dp" android:ellipsize="end" android:maxlines="2" android:text="讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识..." /> </linearlayout> </linearlayout> </android.support.v7.widget.cardview>
这篇文章 android material design学习之recyclerview代替 listview 实现了不加 viewpager,利用 recyclerview 展示新闻列表的功能。
recyclerview 的适配器
/** * 新闻列表的适配器 * 01-14 头部是 viewpager,下面是列表新闻 * created by tomchen on 1/11/16. */ public class articleadapter extends recyclerview.adapter<recyclerview.viewholder> { private static final int type_header = 0; private static final int type_item = 1; //头部固定为 张图片 private static final int num_image = 4; //handler 用到的参数值 private static final int uptate_viewpager = 0; //新闻列表 private list<itemarticle> articlelist; //设置当前 第几个图片 被选中 private int currentindex = 0; //context private context context; private layoutinflater mlayoutinflater; private imageview[] mcircleimages;//底部只是当前页面的小圆点 public articleadapter(context context, list<itemarticle> articlelist) { this.context = context; //头部viewpager图片固定是7张,剩下的是列表的数据 this.articlelist = articlelist; mlayoutinflater = layoutinflater.from(context); } @override public recyclerview.viewholder oncreateviewholder(viewgroup parent, int viewtype) { //理论上应该把最可能返回的 type 放在前面 view view = null; if (viewtype == type_item) { view = mlayoutinflater.inflate( r.layout.viewholder_article_item, parent, false); return new itemarticleviewholder(view); } //头部返回 viewpager 实现的轮播图片 if (viewtype == type_header) { view = mlayoutinflater.inflate( r.layout.viewholder_article_header, parent, false); return new headerarticleviewholder(view); } return null; // //可以抛出异常,没有对应的view类型 // throw new runtimeexception("there is no type that matches the type " + viewtype + " + make sure your using types correctly"); } @override public void onbindviewholder(recyclerview.viewholder holder, int position) { if (holder instanceof itemarticleviewholder) { //转型 itemarticleviewholder newholder = (itemarticleviewholder) holder; //注意recyclerview第0项是 viewpager 占据了0 1 2 3图片 //那么下面的列表展示是 recyclerview 的第1项,从第4项开始 itemarticle article = articlelist.get(position + num_image - 1); newholder.rcvarticlephoto.setimageuri(uri.parse(article.getimageurl())); newholder.rcvarticletitle.settext(article.gettitle()); newholder.rcvarticledate.settext(article.getpublishdate()); newholder.rcvarticlesource.settext(article.getsource()); //注意这个阅读次数是 int 类型,需要转化为 string 类型 newholder.rcvarticlereadtimes.settext(article.getreadtimes() + "次"); newholder.rcvarticlepreview.settext(article.getpreview()); } else if (holder instanceof headerarticleviewholder) { headerarticleviewholder newholder = (headerarticleviewholder) holder; list<itemarticle> headers = articlelist.sublist(0, num_image ); headerimageadapter imageadapter = new headerimageadapter(context, headers); setupviewpager(newholder.vphottest, newholder.llhottestindicator, headers); } } private void setupviewpager(final viewpager vp, linearlayout llbottom, final list<itemarticle> headerarticles) { headerimageadapter imageadapter = new headerimageadapter(context, headerarticles); //??这儿有些疑惑,adapter 里面嵌套设置 adapter 是否优雅? vp.setadapter(imageadapter); final handler mhandler = new handler() { public void handlemessage(message msg) { switch (msg.what) { case uptate_viewpager: if (msg.arg1 != 0) { vp.setcurrentitem(msg.arg1); } else { //false 当从末页调到首页是,不显示翻页动画效果, vp.setcurrentitem(msg.arg1, false); } break; } } }; //下面是设置动画切换的样式 vp.setpagetransformer(true, new rotateuptransformer()); //创建底部指示位置的导航栏 final imageview[] mcircleimages = new imageview[headerarticles.size()]; for (int i = 0; i < mcircleimages.length; i++) { imageview imageview = new imageview(context); linearlayout.layoutparams params = new linearlayout.layoutparams(10, 10); params.setmargins(5, 0, 5, 0); imageview.setlayoutparams(params); if (i == 0) { imageview.setbackgroundresource(r.drawable.indicator_select); } else { imageview.setbackgroundresource(r.drawable.indicator_not_select); } mcircleimages[i] = imageview; //把指示作用的原点图片加入底部的视图中 llbottom.addview(mcircleimages[i]); } vp.addonpagechangelistener(new viewpager.onpagechangelistener() { //图片左右滑动时候,将当前页的圆点图片设为选中状态 @override public void onpageselected(int position) { // 一定几个图片,几个圆点,但注意是从0开始的 int total = mcircleimages.length; for (int j = 0; j < total; j++) { if (j == position) { mcircleimages[j].setbackgroundresource(r.drawable.indicator_select); } else { mcircleimages[j].setbackgroundresource(r.drawable.indicator_not_select); } } //设置全局变量,currentindex为选中图标的 index currentindex = position; } @override public void onpagescrolled(int i, float v, int i1) { } @override public void onpagescrollstatechanged(int state) { //实现切换到末尾后返回到第一张 switch (state) { // 手势滑动 case viewpager.scroll_state_dragging: break; // 界面切换中 case viewpager.scroll_state_settling: break; case viewpager.scroll_state_idle:// 滑动结束,即切换完毕或者加载完毕 // 当前为最后一张,此时从右向左滑,则切换到第一张 if (vp.getcurrentitem() == vp.getadapter() .getcount() - 1) { vp.setcurrentitem(0, false); } // 当前为第一张,此时从左向右滑,则切换到最后一张 else if (vp.getcurrentitem() == 0) { vp.setcurrentitem(vp.getadapter() .getcount() - 1, false); } break; default: break; } } }); //设置自动轮播图片,5s后执行,周期是5s timer timer = new timer(); timer.schedule(new timertask() { @override public void run() { message message = new message(); message.what = uptate_viewpager; if (currentindex == headerarticles.size() - 1) { currentindex = -1; } message.arg1 = currentindex + 1; mhandler.sendmessage(message); } }, 6000, 6000); } @override public int getitemcount() { //因为多了一个头部,所以是+1,但是头部 viewpager 占了7个 //所以实际是少了6个 return articlelist.size() + 1 - num_image; } @override public int getitemviewtype(int position) { if (position == 0) return type_header; else return type_item; } class headerarticleviewholder extends recyclerview.viewholder { //轮播的最热新闻图片 @injectview(r.id.vp_hottest) viewpager vphottest; //轮播图片下面的小圆点 @injectview(r.id.ll_hottest_indicator) linearlayout llhottestindicator; //学院广播信息 @injectview(r.id.tv_college_broadcast) textview tvcollegebroadcast; public headerarticleviewholder(view itemview) { super(itemview); butterknife.inject(this, itemview); } } class itemarticleviewholder extends recyclerview.viewholder { @injectview(r.id.rcv_article_photo) simpledraweeview rcvarticlephoto; @injectview(r.id.rcv_article_title) textview rcvarticletitle; @injectview(r.id.rcv_article_date) textview rcvarticledate; @injectview(r.id.rcv_article_source) textview rcvarticlesource; @injectview(r.id.rcv_article_readtimes) textview rcvarticlereadtimes; @injectview(r.id.rcv_article_preview) textview rcvarticlepreview; public itemarticleviewholder(view itemview) { super(itemview); butterknife.inject(this, itemview); } } }
itemarticleviewholder是列表展示的新闻项的 viewholder,对应了上面的 viewholder_article_item.xml。
headerarticleviewholder 是头部 viewpager 的 viewholder, 对应viewholder_article_header.xml
note
- 本文上面的 viewpager 轮播4幅图片,所以getitemcount()需要复写
- list headers = articlelist.sublist(0, num_image );得到头部图片的数据
- itemarticle article = articlelist.get(position + num_image - 1);得到下面新闻项的数据
- getitemviewtype(int position)根据position判断是不是头部viewpager
- oncreateviewholder(viewgroup parent, int viewtype)根据viewtype生成头部图片或者下面新闻项的viewholder
二、疑惑及后续计划
除了将 viewpager 作为 recyclerview 第一项,还有一张方法就是利用scrollview,大家可以进行研究。
以上就是本文的全部内容,希望对大家的学习有所帮助。