当前位置:  开发笔记 > Android > 正文

ListView内部的水平RecyclerView不能滚动得非常流畅

如何解决《ListView内部的水平RecyclerView不能滚动得非常流畅》经验,为你挑选了2个好方法。

我有一个ListView,每个项目都是水平的RecyclerView.我面临的问题是,水平滚动不是很平滑.如果我在一个完全水平的方向上滚动/滑动,那么它工作正常,但如果滚动甚至略微非水平(比如与水平成10度角),它认为上/下滚动而不是左/右,由于父母ListView.我试过这个方法:

   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;
                }
            });

但它不是很有效.事实上,我没有注意到很多改进.那是因为只有当动作完全水平时才会调用此动作事件 - 这种情况已经正常工作了.然后我尝试这样做:

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.而这也未能产生任何明显的改善.我也注意到这些方法在某种程度上也依赖于压力.当我用轻微的压力轻扫时,什么也没发生.同样的事情在同一条路上完成,压力越大,就会产生滚动.

即使滑动的路径不是水平直的,或者即使动作是圆形的,有没有办法使滚动变得平滑?任何帮助将受到高度赞赏.



1> user1354603..:

Jim Baca让我走上正轨,我的问题是父视图(垂直滚动)正在从子视图(水平滚动)中窃取滚动.这对我有用:

/**
 * 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());
    }
}



2> Jim Baca..:

问题可能是触摸事件不确定去哪里.您可能希望使用onInterceptTouchEvent和TouchSlop来确定是否应该从子行中窃取触摸事件(回收器视图).

onInterceptTouchEvent非常重要,因此ListView可以决定它是否应该窃取来自子项的触摸事件.

触摸倾斜是重要的,因为它定义了在我们采取行动之前需要多少运动(在这种情况下,从孩子那里窃取事件).这是因为只需用手指触摸屏幕(没有明显/预期的移动)就会生成MotionEvent.ACTION_MOVE事件(您可以通过在MOTION_MOVE下添加日志来自行验证,以打印出getX和getY事件.

如果仍遇到问题,请确认回收站视图的子项也不期望运动事件.如果这些孩子是他们可能需要使用requestDisallowInterceptTouchEvent来阻止recyclerview和listview拦截他们的事件.

此代码基于在Android文档中找到的代码,您可以在此处查看原始内容并阅读有关触摸事件的更多信息.

     MotionEvent mOriginal;
     mIsScrolling = false;
     // put these two lines in your constructor
     ViewConfiguration vc = ViewConfiguration.get(view.getContext());
     mTouchSlop = vc.getScaledTouchSlop();


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    /*
     * This method JUST determines whether we want to intercept the motion.
     * If we return true, onTouchEvent will be called and we do the actual
     * scrolling there.
     */


    final int action = MotionEventCompat.getActionMasked(ev);

    // Always handle the case of the touch gesture being complete.
    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
        // Release the scroll.

        mIsScrolling = false;
        return false; // Do not intercept touch event, let the child handle it
    }

    switch (action) {
        case MotionEvent.ACTION_DOWN:  {
            mIsScrolling = false;
            setOriginalMotionEvent(ev);
        }
        case MotionEvent.ACTION_MOVE: {
            if (mIsScrolling) {
                // We're currently scrolling, so yes, intercept the 
                // touch event!
                return true;
            }

            // If the user has dragged her finger horizontally more than 
            // the touch slop, start the scroll

            final int xDiff = calculateDistanceX(ev);
            final int yDiff = calculateDistanceY(ev);

            // Touch slop should be calculated using ViewConfiguration 
            // constants.
            if (yDiff > mTouchSlop &&  yDiff > xDiff) { 
                // Start scrolling!
                mIsScrolling = true;
                return true;
            }
            break;
        }
        ...
    }

    // 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){
     mOriginal = ev.clone();
}

public int calculateDistanceX(ev){
    int distance = Math.abs(mOriginal.getX() - ev.getX());
    return distance;
}

public int calculateDistanceY(ev){
    int distance = Math.abs(mOriginal.getY() - ev.getY());
    return distance;
}

推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有