Welcome 微信登录

首页 / 移动开发 / Android / 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.RecyclerViewandroid: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 热门文章图片展示--> <FrameLayoutandroid: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"> <LinearLayoutandroid: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"> <TextViewandroid: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" /> <!-- 新闻 发布时间 来源 阅读次数--> <LinearLayoutandroid: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> <TextViewandroid: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 sscc实现了不加 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为选中图标的 indexcurrentIndex = 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后执行,周期是5sTimer 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,大家可以进行研究。
以上就是本文的全部内容,希望对大家的学习有所帮助。