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 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为选中图标的 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,大家可以进行研究。

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • Android控件RecyclerView实现混排效果仿网易云音乐

    前言 最近在使用网易云音乐的时候,看到如下图的排版效果图,自己也想实现一个 这里采用网上用法最多的方式,而且是比较简单的方式实现的,想要做项目的同学也可以快速入手搞定首页界面,可以在最快的时间内模仿出来,且效果达到90%以上的相似 效果演示 至于图片的加载你们可以根据网上的Api获取相应的图片加载到对应的位置,这里只是采用本地图片来演示 实现分析 这里是采用RecyclerView的GridLayoutManager的一个SpanSize这么一个东西,从下图很容易知道其意思 项目结构 项目结构可

  • Android使用RecyclerView实现水平滚动控件

    前言 相信大家都知道Android滚动控件的实现方式有很多, 使用RecyclerView也比较简单. 做了一个简单的年龄滚动控件, 让我们来看看RecyclerView的使用方式, 主要有以下几点: (1) 对齐控件中心位置. (2) 计算滚动距离. (3) 高亮中心视图. (4) 实时显示中心数据. (5) 停止时自动对齐. (6) 滚动时, 设置按钮状态开关. 效果 1. 框架 主要关注RecyclerView部分逻辑. /** * 初始化年龄滑动条 */ private void ini

  • Android中RecyclerView点击Item设置事件

    在上一篇Android RecylerView入门教程中提到,RecyclerView不再负责Item视图的布局及显示,所以RecyclerView也没有为Item开放OnItemClick等点击事件,这就需要开发者自己实现.博客最下面有Demo程序运行动画. 奉上Demo的Github链接. 在调研过程中,发现有同学修改RecyclerView源码来实现Item的点击监听,但认为这不是一个优雅的解决方案,最终决定在RecyclerView.ViewHolder上做文章. 思路是:因为ViewH

  • Android中RecyclerView实现横向滑动代码

    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍Android中RecyclerView实现横向滑动代码,一起看看吧. android.support.v7.widget.RecyclerView 功能:RecyclerView横向滑动 控件:<android.support.v7.widget.RecyclerView /> Java类:RecyclerView.GalleryAdap

  • Android RecyclerView网格布局(支持多种分割线)详解(2)

    上篇Android RecyclerView 详解(1)-线性布局 记录了下RecyclerView的使用方法,并且讲述了线性布局列表的使用方法,在此基础上加上了万能分割线,支持颜色分割线和图片分割线,同时支持对分割线设置线宽. 这篇是总结一下网格布局的使用,同样也支持两种分割线和线宽的设置. 主要的相关类: 1. RecyclerView.Adapter 2. GridLayoutManager 网格布局管理器 3. RecycleView.ItemDecoration 分割线 下面就直接通过

  • Android RecyclerView实现下拉刷新和上拉加载

    RecyclerView已经出来很久了,许许多多的项目都开始从ListView转战RecyclerView,那么,上拉加载和下拉刷新是一件很有必要的事情. 在ListView上,我们可以通过自己添加addHeadView和addFootView去添加头布局和底部局实现自定义的上拉和下拉,或者使用一些第三方库来简单的集成,例如Android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定义更强,但需要自己实现上拉加载. 而在下面我们将用两种方式

  • Android中使用RecyclerView实现下拉刷新和上拉加载

    推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

  • Android中RecyclerView布局代替GridView实现类似支付宝的界面

    单纯使用GridView 通用的两种给GridView 添加分割线的方法:http://stackoverflow.com/questions/7132030/android-gridview-draw-dividers 给Gridview 添加分割线,也就是实现网格布局,不清楚谷歌为什么没有给Gridview 添加一个类似 ListView 的Divider 属性,因此就需要我们自己去添加分割线, 目前两种方法,第一种是 利用GridView 的  android:horizontalSpac

  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

    RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

  • Android中RecyclerView嵌套滑动冲突解决的代码片段

    在纵向RecyclerView嵌套横向RecyclerView时,如果纵向RecyclerView有下拉刷新功能,那么内部的横向RecyclerView的横向滑动体验会很差.(只有纯横向滑动时,才能滑动内部的横向RecyclerView,否则滑动事件就会影响到下拉刷新),添加拦截判断. public class MySwipeRefreshLayout extends SwipeRefreshLayout { private boolean mIsVpDragger; private final

随机推荐