【问题标题】:How to get clicks on RecyclerView (NOT the children)如何在 RecyclerView 上获得点击(不是孩子)
【发布时间】:2016-07-07 23:07:50
【问题描述】:

有没有办法在RecyclerView 上设置onClickListener

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

我尝试对孩子们做setFocusable(false)setClickable(false)setOnClickListener(null) 无济于事。无论如何,我不认为孩子们从父母那里窃取点击,因为当我点击没有孩子的区域时,点击也不会注册。

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();
        }

    }

}

【问题讨论】:

    标签: android android-layout android-recyclerview onclicklistener recyclerview-layout


    【解决方案1】:

    有没有办法在RecyclerView 上设置onClickListener

    没有。也就是说,您可以设置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中的MotionEvents喂给GestureDetector,并检查返回来决定是否消费该事件,以免干扰滚动、拖动等。

    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;
            }
        }
    );
    

    请注意,上述解决方案几乎只适用于“简单”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。根据您实例化 this 的方式,可能需要额外的构造函数。

    【讨论】:

    • 这可能会让我们毛骨悚然。你知道为什么RecyclerView 从不打电话给performClick吗?
    • 嗯,你必须向设计师询问一个明确的答案,但 IMO,这是因为它从来没有打算以这种方式使用。通常在向用户展示列表时,您更关心单个项目的交互。这怎么会让你毛茸茸的?如果您担心额外的代码,您可以在RecyclerView 子类中实现它,以免污染您的Activity 的代码。或者,如果您的意思是您已经在使用手势,则只需将此功能添加到您已有的功能中。
    • 如果你死心塌地使用你的OnClickListener,你可以继承RecyclerView来调用performClick(),但是你必须跟踪触摸事件来区分一个简单的点击,例如,拖动,但这基本上就是GestureDetector 所做的一切。
    • 啊,明白了。我们正在使用装饰器在长按上重新排序项目,我只是担心onTouchListener 上的设置会干扰这一点。但如果这是要走的路,我相信我们可以让它发挥作用——我还没有真正尝试过。谢谢!
    • 好的,谢谢。我认为我们只需要在它周围玩一会儿。接受答案,因为它不仅仅是回答我原来的问题:)
    【解决方案2】:

    这就是我在 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  ...
            }
        }
    

    享受:)

    【讨论】:

      【解决方案3】:

      这是一个技巧,它对 cme​​ts 开放。

      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 {
          .....
      }
      

      【讨论】:

        【解决方案4】:

        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>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-20
          • 1970-01-01
          • 2021-12-14
          • 2018-04-28
          • 1970-01-01
          • 2021-11-25
          • 1970-01-01
          • 2018-04-02
          相关资源
          最近更新 更多