Android项目实战之仿网易新闻的页面(RecyclerView )

时间:2022-04-22 09:24:53

本文实例实现一个仿网易新闻的页面,上面是轮播的图片,下面是 RecyclerView 显示新闻列表,具体内容如下

错误方法

?
1
2
3
4
5
6
7
<?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布局

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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布局

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?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 布局

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<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 的适配器

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/**
 * 新闻列表的适配器
 * 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,大家可以进行研究。

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