问题描述
我有一个ListView
,每个项目都是水平RecyclerView
.我面临的问题是,水平滚动不是很平滑.如果我沿完美的水平方向滚动/滑动,则效果很好,但如果滚动甚至略微不水平(例如与水平方向成10度角),则将其视为上/下滚动,而不是左/右,由于父级ListView
.我尝试过这种方法:
I have a ListView
, each of whose items is a Horizontal RecyclerView
. The problem that I am facing is, the horizontal scroll is not very smooth. If I scroll/swipe in a perfectly horizontal direction, then it works fine but if the scroll is even slightly non-horizontal (say at a 10 degree angle to horizontal), it regards that as up/down scroll rather than left / right, due to the parent ListView
. I tried this method:
recyclerView.setOnTouchListener(new FlingRecyclerView.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
Log.i("Scroll down", "Intercept true");
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_MOVE:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
// Handle HorizontalScrollView touch events.
v.onTouchEvent(event);
return true;
}
});
但这不是很有效.实际上,我没注意到有什么改进.那是因为只有在运动完全水平时才调用该motionEvent-这种情况已经可以正常工作了.然后我尝试这样做:
But it was not very effective. In fact, I couldn't notice much improvement. That's because this motionEvent gets called only when the motion is perfectly horizontal - that case is already working fine. Then I tried doing this:
class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
Log.i("TAG", "VelocityX " + Float.toString(velocityX) + "VelocityY " + Float.toString(velocityY));
Log.i("TAG", "e1X " + Float.toString(e1.getX()) + "e1Y " + Float.toString(e1.getY()) + "e2X " + Float.toString(e2.getX()) + "e2Y " + Float.toString(e2.getY()));
// downward swipe
if (e2.getY() - e1.getY() > SWIPE_MAX_OFF_PATH && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
Toast.makeText(TestActivity.this, "Downward Swipe", Toast.LENGTH_SHORT).show();
Log.i("TAG", "Downward swipe");
}
else if (e1.getY() - e2.getY() > SWIPE_MAX_OFF_PATH && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
Toast.makeText(TestActivity.this, "Upward Swipe", Toast.LENGTH_SHORT).show();
Log.i("TAG", "Upward swipe");
}
// right to left swipe
else if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Toast.makeText(TestActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
Log.i("TAG", "Left swipe");
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Toast.makeText(TestActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show();
Log.i("TAG", "Right swipe");
}
} catch (Exception e) {
// nothing
}
return false;
}
}
我注意到它仅在松开手指时才记录事件,而不是在我开始滑动时才记录事件.我触摸RecyclerView
后,它将无法正常工作.而且,这也没有产生任何明显的改善.我还注意到一件事,这些方法也在某种程度上取决于压力.当我轻压一下时,什么也没发生.在相同的路径上做同样的事情,压力更大,确实会产生滚动.
And I noticed that it registers the events only when I release my finger, not when I start the swipe. It does not work as soon as I touch the RecyclerView
. And this too, failed to produce any noticeable improvement. I also noticed one thing that these methods are somehow dependent on pressure too. When I did a swipe with a light pressure, nothing happened. And the same thing done in same path, with greater pressure does produce scroll.
即使滑动路径不是水平笔直还是运动为圆形,是否有任何方法可以使滚动平滑?任何帮助将不胜感激.
Is there any way to make the scroll smooth even if the path of swipe is not horizontally straight or even if the motion is kind of circular? Any help would be highly appreciated.
推荐答案
Jim Baca使我走上了正确的轨道,我的问题是父视图(垂直滚动)从子视图(水平滚动)窃取了滚动.这对我有用:
Jim Baca got me on the right track, my problem was that the parent view(vertical scroll) was stealing the scroll from child views(horizontal scroll). This worked for me:
/**
* This class is a workaround for when a parent RecyclerView with vertical scroll
* is a bit too sensitive and steals onTouchEvents from horizontally scrolling child views
*/
public class NestedScrollingParentRecyclerView extends RecyclerView {
private boolean mChildIsScrolling = false;
private int mTouchSlop;
private float mOriginalX;
private float mOriginalY;
public NestedScrollingParentRecyclerView(Context context) {
super(context);
init(context);
}
public NestedScrollingParentRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public NestedScrollingParentRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
ViewConfiguration vc = ViewConfiguration.get(context);
mTouchSlop = vc.getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the scroll
mChildIsScrolling = false;
return false; // Let child handle touch event
}
switch (action) {
case MotionEvent.ACTION_DOWN: {
mChildIsScrolling = false;
setOriginalMotionEvent(ev);
}
case MotionEvent.ACTION_MOVE: {
if (mChildIsScrolling) {
// Child is scrolling so let child handle touch event
return false;
}
// If the user has dragged her finger horizontally more than
// the touch slop, then child view is scrolling
final int xDiff = calculateDistanceX(ev);
final int yDiff = calculateDistanceY(ev);
// Touch slop should be calculated using ViewConfiguration
// constants.
if (xDiff > mTouchSlop && xDiff > yDiff) {
mChildIsScrolling = true;
return false;
}
}
}
// In general, we don't want to intercept touch events. They should be
// handled by the child view. Be safe and leave it up to the original definition
return super.onInterceptTouchEvent(ev);
}
public void setOriginalMotionEvent(MotionEvent ev) {
mOriginalX = ev.getX();
mOriginalY = ev.getY();
}
public int calculateDistanceX(MotionEvent ev) {
return (int) Math.abs(mOriginalX - ev.getX());
}
public int calculateDistanceY(MotionEvent ev) {
return (int) Math.abs(mOriginalY - ev.getY());
}
}
这篇关于ListView内的水平RecyclerView滚动不流畅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!