为RecyclerView的Item添加点击及长按事件的三种方法
2017-06-14 23:07
543 查看
简介
我们知道RecyclerView作为一个优秀的ListView的替代品现在已经凭借其更为方便的使用,更多样的功能,更炫酷的界面设计等等广为使用,但是官方并没有提供类似ListView的OnItemClickListener的接口。因此如何实现类似于ListView的item点击事件到底该如何实现呢,下面我们通过一个简单的例子来进行介绍。简单的RecyclerView的建立
首先我们先建立一个最最基本的RecyclerView,下面是我们的主布局activity_main.xml,如下所示:<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.luckybear.myrecyclerview.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_my_recycler" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </RelativeLayout>
我们在主布局中仅放置一个RecyclerView,下面来看看我们的activity实现:
public class MainActivity extends AppCompatActivity { private RecyclerView mMyRecyclerView; private MyRecyclerAdapter mMyRecyclerAdapter; private ArrayList<String> mData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMyRecyclerView = (RecyclerView) findViewById(R.id.rv_my_recycler); mMyRecyclerAdapter = new MyRecyclerAdapter(this, getData()); mMyRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mMyRecyclerView.setAdapter(mMyRecyclerAdapter); } public ArrayList<String> getData() { mData = new ArrayList<>(); for (int i = 0; i < 50; i ++) { mData.add("item " + i); } return mData; } }
如上所示,首先建立RecyclerView,之后我们为其创建一个Adapter,我们的数据源即为mData,接下来我们来看看adapter的实现:
public class MyRecyclerAdapter extends RecyclerView.Adapter { ArrayList<String> mData; Context mContext; TextView mMyTextView; public MyRecyclerAdapter(Context context, ArrayList<String> data) { mContext = context; mData = data; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(mContext).infl 4000 ate(R.layout.recycler_item, parent, false); MyViewHolder viewHolder = new MyViewHolder(itemView); return viewHolder; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { mMyTextView = (TextView) holder.itemView.findViewById(R.id.tv_my_item); mMyTextView.setText(mData.get(position)); } @Override public int getItemCount() { return mData.size(); } }
简单到不能呼吸,最简单的adapter,有一个构造函数,在刚才的activity中创建adapter时进行调用,数据源即在这里传入。然后在onCreateViewHolder()方法中创建得到我们的itemView,并且将其与viewHolder绑定,在onBindViewHolder()方法中将数据设置到我们的itemView的textView中,我们的每一个item的布局定义在recycler_item.xml中,定义如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_my_item" android:layout_width="match_parent" android:layout_height="72dp" android:gravity="center_vertical"/> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@android:color/black"/> </LinearLayout>
如上所示,仅有一个textView及一个分割线。最后我们的ViewHolder也是最基本的ViewHolder,定义如下:
public class MyViewHolder extends RecyclerView.ViewHolder { public MyViewHolder(View itemView) { super(itemView); } }
这样我们就创建了一个最简单的RecyclerView,实现效果如下所示:
这个RecyclerView是最为基本的RecyclerView了,它支持最最基本的功能,但是当你点击它的每一项时,它并没有任何响应,接下来我们为其添加最基本的响应。
方法一:在Adapter中为Item设置点击监听
既然官方未提供相应的实现那我们也只有自己来实现它了,接下来我们来说明如何实现这个点击事件,如果我们想显示我们目前点击的每个item所填充的数据,这个其实在adapter内部就可以轻易的实现,adapter修改如下:public class MyRecyclerAdapter extends RecyclerView.Adapter implements View.OnClickListener, View.OnLongClickListener { ArrayList<String> mData; Context mContext; TextView mMyTextView; public MyRecyclerAdapter(Context context, ArrayList<String> data) { mContext = context; mData = data; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(mContext).inflate(R.layout.recycler_item, parent, false); MyViewHolder viewHolder = new MyViewHolder(itemView); itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); return viewHolder; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { mMyTextView = (TextView) holder.itemView.findViewById(R.id.tv_my_item); mMyTextView.setText(mData.get(position)); holder.itemView.setTag(position); } @Override public int getItemCount() { return mData.size(); } @Override public void onClick(View v) { Toast.makeText(mContext, "Click " + mData.get((int)v.getTag()), Toast.LENGTH_SHORT).show(); } @Override public boolean onLongClick(View v) { Toast.makeText(mContext, "LongClick " + mData.get((int)v.getTag()), Toast.LENGTH_SHORT).show(); return true; } }
如上所示,
implements View.OnClickListener接口
在函数在OnClick()方法中得到数据源的数据,并将其显示在Toast中
在onCreateViewHolder()方法中或者onBindViewHolder()方法中为每个itemView设置OnClickListener
在onBindViewHolder()时将相应的位置信息设置为view的Tag,在onClick()方法中进行使用
可以看到,我们已经得到了用户的点击事件,并且正确的处理了这个点击,但是有很多时候我们并不能仅仅在adapter中来处理这个点击事件,我们很多业务逻辑需要在activity中或者fragment中进行处理以满足交互或者调整UI显示,我们在原有的基础上进行修改,让这个Toast在acticvity中来显示,我们一定已经想到了吧,那就是回调,我们回调三部曲:
在adapter中定义interface
在activity中实现这个回调接口并将回调对象设置到adapter中
在adapter中的合适位置进行调用
我们的adapter修改如下:
public class MyRecyclerAdapter extends RecyclerView.Adapter implements View.OnClickListener, View.OnLongClickListener { ArrayList<String> mData; Context mContext; TextView mMyTextView; OnItemClickListener mOnItemClickListener; OnItemLongClickListener mOnItemLongClickListener; public MyRecyclerAdapter(Context context, ArrayList<String> data) { mContext = context; mData = data; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(mContext).inflate(R.layout.recycler_item, parent, false); MyViewHolder viewHolder = new MyViewHolder(itemView); itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); return viewHolder; } public interface OnItemClickListener { void OnItemClick(View v); } public interface OnItemLongClickListener { void OnItemLongClick(View v); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { mMyTextView = (TextView) holder.itemView.findViewById(R.id.tv_my_item); mMyTextView.setText(mData.get(position)); holder.itemView.setTag(position); } @Override public int getItemCount() { return mData.size(); } @Override public void onClick(View v) { if (mOnItemClickListener != null) { mOnItemClickListener.OnItemClick(v); } } @Override public boolean onLongClick(View v) { if (mOnItemLongClickListener != null) { mOnItemLongClickListener.OnItemLongClick(v); } return true; } public void setOnItemClickListener(OnItemClickListener onItemClickListener) { mOnItemClickListener = onItemClickListener; } public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) { mOnItemLongClickListener = onItemLongClickListener; } }
如上所示,定义一个回调接口,并在OnClick()中进行调用,这个接口在activity中实现,具体实现如下:
public class MainActivity extends AppCompatActivity implements MyRecyclerAdapter.OnItemClickListener, MyRecyclerAdapter.OnItemLongClickListener{ private RecyclerView mMyRecyclerView; private MyRecyclerAdapter mMyRecyclerAdapter; private ArrayList<String> mData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMyRecyclerView = (RecyclerView) findViewById(R.id.rv_my_recycler); mMyRecyclerAdapter = new MyRecyclerAdapter(this, getData()); mMyRecyclerAdapter.setOnItemClickListener(this); mMyRecyclerAdapter.setOnItemLongClickListener(this); mMyRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mMyRecyclerView.setAdapter(mMyRecyclerAdapter); } public ArrayList<String> getData() { mData = new ArrayList<>(); for (int i = 0; i < 50; i ++) { mData.add("item " + i); } return mData; } @Override public void OnItemClick(View v) { Toast.makeText(MainActivity.this, "Click " + mData.get((int)v.getTag()), Toast.LENGTH_SHORT).show(); } @Override public void OnItemLongClick(View v) { Toast.makeText(MainActivity.this, "LongClick " + mData.get((int)v.getTag()), Toast.LENGTH_SHORT).show(); } }
如上为activity中的接口的实现并将此接口设置到adapter对象中。
实现效果如下:
点击事件响应
长按事件响应
方法二: 利用RecyclerView的addOnItemTouchListener方法
此方法利用了RecyclerView的addOnItemTouchListener方法,对事件进行相应的拦截处理之后在点击及长按事件的处理中会调在addOnItemTouchListener定义的点击及长按接口方法进行相应的处理。我们需要添加一个RecyclerView的事件处理类如下:public class RecyclerViewTouchListener implements RecyclerView.OnItemTouchListener { private GestureDetector mGestureDetector; private OnItemClickListener mListener; //内部接口,定义点击方法以及长按方法 public interface OnItemClickListener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } public RecyclerViewTouchListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener){ mListener = listener; mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){ //单击事件 @Override public boolean onSingleTapUp(MotionEvent e) { View childView = recyclerView.findChildViewUnder(e.getX(),e.getY()); if(childView != null && mListener != null){ mListener.onItemClick(childView,recyclerView.getChildLayoutPosition(childView)); return true; } return false; } //长按事件 @Override public void onLongPress(MotionEvent e) { View childView = recyclerView.findChildViewUnder(e.getX(),e.getY()); if(childView != null && mListener != null){ mListener.onItemLongClick(childView,recyclerView.getChildLayoutPosition(childView)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { //把事件交给GestureDetector处理 if(mGestureDetector.onTouchEvent(e)){ return true; }else return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } }
如上所示,使用了GestureDetector类,在事件处理过程中将事件拦截,然后交由GestureDetector判断事件类型,将点击及长按事件交由listener去处理,我们的处理必然是写在MainActivity中咯,具体实现如下:
public class MainActivity extends AppCompatActivity { private RecyclerView mMyRecyclerView; private MyRecyclerAdapter mMyRecyclerAdapter; private ArrayList<String> mData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMyRecyclerView = (RecyclerView) findViewById(R.id.rv_my_recycler); mMyRecyclerAdapter = new MyRecyclerAdapter(this, getData()); mMyRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mMyRecyclerView.setAdapter(mMyRecyclerAdapter); mMyRecyclerView.addOnItemTouchListener(new RecyclerViewTouchListener(MainActivity.this, mMyRecyclerView, new RecyclerViewTouchListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "Click " + mData.get((int)view.getTag()), Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this, "LongClick " + mData.get((int)view.getTag()), Toast.LENGTH_SHORT).show(); } })); } public ArrayList<String> getData() { mData = new ArrayList<>(); for (int i = 0; i < 50; i ++) { mData.add("item " + i); } return mData; } }
如上所示,调用addOnItemTouchListener,定义长按及点击事件处理函数,这个方法可以将点击及长按事件独立封装出来,代码结构较好,但是这种实现仅适用于整个Item的点击及长按事件处理,对item中的子View就爱莫能助了,这个方法不是特别推荐,利用事件拦截来进行处理如果一涉及到子view等,这个方法就变得不那么优雅了,接下来的方法三更为推荐,并且不存在方法二的这些问题。方法二的具体实现效果就不上图了,有兴趣的小伙伴可以自行尝试。
方法三: 独立封装,在attach ItemView时添加监听
这个方法是一个国外开发者提出的实现方法,十分推荐,他的实现方式可以独立封装为一个类,在定义了RecyclerView对象时即可进行点击及长按事件的绑定,此时就可以添加你想要的事件监听,十分方便。我们所有的工作仅是在第一部分的简单的RecyclerView的建立的代码基础上添加一个ItemClickSupport.java类,类定义如下:public class ItemClickSupport { private final RecyclerView mRecyclerView; private OnItemClickListener mOnItemClickListener; private OnItemLongClickListener mOnItemLongClickListener; private View.OnClickListener mOnClickListener = new View.OnClickListener() { @Override public void onClick(View v) { if (mOnItemClickListener != null) { RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v); mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v); } } }; private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (mOnItemLongClickListener != null) { RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v); return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v); } return false; } }; private RecyclerView.OnChildAttachStateChangeListener mAttachListener = new RecyclerView.OnChildAttachStateChangeListener() { @Override public void onChildViewAttachedToWindow(View view) { if (mOnItemClickListener != null) { view.setOnClickListener(mOnClickListener); } if (mOnItemLongClickListener != null) { view.setOnLongClickListener(mOnLongClickListener); } } @Override public void onChildViewDetachedFromWindow(View view) {} }; private ItemClickSupport(RecyclerView recyclerView) { mRecyclerView = recyclerView; mRecyclerView.setTag(R.id.item_click_support, this); mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener); } public static ItemClickSupport addTo(RecyclerView view) { ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support); if (support == null) { support = new ItemClickSupport(view); } return support; } public static ItemClickSupport removeFrom(RecyclerView view) { ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support); if (support != null) { support.detach(view); } return support; } public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) { mOnItemClickListener = listener; return this; } public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) { mOnItemLongClickListener = listener; return this; } private void detach(RecyclerView view) { view.removeOnChildAttachStateChangeListener(mAttachListener); view.setTag(R.id.item_click_support, null); } public interface OnItemClickListener { void onItemClicked(RecyclerView recyclerView, int position, View v); } public interface OnItemLongClickListener { boolean onItemLongClicked(RecyclerView recyclerView, int position, View v); } }
之后在ids.xml中定义一个id如下:
<resources> <item name="item_click_support" type="id" /> </resources>
这个id的作用主要是将RecyclerView与自己的ItemClickSupport绑定,在add时绑定,之后在remove时将其解绑定。
最后我们在定义RecyclerView的地方添加这个ItemClickSupport对象并将这个RecyclerView与ItemClickSupport进行相应的绑定并定义相应的点击及长按事件即可,这部分的工作我们放在 MainActivity中,具体实现如下所示:
public class MainActivity extends AppCompatActivity { private RecyclerView mMyRecyclerView; private MyRecyclerAdapter mMyRecyclerAdapter; private ArrayList<String> mData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMyRecyclerView = (RecyclerView) findViewById(R.id.rv_my_recycler); mMyRecyclerAdapter = new MyRecyclerAdapter(this, getData()); mMyRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mMyRecyclerView.setAdapter(mMyRecyclerAdapter); ItemClickSupport.addTo(mMyRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() { @Override public void onItemClicked(RecyclerView recyclerView, int position, View v) { Toast.makeText(MainActivity.this, "Click " + mData.get(position), Toast.LENGTH_SHORT).show(); } }).setOnItemLongClickListener(new ItemClickSupport.OnItemLongClickListener() { @Override public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) { Toast.makeText(MainActivity.this, "LongClick " + mData.get(position), Toast.LENGTH_SHORT).show(); return true; } }); } public ArrayList<String> getData() { mData = new ArrayList<>(); for (int i = 0; i < 50; i ++) { mData.add("item " + i); } return mData; } @Override protected void onDestroy() { super.onDestroy(); ItemClickSupport.removeFrom(mMyRecyclerView); } }
具体的点击长按效果如下所示:
点击效果:
长按效果
个人认为这个方法最优雅并且这种定义不需要将逻辑写在adapter中,不会造成adapter类处理过多的逻辑,可以专心与adapter原有功能的处理,因此这种方法极为推荐。
至此我们的item的点击及长按实现完成,最后希望大家读有所获。
相关文章推荐
- 给recyclerview的item添加点击事件的 一种方法
- 为RecyclerView添加item的点击事件
- RecyclerView 添加点击事件的几种方法
- RecyclerView加载不同item并实现其item点击事件,实现添加常用应用的功能
- Android RecyclerView使用(二) -给Item添加点击事件
- 初识RecyclerView(二)——添加item的点击事件
- RecyclerView 添加头部和尾部,并实现Item的点击事件
- RecyclerView使用(二)多种Item布局、添加点击事件
- 给RecyclerView的Item添加点击事件
- 从头开始学 RecyclerView(二) 添加item点击事件
- 精通RecyclerView:打造ListView、GridView、瀑布流;学会添加分割线、 添加删除动画 、Item点击事件
- 为RecyclerView添加item的点击事件
- 为RecyclerView添加item的点击事件
- Android 高级UI设计笔记20:RecyclerView 的详解之RecyclerView添加Item点击事件
- RecyclerView 添加头部和尾部,并实现Item的点击事件
- 为RecyclerView添加item的点击事件
- RecyclerView 给Item添加点击事件
- 为RecyclerView添加item的点击事件
- 为RecyclerView添加item的点击事件
- RecyclerView实现Item点击事件方法一