【问题标题】:Problem in detecting swipe gesture on recyclerView, MotionEvent e1 is null?检测 recyclerView 上的滑动手势时出现问题,MotionEvent e1 为空?
【发布时间】:2021-08-17 17:57:16
【问题描述】:

我有一个recyclerView 和一个tabLayout。当用户浏览选项卡时,recyclerView 会加载新项目。我现在想要的是添加滑动功能,因此当用户在recyclerView 上向右或向左滑动时,应用程序会在选项卡之间切换(并使用新项目重新加载recyclerView)。我很难有效地检测recyclerView 上的滑动(右/左)手势。我不想使用 ViewPager,因为我想让我的应用程序轻便快速。除了我想要的唯一功能外,只有滑动。所以这是我使用的最后一种方法:

OnSwipeTouchListener onSwipeTouchListener = new OnSwipeTouchListener(getActivity())
    {
        public void onSwipeRight() {
            //update recyclerview with new data
        }

        public void onSwipeLeft(){
            //update recyclerview with new data
        }
    };

b.rcvItems.setOnTouchListener(onSwipeTouchListener);



b.rcvItems.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if (dy > 0) {
                int threshold = 20;
                int count = adapter.getItemCount();
                LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                int lastVisible = layoutManager.findLastVisibleItemPosition();
                if (lastVisible >= count - threshold) { 
                    //load more items
                }
            }
        }
    });

这里是 OnSwipeTouchListener 类,其中是 GestureListener 类:

public class OnSwipeTouchListener implements View.OnTouchListener {
private final GestureDetector gestureDetector;

protected OnSwipeTouchListener(Context ctx) {
    gestureDetector = new GestureDetector(ctx, new GestureListener());
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    return gestureDetector.onTouchEvent(event);
}

public void onSwipeRight() {
}

public void onSwipeLeft() {
}

public void onSwipeTop() {
}

public void onSwipeBottom() {
}





private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
    private static final int SWIPE_THRESHOLD = 23;
    private static final int SWIPE_VELOCITY_THRESHOLD = 0;

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        boolean result = false;
        try {
            float diffY = e2.getY() - e1.getY();
            float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        onSwipeRight();
                    } else {
                        onSwipeLeft();
                    }
                    result = true;
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}
}

现在的问题是当Fragment 加载时,首先,滑动功能不起作用。但是在我滚动recyclerView 之后,或者有时在我尝试多次滑动之后,它开始检测到滑动!然后它变得柔软光滑。我检查并注意到方法 onFling() 中的第一个参数 MotionEvent e1 为空,直到我第一次滚动我的 recyclerView 或在多次滑动尝试之后。仍然在某些发布时,滑动功能甚至在一开始就可以正常工作! (这种情况很少见,但它会发生)。它太老套了,我什至无法弄清楚正确的行为。我该如何解决这个问题?

旁注:有时应用程序会冻结然后崩溃。我得到以下崩溃日志:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionMyViewHolder{779d2db position=27 id=-1, oldPos=-1, pLpos:-1 no parent} androidx.recyclerview.widget.RecyclerView{2415051 VFED..... ........ 0,0-1080,2049 #7f09016d app:id/rcvItems}, adapter:ui.adapter.ItemsAdapter@bee10b6, layout:androidx.recyclerview.widget.LinearLayoutManager@38c1b7, context:ui.MainActivity@577a61c
    at androidx.recyclerview.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5972)
    at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6156)
    at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:288)
    at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:345)
    at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:361)
    at androidx.recyclerview.widget.GapWorker.prefetch(GapWorker.java:368)
    at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:399)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:237)
    at android.app.ActivityThread.main(ActivityThread.java:8167)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)

