如何在 RecyclerView 上获得点击(不是 children)

How to get clicks on RecyclerView (NOT the children)

有什么方法可以在 RecyclerView 上设置 onClickListener 吗?

我有一个 RecyclerView,其中有一些 children,并在 parent RecyclerView 上设置了一个 OnClickListener。但是,当我单击该视图时,onClick 不会触发。请参阅下面的示例代码——我们希望在 parent 上获得点击, 而不是 children。在这种情况下,我们不关心对项目的点击。

我曾尝试在 children 上执行 setFocusable(false)setClickable(false)setOnClickListener(null),但均无济于事。在任何情况下,我都不认为 children 从 parent 窃取点击,因为当我点击没有 children 的区域时,点击也不会注册。

package com.formagrid.hellotest;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.Arrays;
import java.util.List;

public class HelloActivity extends Activity {

    private RecyclerView mRecyclerView;
    private RecyclerAdapter mAdapter;

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

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mAdapter = new RecyclerAdapter(Arrays.asList("hi", "this", "is", "some", "text"));
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("patricia", view.toString());
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.Holder> {

        public class Holder extends RecyclerView.ViewHolder {

            protected TextView textView;

            public Holder(TextView itemView) {
                super(itemView);
                this.textView = itemView;
            }

        }

        private List<String> contents;

        public RecyclerAdapter(List<String> contents) {
            this.contents = contents;
        }

        @Override
        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new Holder(new TextView(parent.getContext()));
        }

        @Override
        public void onBindViewHolder(Holder holder, int position) {
            holder.textView.setText(contents.get(position));
        }

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

    }

}

Is there any way to set an onClickListener on a RecyclerView?

没有。也就是说,您 可以 设置一个 OnClickListener,但 RecyclerView 永远不会调用它。 RecyclerView 拦截所有触摸事件,但从不调用 performClick(),这就是 View 调用其侦听器的方式。

但是,您可以用 OnTouchListenerGestureDetector 来模拟 OnClickListener。对于 GestureDetector 的侦听器,我们可以使用 SimpleOnGestureListener,仅实现 onSingleTapUp() 方法。

class ClickListener extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        Toast.makeText(HelloActivity.this, "Clicked", 0).show();
        return true;
    }
};

那么我们只需要从OnTouchListener中给GestureDetector提供MotionEvent,然后检查return来决定是否消费这个事件,这样不干扰滚动、拖动等

final GestureDetector detector = new GestureDetector(HelloActivity.this, new ClickListener());

mRecyclerView.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(detector.onTouchEvent(event)) {
                return true;
            }
            return false;
        }
    }
);

请注意,上面的解决方案几乎只适用于 "simple" RecyclerView,就像问题中描述和给出的那样。如果您使用更复杂的项目处理,例如拖放或滑动,我们将需要在触摸事件链的更上层处理我们的手势检测。

为此,我们可以子类化RecyclerView并在dispatchTouchEvent()方法中执行检测。如果检测到单击,我们只需调用 performClick(),这将触发 RecyclerViewOnClickListener

public class ClickableRecyclerView extends RecyclerView {

    private final GestureDetectorCompat detector;

    public ClickableRecyclerView(Context context) {
        this(context, null);
    }

    public ClickableRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        detector = new GestureDetectorCompat(context, new ClickListener());
    }

    private class ClickListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            performClick();
            return true;
        }
    };

    @Override
    public boolean dispatchTouchEvent(MotionEvent e) {
        detector.onTouchEvent(e);
        return super.dispatchTouchEvent(e);
    }
}

只需使用此子类代替您的常规 RecyclerView,并像往常一样在其上设置 OnClickListener。可能需要额外的构造函数,具体取决于您如何实例化它。

RecycleView 上的点击事件?像这样尝试:

//set android:descendantFocusability="blocksDescendants" on parent layout 
//then setOnClickListener

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
**android:descendantFocusability="blocksDescendants"**
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
  android:layout_width="match_parent"
  android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>

</LinearLayout>

这就是我在 Kotlin 风格中的做法

fun RecyclerView.enableClickListener(){
    val gesture = object : GestureDetector.SimpleOnGestureListener(){
        override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
            this@enableClickListener.performClick()
            return super.onSingleTapConfirmed(e)
        }
    }
    val detector = GestureDetector(this.context, gesture)
    this.setOnTouchListener { v, event -> detector.onTouchEvent(event) }
}

这是使用方法

yourRecyclerView.apply {
        enableClickListener()
        setOnClickListener {
           // Do what you want  ...
        }
    }

尽情享受吧:)

这里有技巧,欢迎评论。

一个 FrameLayout 中可以有 2 个子项目。第一个是 RecyclerView,第二个是 View

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <View
            android:id="@+id/over_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    </FrameLayout>

因为它是 FrameLayout,所以 View 将位于 RecyclerView 之上,您可以将 onClickListener 设置为 View,它会表现得好像是 RecyclerView 被点击了

over_view.setOnClickListener {
    .....
}