学习RecyclerView笔记

RecyclerView 是一个非常强大的控件,甚至它可以替代 ListView 和 GridView ,如果想使用 RecyclerView ,就需要了解它的优点、以及它的缺点,这样才可以更好地使用这一款控件。

  • RecyclerView 的优点:
    • 高度的解耦
    • 异常的灵活性和更高的灵活性
    • 通够设置它提供的不同 LayoutManager 、ItemDecoration 、ItemAnimator可以实现丰富多彩的效果
  • RecyclerView 的缺点:
    • 设置列表的分割线需要自定义
    • 需要自己实现点击事件

下面我们需要实现的 RecyclerView 的效果如下

RecyclerView效果图

实现上面的图片的效果,我们需要实现的代码逻辑有:

  • 添加页面的布局代码
  • 创建继承于 RecyclerView.Adapter 的 Adapter
  • 向自定义的 Adapter 里面添加数据
  • 自定义 item 的分割线
  • 自定 item 的监听事件

1.0 添加页面的布局代码

activity_main.xml 的布局代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.test.learnrecyclerview.MainActivity"
tools:layout_editor_absoluteY="81dp"
tools:layout_editor_absoluteX="0dp">

<android.support.v7.widget.RecyclerView
android:id="@+id/id_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent">

</android.support.v7.widget.RecyclerView>

</RelativeLayout>

item_recycler.xml 的布局代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:background="@android:color/white"
>

<TextView
android:id="@+id/tv_item"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="moon"/>
</FrameLayout>

因为实现 RecyclerView 的页面布局的代码比较简单,在这里就不做详细的说明了;

2.0 创建继承于 RecyclerView.Adapter 的 Adapter

Adapter 最大的改进就是对 ViewHolder 进行封装:

1
public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>

对于需要继承 RecyclerView.Adapter<HomeAdapter.MyViewHolder> 这个泛型类,我的理解是这样的,因为在自定义的 Adapter 里面,需要重写 public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 方法,以及重写 public void onBindViewHolder(final MyViewHolder holder, final int position) 着一个方法,方法一返回的 MyViewHolder 的对象实例,方法二是如何知道它自己拿到的就是方法一返回的对象实例呢?只有通过泛型类,限制了方法二拿到的对象类型,这样才可以确保方法二拿到的是方法一的对象实例;

HomeAdapter.java 的代码逻辑如下:

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
public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder> {

private Context mContext;
private List<String> mList;
private OnItemClickListtener mOnItemClickListtener;
private List<Integer> mHeights;

public HomeAdapter(Context mContext, List<String> mList) {
this.mContext = mContext;
this.mList = mList;

mHeights = new ArrayList<>();
for (int i = 0; i < mList.size(); i++){
mHeights.add((int)(100 + Math.random()*300));
}
}

public void removeDate(int position) {
mList.remove(position);
notifyItemRemoved(position);
}


@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//[1.0] 使用布局加载器加载xml布局,使得xml布局转换为view对象
View inflate = LayoutInflater.from(mContext).inflate(R.layout.item_recycler, parent, false);
//[2.0] 把view对象装进继承于 RecyclerView.ViewHolder() 的 MyViewHolder 里面
MyViewHolder holder = new MyViewHolder(inflate);


return holder;
}

@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
//[3.0] 在 MyViewHolder 的构造器里面初始化对应的组件以后,就可以把数据绑定到对应的组件
holder.mTextView.setText(mList.get(position));

//只要一直点击对应的 RecyclerView 的 item , 监听事件就会一直执行,虽然不会出现效果,直到有实例传进来,如果
//实例里面有逻辑,就会实现示例里面的逻辑
if (mOnItemClickListtener != null){
holder.mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getLayoutPosition();
mOnItemClickListtener.onItemClick(holder.mTextView, pos);
}
});

holder.mTextView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = holder.getLayoutPosition();
mOnItemClickListtener.onItemLongClick(holder.mTextView, pos);
return false;
}
});
}

/*ViewGroup.LayoutParams layoutParams = holder.mTextView.getLayoutParams();
layoutParams.height = mHeights.get(position);
holder.mTextView.setLayoutParams(layoutParams);*/
}

@Override
public int getItemCount() {
return mList.size();
}

/**
* 主要是为了拿到 onCreateViewHolder()的方法返回的 view
*/
class MyViewHolder extends RecyclerView.ViewHolder {

private TextView mTextView;

public MyViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.tv_item);
}
}

public interface OnItemClickListtener{
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}

public void setOnItemClickListtener(OnItemClickListtener mOnItemClickListtener){
this.mOnItemClickListtener = mOnItemClickListtener;
}


}

3.0 向自定义的 Adapter 里面添加数据

MainActivity.java 的代码逻辑如下:

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
public class MainActivity extends AppCompatActivity {

private Context mContext;
private RecyclerView mRecyclerView;
private List<String> mList;
private HomeAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mContext = this;

initView();
setRecyclerView();
}



/**
* 初始化组件
*/
public void initView(){
mRecyclerView = (RecyclerView) this.findViewById(R.id.id_recyclerview);
}

/**
* 初始化数据
*/
public void initData(){
mList = new ArrayList<>();
for (int i = 0; i < 20; ++i){
int temp = i;
mList.add(String.valueOf(temp));
}
Log.d("测试mList", "initData: " + mList);
}


/**
* 配置 RecyclerView 的属性
*/
public void setRecyclerView(){
initData();
//设置布局管理器
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));


//设置 item 增加和删除时的动画
mRecyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this,
DividerItemDecoration.HORIZONTAL_LIST));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//初始化HomeAdapter
adapter = new HomeAdapter(mContext, mList);
//给 mRecyclerView 设置

//设置监听事件
adapter.setOnItemClickListtener(new HomeAdapter.OnItemClickListtener() {
@Override
public void onItemClick(View view, int position) {

}

@Override
public void onItemLongClick(View view, final int position) {
new AlertDialog.Builder(mContext)
.setTitle("确认删除吗?")
.setNegativeButton("取消", null)
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
adapter.removeDate(position);
}
})
.show();
}
});

mRecyclerView.setAdapter(adapter);

}
}

4.0 设置 item 的分割线

DividerItemDecoration.java 的代码逻辑如下:

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
public class DividerItemDecoration extends RecyclerView.ItemDecoration {

private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};

public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;

public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}

private void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}

private void drawHorizontal(Canvas c, RecyclerView parent) {
// parent.getPaddingLeft()属性得到的是 RecyclerView 这个组件的内容距离左边的边距 left这个是起始的坐标
final int left = parent.getPaddingLeft();
// right 这个是结束的位置,也可以理解做结束坐标
final int right = parent.getWidth() - parent.getPaddingRight();

final int childCount = parent.getChildCount();

for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
RecyclerView v = new RecyclerView(parent.getContext());
// RecyclerView.LayoutParams 指的是 RecyclerView 的布局参数;
// LayoutParams 是 Layout parameters 的缩写 == 布局的参数
// 这里是多态的使用,params 是item的参数
final RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

private void drawVertical(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();

final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++){
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
}else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}

}

因为部分注释我已经在代码中写了,因而我就不重复再描述一遍了…

本文标题:学习RecyclerView笔记

文章作者:

发布时间:2017年09月10日 - 21:09

最后更新:2021年06月20日 - 19:06

原始链接:https://hndroid.github.io/2017/09/10/%E5%AD%A6%E4%B9%A0RecyclerView%E7%AC%94%E8%AE%B0/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。