RecyclerView 简介及点击事件处理

一、简介:

  • RecyclerView 是 Android5.0 之后新出的控件。
  • RecyclerView 是 ListView 的增强版,不仅能实现 ListView 的效果,还优化了 ListView 的很多不足之处。

  • 想对于 ListView 来说,官方更推荐使用 RecyclerView

二、使用 RecyclerView

  • 演示思路:布局 RecyclerView ,CardView 充当其 Item !
2.1 app 的 build.gradle 中添加依赖
1
2
3
// cardview 是因为这个 Demo 也要用到 cardview
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
2.2 主页面布局文件
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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--标题栏-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark"
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<!--自定义控件-->
<TextView
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:gravity="center"
android:text="FloatingActionButton"
android:textSize="20dp"
android:textStyle="bold" />
</android.support.v7.widget.Toolbar>
<!--RecyclerView-->
<android.support.v7.widget.RecyclerView
android:layout_below="@id/toolbar"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
<!--FloatingActionButton-->
<android.support.design.widget.CoordinatorLayout
android:layout_alignBottom="@id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/floating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/floating_icon"
app:fabSize="normal"
app:pressedTranslationZ="10dp"
app:rippleColor="@color/colorAccent"
/>
</android.support.design.widget.CoordinatorLayout>
</RelativeLayout>
2.3 Actiity 和 Adapter 的代码
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
public class MyRecyclerViewActivity extends AppCompatActivity {
@BindView(R.id.floating)
FloatingActionButton floating;
@BindView(R.id.toolbar_title)
TextView toolbarTitle;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.recycler_view)
RecyclerView recyclerView;
private Snackbar snackbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycleview);
ButterKnife.bind(this);
//设置透明状态栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
}
toolbar.setTitle("");
setSupportActionBar(toolbar);
toolbar.setNavigationIcon(R.drawable.setting);
initRecyclerViewData();
initRcyclerView();
}
private void initRcyclerView() {
LinearLayoutManager manager = new LinearLayoutManager(this);
// GridLayoutManager manager = new GridLayoutManager(getApplicationContext(),2);
// StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, OrientationHelper.HORIZONTAL);
recyclerView.setLayoutManager(manager);
myRecyclerAdapter = new MyRecyclerAdapter();
recyclerView.setAdapter(myRecyclerAdapter);
}
private List<String> listData;
/**
* 初始化 RecyclerView 的数据
*/
private void initRecyclerViewData() {
listData = new ArrayList<>();
int m = 0;
for (int i = 0; i < 20; i++) {
listData.add("珞璃之神" + m);
m++;
}
}
private MyRecyclerAdapter myRecyclerAdapter;
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_card_view, parent, false);
// View view = View.inflate(getApplicationContext(), R.layout.activity_card_view, parent);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.cardText.setText(listData.get(position));
}
@Override
public int getItemCount() {
return listData.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.card_text)
TextView cardText;
@BindView(R.id.card_image)
ImageView cardImage;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
}

注意: RecyclerView 的三种 manager ,分别为 线性、宫格、瀑布流样式。

2.4 RecyclerView 的 item 的样式为:
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
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/cardview_margin"
android:layout_marginTop="@dimen/cardview_margin"
android:layout_marginLeft="@dimen/cardview_margin_left_right"
android:layout_marginRight="@dimen/cardview_margin_left_right"
app:cardCornerRadius="5dp"
android:foreground="?android:attr/selectableItemBackground"
app:cardElevation="10dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_weight="5"
android:id="@+id/card_image"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:src="@drawable/cardview_icon" />
<TextView
android:layout_weight="1"
android:id="@+id/card_text"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:layout_below="@id/card_image"
android:text="珞神"
android:textColor="#ba2f2f"
android:textSize="30dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
2.5 运行结果如下:
  • LinearLayoutManager manager = new LinearLayoutManager(this); 结果:
    image
  • GridLayoutManager manager = new GridLayoutManager(getApplicationContext(),2);结果如下:
    image
  • StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, OrientationHelper.HORIZONTAL);结果如下:
    image

三、RecyclerView 添加条目点击事件

3.1 RecyclerView 并没有对外暴露的具体的单击或长按监听事件,需要我们自己来处理单击或长按事件,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener(){
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
  • 这种需要自己增加单击或长按事件的逻辑,然后利用接口回调出去。

  • Android 给我们提供了一个手势监测帮助类 GestureDetector ,我们可以借助这个类来处理不同的手势,我们重新建个类实现 RecyclerView.OnItemTouchListener 接口。代码如下:

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
/**
* 自定义手势监听
*/
public class RecyclerViewClickListener implements RecyclerView.OnItemTouchListener {
// GestureDetectorCompat 是为了版本兼容
private GestureDetectorCompat mGestureDetector;
private OnItem2ClickListener mListener;
//自定义内部监听
public interface OnItem2ClickListener {
//单击
void onItemClick(View view, int position);
//长按
void onItemLongClick(View view, int position);
}
public RecyclerViewClickListener(Context context, final RecyclerView mRecyclerView,
OnItem2ClickListener listener) {
this.mListener = listener;
// SimpleOnGestureListener 是为了选择重写需要的方法
mGestureDetector = new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener() {
//单击事件
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.i("mGestureDetector","onSingleTapUp");
View childViewUnder = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if (childViewUnder != null && mListener != null) {
mListener.onItemClick(childViewUnder, mRecyclerView.getChildLayoutPosition(childViewUnder));
return true;
}
return false;
}
//长按事件
@Override
public void onLongPress(MotionEvent e) {
Log.i("mGestureDetector","onLongPress");
View childView = mRecyclerView.findChildViewUnder(e.getX(),e.getY());
if(childView != null && mListener != null){
mListener.onItemLongClick(childView,mRecyclerView.getChildLayoutPosition(childView));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
//是否拦截事件交给 mGestureDetector 处理
if(mGestureDetector.onTouchEvent(e)){
return true;
}else
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
  • 代码中只实现了,单击和长按事件的回调,更多的事件操作方法可以参考 GestureDetector 类。
3.2 另一种方式是在 Adapter 的 onBindViewHolder 方法中,利用 View 本身的监听事件,来设置回调监听,代码如下:
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
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.ViewHolder> {
private List<String> listData;
public MyRecyclerAdapter(List<String> listData){
this.listData = listData;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_card_view, parent, false);
// View view = View.inflate(getApplicationContext(), R.layout.activity_card_view, parent);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
/**
* 在 onBindViewHolder 中,设置单击和长按的监听回调
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.cardText.setText(listData.get(position));
//单击
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//触发自定义监听的单击事件
onItemClickListener.onItemClick(holder.itemView,position);
}
});
//长按
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
//触发自定义监听的长按事件
onItemClickListener.onItemLongClick(holder.itemView,position);
return true;//表示此事件已经消费,不会触发单击事件
}
});
}
@Override
public int getItemCount() {
return listData.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.card_text)
TextView cardText;
@BindView(R.id.card_image)
ImageView cardImage;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
public void setOnItemClickListener(MyRecyclerAdapter.OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
private OnItemClickListener onItemClickListener;
/**
* 自定义监听回调,RecyclerView 的 单击和长按事件
*/
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
}
3.3 两种添加点击事件比较
  • 第一种方式更加灵活,解耦性更高,第二种因为设置在 Adapter 内,只能用作特定的 RecyclerView

  • 第二种相对第一种来说,更加简便,实现起来也方便,也比较好理解。

  • 第一种还能用于更加复杂的手势监听,我们可以利用 GestureDetector 类来实现更加复杂的事件监听回调,而第二种监听的事件比较有限。