android - 检测 recyclerView 上的滑动手势时出现问题,MotionEvent e1 为空?
问题描述
我有一个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
,或者有时在我尝试多次滑动之后,它开始检测到滑动!然后它变得柔软光滑。我检查并注意到该方法中的第一个参数 MotionEvent e1onFling()
为空,直到我第一次滚动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)
解决方案
制作 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()
}
})
}
推荐阅读
- user-interface - 为什么不鼓励从代码中移动 GUI 窗口?
- syntax - ^ SyntaxError: 无效的语法
- visual-studio-code - Live Sass 编译器未编译
- reactjs - 无法在 useEffect 中对未安装的组件执行 React 状态更新
- java - 未显示通知并崩溃说 RemoteServiceException
- java - 如何添加它首先删除节点并删除节点最后
- flutter - 使用 Bloc 刷新 Flutter 中的对话框
- r - 如何为所有列添加引号?
- pandas - 对熊猫中每个组内的数据进行重新采样
- html - HTML 中的多个参考书目,使用关键字 - 可能吗?