【问题标题】:Parent click event not firing when recyclerview clicked单击recyclerview时未触发父单击事件
【发布时间】:2015-05-24 03:51:35
【问题描述】:

我有一个 RecyclerView,它位于具有几个 TextView 的 CardView 中。 CardView 有一个OnClickListener 集,并在单击 TextViews 时触发,但在单击 RecyclerView 时不会触发。

这是 CardView 的样子:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="5dp"
    card_view:cardCornerRadius="4dp"
    card_view:cardElevation="5dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:weightSum="100"
        android:minWidth="100dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/text1"
            android:textColor="@color/abc_primary_text_material_light"
            android:layout_weight="1"
            android:layout_gravity="center_horizontal" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:listSelector="@color/highlighted_text_material_light"
            android:layout_weight="98" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@android:color/black" />

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/relativeSummary"
            android:orientation="horizontal"
            android:layout_weight="1">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:id="@+id/text2"
                android:textAlignment="viewEnd"
                android:textColor="@color/abc_secondary_text_material_light"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:gravity="start"
                android:singleLine="true"
                android:layout_alignParentLeft="true" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:id="@+id/text3"
                android:textColor="@color/abc_primary_text_material_light"
                android:layout_marginRight="5dp"
                android:layout_marginLeft="5dp"
                android:gravity="end"
                android:singleLine="true"
                android:layout_alignParentRight="true"
                android:layout_toRightOf="@+id/text2" />

        </RelativeLayout>
    </LinearLayout>
</android.support.v7.widget.CardView>

我不需要此 RecyclerView 上的单击侦听器,实际上只需要在单击 RecyclerView 时触发父视图的单击事件(OnLongClick 事件也是如此)。我还需要 RecyclerView 来滚动。 RecyclerView 是如何吃点击事件而不将其传递给父级的吗?

【问题讨论】:

    标签: android android-cardview android-recyclerview


    【解决方案1】:

    有一个更好的解决方案。也就是说,将您的CardView 子类化:

    public class InterceptTouchCardView extends CardView {
    
        public InterceptTouchCardView(Context context) {
            super(context);
        }
    
        public InterceptTouchCardView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public InterceptTouchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        /**
         * Intercept touch event so that inner views cannot receive it.
         * 
         * If a ViewGroup contains a RecyclerView and has an OnTouchListener or something like that,
         * touch events will be directly delivered to inner RecyclerView and handled by it. As a result, 
         * parent ViewGroup won't receive the touch event any longer.
         */
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return true;
        }
    }
    

    【讨论】:

      【解决方案2】:
      recyclerView.setLayoutFrozen(true);
      

      在 setAdapter for recyclerView 之后只 setLayoutFrozen true

      【讨论】:

      • 这个方法防止 RecyclerView 被更新——适配器的onBindViewHolder 在某些情况下不会被调用。
      • 这会禁用滚动事件。
      • 回收时不会刷新项目布局。不应该在包含另一个 recyclerView 的列表中使用它。
      • setLayoutFrozen 已弃用,因此如果您实际上只需要在回收站中显示数据而不需要其他任何内容(滚动、单击...等),请使用 supressLayout(true)
      【解决方案3】:

      就我而言,我有一个带有几个按钮的 CardView 和一个 RecyclerView。使用ywwynmDaryl 的解决方案,问题在于 CardView 会拦截来自其所有子视图的事件,包括按钮。但我想要的是 CardView 只拦截 RecyclerView 的触摸事件。我的解决方案如下:

      public class UntouchableRecyclerView extends RecyclerView {
      
          public UntouchableRecyclerView(Context context) {
              super(context);
          }
      
          public UntouchableRecyclerView(Context context, AttributeSet attrs) {
              super(context, attrs);
          }
      
          public UntouchableRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
          }
      
          @Override
          public boolean onTouchEvent(MotionEvent e) {
              return false;
          }
      }
      

      【讨论】:

      • 感谢乔治的回答,默认情况下,当布局未冻结时,Recyclerview-OnTouchevents 返回 true 表示它可以处理。在这种情况下,触摸事件被完全禁用。
      • 完美解决方案!!
      【解决方案4】:

      我想出了如何将点击事件传递给 RecyclerView 的父级。我的解决方案有点像 hack,所以我希望有人能提出更好的解决方案。

      在 RecylerView.Adapter 中:

      @Override
      public ViewHolder onCreateViewHolder(final ViewGroup viewGroup, int i) {
          View view = LayoutInflater.from(viewGroup.getContext())
                  .inflate(R.layout.my_item_layout, viewGroup, false);
      
          ViewHolder viewHolder = new ViewHolder(view);
      
          viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  viewGroup.callOnClick();
              }
          });
          viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
              @Override
              public boolean onLongClick(View v) {
                  return viewGroup.performLongClick();
              }
          });
      
          return viewHolder;
      }
      

      然后我不得不在 RecyclerView 上连接点击事件:

      RecyclerView.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              parentView.callOnClick();
          }
      });
      RecyclerView.setOnLongClickListener(new View.OnLongClickListener() {
          @Override
          public boolean onLongClick(View v) {
              return parentView.performLongClick();
          }
      });
      

      【讨论】:

      • 这个可以,但是没有显示ViewGroup的点击效果。
      【解决方案5】:

      @ywwynm,除了解决方案不允许嵌套的 RecyclerView 滚动之外,您在正确的轨道上。我将它与解决方案 here 结合起来,并提出了这个解决方案来处理点击和 onLongClick 事件以及允许滚动。

      public class InterceptTouchCardView extends CardView {
          private GestureDetector mGestureDetector;
          private boolean mLongClicked;
      
          public InterceptTouchCardView(Context context) {
              super(context);
              Initialize();
          }
      
          public InterceptTouchCardView(Context context, AttributeSet attrs) {
              super(context, attrs);
              Initialize();
          }
      
          public InterceptTouchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
              Initialize();
          }
      
          private void Initialize() {
              mGestureDetector = new GestureDetector(getContext(),
                      new GestureDetector.SimpleOnGestureListener() {
                          public boolean onDown(MotionEvent e) {
                              mLongClicked = false;
                              return true;
                          }
      
                          public void onLongPress(MotionEvent e) {
                              mLongClicked = true;
                              performLongClick();
                          }
                      });
          }
      
          /**
           * Intercept touch event so that inner views cannot receive it.
           *
           * If a ViewGroup contains a RecyclerView and has an OnTouchListener or something like that,
           * touch events will be directly delivered to inner RecyclerView and handled by it. As a result,
           * parent ViewGroup won't receive the touch event any longer.
           *
           * We can't Intercept the touch event if we want to allow scrolling since ACTION_DOWN always
           * happens before ACTION_MOVE.  So handle touch events here since onTouchEvent won't be triggered.
           */
          @Override
          public boolean onInterceptTouchEvent(MotionEvent ev) {
              mGestureDetector.onTouchEvent(ev);
              if (ev.getAction() == MotionEvent.ACTION_UP && !mLongClicked)
                  this.callOnClick();
              return false;
          }
      }
      

      【讨论】:

      • 这种方法的缺点是不再使用 CardView 的选择器,因此不会显示 CardView 的任何反馈。
      • 它还消耗应该去 RecyclerView 项目的点击。
      猜你喜欢
      • 2013-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-16
      相关资源
      最近更新 更多