Android Material Design 之路之 RecyclerView 学习笔记

前言

Android Material Design越来越流行,以前很常用的 ListView 现在也用RecyclerView代替了,实现原理还是相似的。笔者实现一下 RecyclerView,代码比较简单,适合初学者,如有错误,欢迎指出。

源码地址(欢迎star) https://github.com/studychen/SeeNewsV2

本文链接 http://www.alijava.com/recyclerview-basics/ 转载请注明出处

复习 ListView

ListView 实现过程中需要复写BaseAdapter,主要是这4个方法

  • public int getCount() :适配器中数据集中 数据的个数,即ListView需要显示的数据个数
  • public Object getItem(int position) : 获取数据集中与指定索引对应的数据项
  • public long getItemId(int position) : 获取指定行对应的ID
  • public View getView(int position, View convertView, ViewGroup parent) :获取每一个Item的显示内容

一般 ListView 每一项都是相同的布局,若想各个项实现不同的布局,可复写 getItemViewTypegetViewTypeCount实现

RecyclerView 实现

xml 布局

RecyclerView中每一项的布局 item_article_type_1.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
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
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"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="5dp"
app:cardElevation="5dp"
app:contentPadding="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/rcv_article_photo"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerVertical="true"
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="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:ellipsize="end"
android:maxLines="2"
android:text="讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识..." />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

布局思路就是 CardView里面嵌入了一个LinearLayout。图片部分用固定宽度100dp,文字部分利用android:layout_weight="1"占据了其他部分。

TextView利用android:gravity="center"使得标题的文字居中。

LinearLayout里面利用android:gravity="center"使得"2015-01-09 科学研究院 1129次"居中,

新闻详情内容的TextView利用

1
2
android:maxLines="2"
android:ellipsize="end"

将文章内容限定为2行,超出部分用省略号显示。

预览效果

这里写图片描述

新闻列表的 xml 文件,fragment_article.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?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"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

实现 Adapter

主要步骤是:

  • 根据上面的 item_article_type_1.xml实现一个 class ImageItemArticleViewHolder extends RecyclerView.ViewHolder
  • 实现ItemArticleListAdapter extends RecyclerView.Adapter <>
    重写三个方法
    • public int getItemCount()
    • public TestAdapter.ImageItemArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    • public void onBindViewHolder(ImageItemArticleViewHolder holder, int position)
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
public class ItemArticleAdapter extends RecyclerView.Adapter<ItemArticleAdapter.ImageItemArticleViewHolder> {
//新闻列表
private List<ItemArticle> articleList;
//context
private Context context;
private LayoutInflater mLayoutInflater;
public ItemArticleAdapter(Context context,List<ItemArticle> articleList) {
this.context = context;
this.articleList = articleList;
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public ItemArticleAdapter.ImageItemArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mLayoutInflater.inflate(
R.layout.item_article_type_1, parent, false);
return new ImageItemArticleViewHolder(view);
}
@Override
public void onBindViewHolder(ImageItemArticleViewHolder holder, int position) {
ItemArticle article = articleList.get(position);
holder.rcvArticlePhoto.setImageURI(Uri.parse(article.getImageUrl()));
holder.rcvArticleTitle.setText(article.getTitle());
holder.rcvArticleDate.setText(article.getPublishDate());
holder.rcvArticleSource.setText(article.getSource());
//注意这个阅读次数是 int 类型,需要转化为 String 类型
holder.rcvArticleReadtimes.setText(article.getReadTimes()+"次");
holder.rcvArticlePreview.setText(article.getPreview());
}
@Override
public int getItemCount() {
return articleList.size();
}
class ImageItemArticleViewHolder 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 ImageItemArticleViewHolder(View itemView) {
super(itemView);
ButterKnife.inject(this, itemView);
}
}
}

新闻实体类 javabean

