仿网易新闻的页面(ViewPager作为RecyclerView的Header)

ShaunJohnso 8年前

来自: http://blog.csdn.net//never_cxb/article/details/50520270


需求

>
想实现一个仿网易新闻的页面,上面是轮播的图片,下面是 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.非死book.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 http://blog.csdn.net/never_cxb/article/details/50495505实现了不加 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,要复写一些方法,可以参考这篇文章
Doing it right: vertical ScrollView with ViewPager and ListViewhttps://pristalovpavel.wordpress.com/2014/12/26/doing-it-right-vertical-scrollview-with-viewpager-and-listview/

参考文章

Placing ViewPager as a row in ListView http://stackoverflow.com/questions/14920459/placing-viewpager-as-a-row-in-listview
ListView inside ViewPager inside ScrollViewhttp://stackoverflow.com/questions/23429151/listview-inside-viewpager-inside-scrollview
RecyclerView:实现带header的grid
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0722/3214.html