【问题讨论】:

    标签: android android-recyclerview swipe


    【解决方案1】:

    制作 OnSwipeTouchListener.kt :

    import android.content.Context
    import android.view.GestureDetector
    import android.view.GestureDetector.SimpleOnGestureListener
    import android.view.MotionEvent
    import android.view.View
    import android.view.View.OnTouchListener
    import kotlin.math.abs
    internal open class OnSwipeTouchListener(c: Context?) :
    OnTouchListener {
       private val gestureDetector: GestureDetector
       override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
          return gestureDetector.onTouchEvent(motionEvent)
       }
       private inner class GestureListener : SimpleOnGestureListener() {
          private val SWIPE_THRESHOLD: Int = 100
          private val SWIPE_VELOCITY_THRESHOLD: Int = 100
          override fun onDown(e: MotionEvent): Boolean {
             return true
          }
          override fun onSingleTapUp(e: MotionEvent): Boolean {
             onClick()
             return super.onSingleTapUp(e)
          }
          override fun onDoubleTap(e: MotionEvent): Boolean {
             onDoubleClick()
             return super.onDoubleTap(e)
          }
          override fun onLongPress(e: MotionEvent) {
             onLongClick()
             super.onLongPress(e)
          }
          override fun onFling(
             e1: MotionEvent,
             e2: MotionEvent,
             velocityX: Float,
             velocityY: Float
          ): Boolean {
             try {
                val diffY = e2.y - e1.y
                val diffX = e2.x - e1.x
                if (abs(diffX) > abs(diffY)) {
                   if (abs(diffX) > SWIPE_THRESHOLD && abs(
                      velocityX
                   ) > SWIPE_VELOCITY_THRESHOLD
                ) {
                   if (diffX > 0) {
                      onSwipeRight()
                   }
                   else {
                      onSwipeLeft()
                   }
                }
             }
              else {
                if (abs(diffY) > SWIPE_THRESHOLD && abs(
                   velocityY
                ) > SWIPE_VELOCITY_THRESHOLD
             ) {
                if (diffY < 0) {
                   onSwipeUp()
                }
                else {
                      onSwipeDown()
                      }
                   }
                   }
             } catch (exception: Exception) {
             exception.printStackTrace()
          }
          return false
          }
       }
       open fun onSwipeRight() {}
       open fun onSwipeLeft() {}
       open fun onSwipeUp() {}
       open fun onSwipeDown() {}
       private fun onClick() {}
       private fun onDoubleClick() {}
       private fun onLongClick() {}
       init {
          gestureDetector = GestureDetector(c, GestureListener())
       }
    }
    
    

    在 MainActivity.kt 中

    val recyclerView = findViewById(R.id.recyclerView)
    recyclerView.setOnTouchListener(object : OnSwipeTouchListener(this@MainActivity) {
             override fun onSwipeLeft() {
                super.onSwipeLeft()
                Toast.makeText(this@MainActivity, "Swipe Left gesture detected",
                Toast.LENGTH_SHORT)
                .show()
             }
             override fun onSwipeRight() {
                super.onSwipeRight()
                Toast.makeText(
                   this@MainActivity,
                   "Swipe Right gesture detected",
                   Toast.LENGTH_SHORT
                ).show()
             }
             override fun onSwipeUp() {
                super.onSwipeUp()
                Toast.makeText(this@MainActivity, "Swipe up gesture detected", Toast.LENGTH_SHORT)
                .show()
             }
             override fun onSwipeDown() {
                super.onSwipeDown()
                Toast.makeText(this@MainActivity, "Swipe down gesture detected", Toast.LENGTH_SHORT)
                .show()
             }
          })
       }
    

    【讨论】:

      【解决方案2】:

      主要原因是 e1 对于初始移动可能为空,因此将其放在 try/catch 块中。对于 try/catch,检查 null 并返回。检查下面的sn-p

      if (e1 == null || e2 == null) 
        return false;
      try {
      ...
      } catch (Exception e) {}
      return false;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-07-30
        • 1970-01-01
        • 2012-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-18
        相关资源
        最近更新 更多