【问题标题】:Detect click on RecyclerView outside of items检测点击项目外的 RecyclerView
【发布时间】:2014-12-30 10:31:24
【问题描述】:

我有一个 RecyclerView,其中有 2 个项目没有填满整个屏幕。如何检测到用户点击了 RecyclerView 的空白部分(意味着直接点击了 RecyclerView 而不是其中一项)?

【问题讨论】:

    标签: android android-recyclerview


    【解决方案1】:

    如评论中所述

    mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    
      @Override
      public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
        if (motionEvent.getAction() != MotionEvent.ACTION_UP) {
            return false;
        }
        View child = recyclerView.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
        if (child != null) {
          // tapped on child
          return false;
        } else {
          // Tap occured outside all child-views.
          // do something
          return true;
        }
      }
    
      @Override
      public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
      }
    });
    

    【讨论】:

    • 还应该检查 MotionEvent.getAction() == ACTION_UP,否则,你会得到 2 个点击事件。
    • 这应该是公认的答案。此外,它可能应该是return false;,而不是MotionEvent.ACTION_UP 部分下的return;
    • 这适用于一个问题:在某些设备上,单击视图会发出声音。在这种情况下,它不会。这可以通过在返回 true 之前调用 mRecyclerView.playSoundEffect(SoundEffectConstants.CLICK); 来解决。
    【解决方案2】:

    您可以继承 RecyclerView 并覆盖 dispatchTouchEvent() 方法来完成此操作。使用findChildViewUnder() 方法,我们可以确定触摸事件是否发生在子View 之外,如果是,则使用interface 通知侦听器。在以下示例中,OnNoChildClickListenerinterface 提供了该功能。

    public class TouchyRecyclerView extends RecyclerView
    {
        // Depending on how you're creating this View,
        // you might need to specify additional constructors.
        public TouchyRecyclerView(Context context, AttributeSet attrs)
        {
            super(context, attrs);
        }
    
        private OnNoChildClickListener listener;
        public interface OnNoChildClickListener
        {
            public void onNoChildClick();
        }
    
        public void setOnNoChildClickListener(OnNoChildClickListener listener)
        {
            this.listener = listener;
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event)
        {
            // The findChildViewUnder() method returns null if the touch event
            // occurs outside of a child View.
            // Change the MotionEvent action as needed. Here we use ACTION_DOWN
            // as a simple, naive indication of a click.
            if (event.getAction() == MotionEvent.ACTION_DOWN
                && findChildViewUnder(event.getX(), event.getY()) == null)
            {
                if (listener != null)
                {
                    listener.onNoChildClick();
                }
            }
            return super.dispatchTouchEvent(event);
        }
    }
    

    注意:这适用于 RecyclerView 来自 my answer hereGridView 相关的内容。

    【讨论】:

    • 非常感谢!我最终将 recyclerView.addOnItemTouchListener 与 findChildViewUnder(..) 一起使用。
    • @Omar 啊!很好!我还在熟悉 RecyclerView,我还不知道那个界面。谢谢(你的)信息!您应该为此创建一个答案。你会得到我的支持。
    【解决方案3】:

    @driss-bounouar 的回答几乎是正确的,尽管这会阻止用户滚动回收站视图,因为任何向下事件都会导致您的操作发生。稍作修改,我们记录下事件,然后检查上事件,如果坐标没有太大变化,然后触发事件。

    private MotionEvent lastRecyclerViewDownTouchEvent;
    myRecyclerView.setOnTouchListener(new View.OnTouchListener() {
                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
                            if (event.getAction() == MotionEvent.ACTION_DOWN && myRecyclerView.findChildViewUnder(event.getX(), event.getY()) == null) {
                                lastRecyclerViewDownTouchEvent = event;
                            } else if (event.getAction() == MotionEvent.ACTION_UP && myRecyclerView.findChildViewUnder(event.getX(), event.getY()) == null
                                    && lastRecyclerViewDownTouchEvent != null) {
                                // Check to see if it was a tap or a swipe
                                float xDelta = Math.abs(lastRecyclerViewDownTouchEvent.getX() - event.getX());
                                float yDelta = Math.abs(lastRecyclerViewDownTouchEvent.getY() - event.getY());
                                if (xDelta < 30 && yDelta < 30) {
                                    // Do action
                                }
                                lastRecyclerViewDownTouchEvent = null;
                            }
                            return false;
                        }
                    });
    

    【讨论】:

      【解决方案4】:

      您只需要在RecyclerView 上设置一个TouchListener,如上所示:

      categoryTable.setAdapter(new CatgoriesAdapter(categories.getWrappedList()));
          categoryTable.setOnTouchListener(new View.OnTouchListener() {
              @Override
              public boolean onTouch(View v, MotionEvent event) {
                  if (event.getAction() == MotionEvent.ACTION_DOWN
                          && categoryTable.findChildViewUnder(event.getX(), event.getY()) == null)
                  {
                      // Touch outside items here, you do whatever you want  
                      HideCategoryMenu();
                  }
                  return false;
              }
          });
      

      【讨论】:

        【解决方案5】:

        @Angel Kjosski 的答案在 Kotlin 中看起来像这样:

        yourRecyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
                        override fun onInterceptTouchEvent(recyclerView: RecyclerView, motionEvent: MotionEvent): Boolean {
                            return when {
                                motionEvent.action != MotionEvent.ACTION_UP || recyclerView.findChildViewUnder(motionEvent.x, motionEvent.y) != null -> false
                                else -> {
                                    // do something here
                                    true
                                }
                            }
                        }
        
                        override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
                        override fun onTouchEvent(recyclerView: RecyclerView, motionEvent: MotionEvent) {}
                    })
        

        【讨论】:

          【解决方案6】:

          这对我有用,我用它来代替 RecyclerView:

          public class ClickeableRecyclerView extends RecyclerView
          {
              private static final int CLICK_DURATION = 800;
              private CountDownTimer clickCountDown;
              private final Object sincClick = new Object();
              private boolean shortClick = true;
          
              public ClickeableRecyclerView(@NonNull Context context)
              {
                  super(context);
                  init();
              }
          
              public ClickeableRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs)
              {
                  super(context, attrs);
                  init();
              }
          
              public ClickeableRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)
              {
                  super(context, attrs, defStyleAttr);
                  init();
              }
          
              private void init()
              {
                  clickCountDown = new CountDownTimer(CLICK_DURATION, 100)
                  {
                      public void onTick(long millisUntilFinished)
                      {
                          synchronized(sincClick)
                          {
                              shortClick = true;
                          }
                      }
                      public void onFinish()
                      {
                          synchronized(sincClick)
                          {
                              shortClick = false;
                          }
                      }
                  };
              }
          
              @Override
              public boolean onTouchEvent(MotionEvent e)
              {
                  if(findChildViewUnder(e.getX(), e.getY()) == null)
                  {
                      if(e.getAction() == MotionEvent.ACTION_DOWN)
                      {
                          clickCountDown.start();
                      }
                      else if(e.getAction() == MotionEvent.ACTION_UP)
                      {
                          synchronized(sincClick)
                          {
                              if(shortClick)
                              {
                                  performClick();
                                  clickCountDown.cancel();
                              }
                          }
                      }
                  }
                  else
                  {
                      clickCountDown.cancel();
                  }
                  return super.onTouchEvent(e);
              }
          
              @Override
              public boolean performClick()
              {
                  return super.performClick();
              }
          }
          

          在布局中:

          <yourpackage.ClickeableRecyclerView
              android:id="@+id/recyclerViewItems"
              android:layout_width="match_parent"
              android:layout_height="0dp"
              android:background="@color/white"
              android:visibility="visible"
              app:layout_constraintBottom_toTopOf="@+id/viewTotal"
              app:layout_constraintEnd_toEndOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintTop_toBottomOf="@+id/buttonShowItems"
              tools:visibility="visible" />
          

          在活动中:

          private ClickeableRecyclerView recyclerView;
          
          recyclerView = findViewById(R.id.recyclerView);
          
          recyclerView.setOnClickListener(new View.OnClickListener()
          {
              @Override
              public void onClick(View v)
              {
                  // Do something when click no items
              }
          });
          

          希望对你有帮助。

          【讨论】:

            【解决方案7】:
            shortcutDeviceRecly.setOnTouchListener(new View.OnTouchListener() {
                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
            
                            if(event.getAction() == MotionEvent.ACTION_UP){
                                roomClickEvent(true, v);
                            }
            
                            return false;
                        }
                    });
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多