解决recyclerview多指点击相应多个条目冲突的问题
最近在开发一个应用的列表运用到了recyclerview,实现的过程很容易,但是遇到了一个问题,根据公司产品的需求,点击的列表的一个条目再点击其它的条目,如果当前的手指没有放开,那么第二个手指点击时不响应点击事件,通俗一些说就是单指点击效果。由于当时没有考虑到这个问题,造成了一些困扰,好在已经找到了解决方案。
首先来看一下基本的recyclerview的实现
先在build.gradle文件中添加依赖:
compile 'com.android.support:recyclerview-v7:26.1.0
然后在xml文件中引用recyclerview,以下为HomeActivity引用recyclerview的xml文件代码:
<?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:background="@mipmap/bg"> <LinearLayout android:id="@+id/layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" > </android.support.v7.widget.RecyclerView> </LinearLayout> </LinearLayout>
然后在HomeActivity中引用此xml文件,调用实现recyclerview,以下为在HomeActivity中调用recyclerview的方法,代码如下:
public class HomeActivity extends Activity { private RecyclerView recyclerView; private String[] names={"投掷哥布林","牛头兵","牛头巨兽","暗咒猫妖","落雷哥布林凯诺","毒猫王", "冰霜克拉赫","牛头王萨乌塔","烈焰彼诺修","盗尸者骨狱息"}; private List<String> nameList=new ArrayList<>(); private LinearLayout layout; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); recyclerView = findViewById(R.id.recycler_view); layout = findViewById(R.id.layout); for(String name : names){ nameList.add(name); } //创建recyclerview管理者 RecyclerView.LayoutManager layoutManager=new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); //创建适配器 SingleFingerListAdapter adapter=new SingleFingerListAdapter(this,nameList); //设置recyclerview列表的方向 recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); recyclerView.setAdapter(adapter); } }
接下来就是recyclerview适配器功能的实现,先来看recyclerview的布局文件,代码如下:
<?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"> <LinearLayout android:id="@+id/name_layout" android:layout_width="wrap_content" android:layout_height="80dp" android:orientation="horizontal" android:background="@drawable/button_state"> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="投掷哥布林" android:layout_gravity="center_vertical" android:textSize="18sp" android:layout_marginLeft="10dp" android:textColor="#fff"/> </LinearLayout> </LinearLayout>
然后在Java代码中实现recyclerview的适配器功能,代码如下:
public class SingleFingerListAdapter extends RecyclerView.Adapter{ private Context context; private List<String> nameList; public SingleFingerListAdapter(Context context, List<String> nameList) { this.context = context; this.nameList = nameList; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view= LayoutInflater.from(context).inflate(R.layout.adapter_single_finger_list,parent,false); MyHolder holder=new MyHolder(view); return holder; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { MyHolder myHolder= (MyHolder) holder; myHolder.name.setText(nameList.get(position)); myHolder.nameLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } @Override public int getItemCount() { return nameList.size(); } public class MyHolder extends RecyclerView.ViewHolder{ TextView name; LinearLayout nameLayout; public MyHolder(View itemView) { super(itemView); name=itemView.findViewById(R.id.name); nameLayout=itemView.findViewById(R.id.name_layout); } } }至此recyclerview的基本使用已经实现,如下图为实现后的效果图:
首先来看一下未经处理的recyclerview的效果
如图,现在是三个手指的点击,图中就有三个条目发生了响应事件。如此一来我们就要对点击事件进行相应的处理,进行多指点击拦截处理。
在布局文件中可以看到recyclerview上层的父容器是一个LinearLayout,点击recyclerview的条目时肯定会经过LinearLayout,如此,我们只需要父类的LinearLayout中的dispathTouchEvent方法中进行相关的拦截处理就可以了。这时需要自定义一个类继承自LinearLayout,在里面的dispathTouchEvent方法中进行相关的拦截处理就可以了。代码如下:
public class TouchLayout extends LinearLayout { private Context context; public TouchLayout(Context context) { super(context); this.context=context; } public TouchLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); this.context=context; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { int count=ev.getPointerCount(); switch (ev.getAction() & MotionEvent.ACTION_MASK){ case MotionEvent.ACTION_DOWN: Log.i("TAG-->","数量:"+count); break; case MotionEvent.ACTION_POINTER_DOWN: Log.i("TAG-->","多指数量:"+count); return false; default: break; } return super.dispatchTouchEvent(ev); } }
事先通过Logcat打印看到ev.getAction()出现的数字是262,但是在源码中的对应touch对应的动作序号并没有这个,所以不会走到ACTION_POINTER_DOWN——这个多指的事件中,见过查询源码,发现如果需要使用多指事件,需要使用ev.getAction() & MotionEvent.ACTION_MASK,然后才会触发ACTION_POINTER_DOWN在去处理,以下为MotionEvent.ACTION_MASK的源码:
public static final int ACTION_MASK = 0xff;
根据源码可以看出ACTION_MASK=0xff 转换为二进制就是11111111,如果ev.getAction()动作=262,转换成2进制就是000100000110,两者进行&运算,得到的结果就是00000110,再转换成二进制结果就是6了,就可以进入ACTION_POINTER_DOWN动作了。在MotionEvent.ACTION_POINTER_DOWN中进行return false的处理,就可以实现拦截多指事件了。
自定义的LinearLayout封装完成后,在布局文件中引用就可以拦截多指事件了,代码如下:
<?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:background="@mipmap/bg"> <com.mxnavi.singlefingerlist.TouchLayout android:id="@+id/layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" > </android.support.v7.widget.RecyclerView> </com.mxnavi.singlefingerlist.TouchLayout> </LinearLayout>
如此一来,重新进行编译就会实现如下效果:
如上所示:三个手指点击,但只响应了一个点击事件。
以上为通过自定义类实现拦截多指的点击事件,下面介绍一种简单暴力直接屏蔽掉多指点击事件的方法,请先看代码:
<?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:background="@mipmap/bg"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:splitMotionEvents="false" > </android.support.v7.widget.RecyclerView> </LinearLayout>如上代码所示:在想要拦截的多指点击事件的布局控件中加入android:splitMotionEvents="false"这段代码,就可以实现多指点击事件拦截了。