有新闻的 index,图片 url,标题,发布时间,来源,阅读次数,新闻内容预览

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
/**
* 新闻类,这是在 RecycleView 使用的新闻 javabean
* 还有一个新闻详情javabean
*/
public class ItemArticle {
private int index;
private String imageUrl;
private String title;
private String publishDate;
private String source;
private int readTimes;
private String preview;
public ItemArticle(int index, String imageUrl, String title, String publishDate, String source, int readTimes, String preview) {
this.index = index;
this.imageUrl = imageUrl;
this.title = title;
this.publishDate = publishDate;
this.source = source;
this.readTimes = readTimes;
this.preview = preview;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPublishDate() {
return publishDate;
}
public void setPublishDate(String publishDate) {
this.publishDate = publishDate;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public int getReadTimes() {
return readTimes;
}
public void setReadTimes(int readTimes) {
this.readTimes = readTimes;
}
public String getPreview() {
return preview;
}
public void setPreview(String preview) {
this.preview = preview;
}
}

fragment 里面使用 RecyclerView

思路就是开启一个异步线程,读取多条新闻,加入List<ItemArticle>,由这个List构造ItemArticleAdapter,最后利用setAdapter()方法给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
public class ArticleFragment extends Fragment {
private static final String STORE_PARAM = "param";
@InjectView(R.id.rcv_article)
RecyclerView rcvArticle;
private String mParam;
//新闻列表数据
private List<ItemArticle> itemArticleList = new ArrayList<ItemArticle>();
//获取 fragment 依赖的 Activity,方便使用 Context
private Activity mAct;
public static Fragment newInstance(String param) {
ArticleFragment fragment = new ArticleFragment();
Bundle args = new Bundle();
args.putString(STORE_PARAM, param);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam = getArguments().getString(STORE_PARAM);
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_article, null);
Log.i(STORE_PARAM, "in StoreFragment");
mAct = getActivity();
ButterKnife.inject(this, view);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
rcvArticle.setLayoutManager(new LinearLayoutManager(mAct));//这里用线性显示 类似于listview
// rcvArticle.setLayoutManager(new GridLayoutManager(mAct, 2));//这里用线性宫格显示 类似于grid view
// rcvArticle.setLayoutManager(new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL));//这里用线性宫格显示 类似于瀑布流
new LatestArticleTask().execute();
}
@Override
public void onDestroyView() {
super.onDestroyView();
ButterKnife.reset(this);
}
class LatestArticleTask extends AsyncTask<String, Void, List<ItemArticle>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected List<ItemArticle> doInBackground(String... params) {
ItemArticle storeInfo1 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo2 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo3 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo4 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo5 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo6 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
itemArticleList.add(storeInfo1);
itemArticleList.add(storeInfo2);
itemArticleList.add(storeInfo3);
itemArticleList.add(storeInfo4);
itemArticleList.add(storeInfo5);
itemArticleList.add(storeInfo6);
return itemArticleList;
}
@Override
protected void onPostExecute(List<ItemArticle> data) {
super.onPostExecute(data);
ItemArticleAdapter adapter = new ItemArticleAdapter(mAct, data);
rcvArticle.setAdapter(adapter);
}
}
}

效果图

利用修改布局,线性显示或者宫格显示 (以前宫格显示很麻烦,现在一条命令就好了)

1
2
3
rcvArticle.setLayoutManager(new LinearLayoutManager(mAct));//这里用线性显示 类似于listview
// rcvArticle.setLayoutManager(new GridLayoutManager(mAct, 2));//这里用线性宫格显示 类似于grid view
// rcvArticle.setLayoutManager(new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL));//这里用线性宫格显示 类似于瀑布流

效果图

线性布局

统计结果1

线性宫格布局

统计结果1

总结

int 转化为 String

TextViewsetText(int resid) 方法,但是这儿 int 表示 resourceId,如果我想把阅读次数(int 1123)赋给这个 TextView,不能使用这个方法。

1
2
3
4
5
6
//int 转 String 有三种方法
int i =8;
String s =Integer.toString(i);
String g =String.valueOf(i);
String h =i+"";
holder.rcvArticleReadtimes.setText(String.valueOf(article.getReadTimes()));

Todo List

  • Picasso 图片缓存库的学习
  • 实现 RecyclerView 每个项各自的布局

源码地址(欢迎star) https://github.com/studychen/SeeNewsV2

参考文章

Android RecyclerView Example | Java Techig