Android项目实战之仿网易新闻的页面(RecyclerView )
本文实例实现一个仿网易新闻的页面,上面是轮播的图片,下面是RecyclerView显示新闻列表,具体内容如下
错误方法
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayout...> <ViewPager.../> <android.support.v7.widget.RecyclerView.../> </LinearLayout>
这样布局ViewPager在RecyclerView的上面,如果不做特殊处理,当下滑RecyclerView加载更多内容的时候,ViewPager会固定不动。
正确的效果是下滑加载更多的时候,ViewPager会滑出页面,释放空间供其他内容展示。
一、解决思路
方法有两种
- ViewPager作为RecyclerView的第0项,也就是Header(本文采用该方法)
- 利用ScrollView,重写一些方法解决滑动冲突
总xml布局
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns: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布局
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns: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>
这篇文章AndroidMaterialDesign学习之RecyclerView代替ListViewsscc实现了不加ViewPager,利用RecyclerView展示新闻列表的功能。
RecyclerView的适配器
/** *新闻列表的适配器 *01-14头部是ViewPager,下面是列表新闻 *Createdbytomchenon1/11/16. */ publicclassArticleAdapterextendsRecyclerView.Adapter<RecyclerView.ViewHolder>{ privatestaticfinalintTYPE_HEADER=0; privatestaticfinalintTYPE_ITEM=1; //头部固定为张图片 privatestaticfinalintNUM_IMAGE=4; //Handler用到的参数值 privatestaticfinalintUPTATE_VIEWPAGER=0; //新闻列表 privateList<ItemArticle>articleList; //设置当前第几个图片被选中 privateintcurrentIndex=0; //context privateContextcontext; privateLayoutInflatermLayoutInflater; privateImageView[]mCircleImages;//底部只是当前页面的小圆点 publicArticleAdapter(Contextcontext,List<ItemArticle>articleList){ this.context=context; //头部viewpager图片固定是7张,剩下的是列表的数据 this.articleList=articleList; mLayoutInflater=LayoutInflater.from(context); } @Override publicRecyclerView.ViewHolderonCreateViewHolder(ViewGroupparent,intviewType){ //理论上应该把最可能返回的TYPE放在前面 Viewview=null; if(viewType==TYPE_ITEM){ view=mLayoutInflater.inflate( R.layout.viewholder_article_item,parent,false); returnnewItemArticleViewHolder(view); } //头部返回ViewPager实现的轮播图片 if(viewType==TYPE_HEADER){ view=mLayoutInflater.inflate( R.layout.viewholder_article_header,parent,false); returnnewHeaderArticleViewHolder(view); } returnnull; ////可以抛出异常,没有对应的View类型 //thrownewRuntimeException("thereisnotypethatmatchesthetype"+viewType+"+makesureyourusingtypescorrectly"); } @Override publicvoidonBindViewHolder(RecyclerView.ViewHolderholder,intposition){ if(holderinstanceofItemArticleViewHolder){ //转型 ItemArticleViewHoldernewHolder=(ItemArticleViewHolder)holder; //注意RecyclerView第0项是ViewPager占据了0123图片 //那么下面的列表展示是RecyclerView的第1项,从第4项开始 ItemArticlearticle=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()); }elseif(holderinstanceofHeaderArticleViewHolder){ HeaderArticleViewHoldernewHolder=(HeaderArticleViewHolder)holder; List<ItemArticle>headers=articleList.subList(0,NUM_IMAGE); HeaderImageAdapterimageAdapter=newHeaderImageAdapter(context,headers); setUpViewPager(newHolder.vpHottest,newHolder.llHottestIndicator,headers); } } privatevoidsetUpViewPager(finalViewPagervp,LinearLayoutllBottom,finalList<ItemArticle>headerArticles){ HeaderImageAdapterimageAdapter=newHeaderImageAdapter(context,headerArticles); //??这儿有些疑惑,Adapter里面嵌套设置Adapter是否优雅? vp.setAdapter(imageAdapter); finalHandlermHandler=newHandler(){ publicvoidhandleMessage(Messagemsg){ switch(msg.what){ caseUPTATE_VIEWPAGER: if(msg.arg1!=0){ vp.setCurrentItem(msg.arg1); }else{ //false当从末页调到首页是,不显示翻页动画效果, vp.setCurrentItem(msg.arg1,false); } break; } } }; //下面是设置动画切换的样式 vp.setPageTransformer(true,newRotateUpTransformer()); //创建底部指示位置的导航栏 finalImageView[]mCircleImages=newImageView[headerArticles.size()]; for(inti=0;i<mCircleImages.length;i++){ ImageViewimageView=newImageView(context); LinearLayout.LayoutParamsparams=newLinearLayout.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(newViewPager.OnPageChangeListener(){ //图片左右滑动时候,将当前页的圆点图片设为选中状态 @Override publicvoidonPageSelected(intposition){ //一定几个图片,几个圆点,但注意是从0开始的 inttotal=mCircleImages.length; for(intj=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 publicvoidonPageScrolled(inti,floatv,inti1){ } @Override publicvoidonPageScrollStateChanged(intstate){ //实现切换到末尾后返回到第一张 switch(state){ //手势滑动 caseViewPager.SCROLL_STATE_DRAGGING: break; //界面切换中 caseViewPager.SCROLL_STATE_SETTLING: break; caseViewPager.SCROLL_STATE_IDLE://滑动结束,即切换完毕或者加载完毕 //当前为最后一张,此时从右向左滑,则切换到第一张 if(vp.getCurrentItem()==vp.getAdapter() .getCount()-1){ vp.setCurrentItem(0,false); } //当前为第一张,此时从左向右滑,则切换到最后一张 elseif(vp.getCurrentItem()==0){ vp.setCurrentItem(vp.getAdapter() .getCount()-1,false); } break; default: break; } } }); //设置自动轮播图片,5s后执行,周期是5s Timertimer=newTimer(); timer.schedule(newTimerTask(){ @Override publicvoidrun(){ Messagemessage=newMessage(); message.what=UPTATE_VIEWPAGER; if(currentIndex==headerArticles.size()-1){ currentIndex=-1; } message.arg1=currentIndex+1; mHandler.sendMessage(message); } },6000,6000); } @Override publicintgetItemCount(){ //因为多了一个头部,所以是+1,但是头部ViewPager占了7个 //所以实际是少了6个 returnarticleList.size()+1-NUM_IMAGE; } @Override publicintgetItemViewType(intposition){ if(position==0) returnTYPE_HEADER; else returnTYPE_ITEM; } classHeaderArticleViewHolderextendsRecyclerView.ViewHolder{ //轮播的最热新闻图片 @InjectView(R.id.vp_hottest) ViewPagervpHottest; //轮播图片下面的小圆点 @InjectView(R.id.ll_hottest_indicator) LinearLayoutllHottestIndicator; //学院广播信息 @InjectView(R.id.tv_college_broadcast) TextViewtvCollegeBroadcast; publicHeaderArticleViewHolder(ViewitemView){ super(itemView); ButterKnife.inject(this,itemView); } } classItemArticleViewHolderextendsRecyclerView.ViewHolder{ @InjectView(R.id.rcv_article_photo) SimpleDraweeViewrcvArticlePhoto; @InjectView(R.id.rcv_article_title) TextViewrcvArticleTitle; @InjectView(R.id.rcv_article_date) TextViewrcvArticleDate; @InjectView(R.id.rcv_article_source) TextViewrcvArticleSource; @InjectView(R.id.rcv_article_readtimes) TextViewrcvArticleReadtimes; @InjectView(R.id.rcv_article_preview) TextViewrcvArticlePreview; publicItemArticleViewHolder(ViewitemView){ super(itemView); ButterKnife.inject(this,itemView); } } }
ItemArticleViewHolder是列表展示的新闻项的ViewHolder,对应了上面的viewholder_article_item.xml。
HeaderArticleViewHolder是头部ViewPager的ViewHolder,对应viewholder_article_header.xml
Note
- 本文上面的ViewPager轮播4幅图片,所以getItemCount()需要复写
- Listheaders=articleList.subList(0,NUM_IMAGE);得到头部图片的数据
- ItemArticlearticle=articleList.get(position+NUM_IMAGE-1);得到下面新闻项的数据
- getItemViewType(intposition)根据position判断是不是头部ViewPager
- onCreateViewHolder(ViewGroupparent,intviewType)根据viewType生成头部图片或者下面新闻项的ViewHolder
二、疑惑及后续计划
除了将ViewPager作为RecyclerView第一项,还有一张方法就是利用ScrollView,大家可以进行研究。
以上就是本文的全部内容,希望对大家的学习有所帮助。