解决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多指点击相应多个条目冲突的问题

      如图,现在是三个手指的点击,图中就有三个条目发生了响应事件。如此一来我们就要对点击事件进行相应的处理,进行多指点击拦截处理。

      在布局文件中可以看到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>

      如此一来,重新进行编译就会实现如下效果:

           解决recyclerview多指点击相应多个条目冲突的问题

如上所示:三个手指点击,但只响应了一个点击事件。

以上为通过自定义类实现拦截多指的点击事件,下面介绍一种简单暴力直接屏蔽掉多指点击事件的方法,请先看代码:

<?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"这段代码,就可以实现多指点击事件拦截了。