Android解决MyScrollView包含RecyclerView刷新数据会滑动到RecyclerView顶部的问题

首先要说说MyScrollView是什么东西,其实这个控件大家用过的话都会了解,Android原生自带的ScrollView中如果包裹了RecyclerView或者ListView等控件的话,那么运行效果不尽如人意,主要体现在RecyclerView或ListView等数据不能完全显示出来,这是因为系统内部对这些并没有处理,所以这个时候MyScrollView基于ScrollView进行了改造,使得我们的数据能够完全显示并且没有滑动冲突等问题,这类代码网上有很多,我这里只简单的提供一个供大家参考:

public class MyScrollView extends ScrollView {
    private List<OnScrollListener> onScrollListeners;

    //滑动开始、结束状态回调
    private List<OnScrollStateListener> mOnScrollStateListeners;

    /** 当前滑动到y坐标 */
    private int mCurrentY;

    /** 滑动到底部监听器 */
    private OnScrollToBottomListener mOnScrollToBottom;

    /** 滑动到底部监听器 */
    public interface OnScrollToBottomListener {
        void onScrollBottom();
    }

    /**
     * 主要是用在用户手指离开MyScrollView,MyScrollView还在继续滑动,我们用来保存Y的距离,然后做比较
     */
    private int lastScrollY;

    private long DELAY_TIME = 100;    //延迟判断滑动是否结束
    private long lastScrollTime = -1;  //上次滑动时间

    /**
     * 用于用户手指离开MyScrollView的时候获取MyScrollView滚动的Y距离,然后回调给onScroll方法中
     */
    private Handler handler = new Handler() {

        public void handleMessage(android.os.Message msg) {
            int scrollY = MyScrollView.this.getScrollY();

            // 此时的距离和记录下的距离不相等,在隔5毫秒给handler发送消息
            if (lastScrollY != scrollY) {
                lastScrollY = scrollY;
                handler.sendMessageDelayed(handler.obtainMessage(), 5);
            }
            if (onScrollListeners != null) {
                for (OnScrollListener listner : onScrollListeners) {
                    listner.onScroll(scrollY);
                }
            }
        }

        ;
    };

    //通过延时时间判断onScroll函数是否执行
    private Runnable scrollTask = new Runnable() {
        @Override public void run() {
            long currentTime = System.currentTimeMillis();
            if (currentTime - lastScrollTime > DELAY_TIME) {
                lastScrollTime = -1;
                onScrollStop();
            } else {
                //滑动中
                handler.postDelayed(this, DELAY_TIME);
            }
        }
    };

    //开始滑动
    private void onScrollStart() {
        if (mOnScrollStateListeners == null) {
            return;
        }

        for (OnScrollStateListener listener:mOnScrollStateListeners) {
            listener.onScrollStart();
        }
    }

    //停止滑动
    private void onScrollStop() {
        if (mOnScrollStateListeners == null) {
            return;
        }

        for (OnScrollStateListener listener:mOnScrollStateListeners) {
            listener.onScrollStop();
        }
    }

    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 设置滑动状态监听
     */
    public void setOnScrollStateListener(OnScrollStateListener listener) {
        if (mOnScrollStateListeners == null) {
            mOnScrollStateListeners = new ArrayList<>();
        }

        if (listener != null) {
            mOnScrollStateListeners.add(listener);
        }
    }

    public void removeScrollStateListener(OnScrollStateListener listener) {
        if (listener != null && mOnScrollStateListeners != null) {
            mOnScrollStateListeners.remove(listener);
        }
    }

    /**
     * 设置滚动接口
     */
    public void setOnScrollListener(OnScrollListener onScrollListener) {
        if (onScrollListeners == null) {
            onScrollListeners = new ArrayList<>();
        }
        onScrollListeners.add(onScrollListener);
    }

    public void removeAllScrollListener() {
        if (onScrollListeners != null) {
            onScrollListeners.clear();
        }
    }

    public void setOnScrollToBottomListener(OnScrollToBottomListener listener) {
        mOnScrollToBottom = listener;
    }

    private float xDistance, yDistance, lastX, lastY;

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xDistance = yDistance = 0f;
                lastX = ev.getX();
                lastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                final float curX = ev.getX();
                final float curY = ev.getY();
                xDistance += Math.abs(curX - lastX);
                yDistance += Math.abs(curY - lastY);
                lastX = curX;
                lastY = curY;
                if (xDistance > yDistance) {
                    return false;
                }
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 重写onTouchEvent, 当用户的手在MyScrollView上面的时候,
     * 直接将MyScrollView滑动的Y方向距离回调给onScroll方法中,当用户抬起手的时候,
     * MyScrollView可能还在滑动,所以当用户抬起手我们隔5毫秒给handler发送消息,在handler处理
     * MyScrollView滑动的距离
     */
    @Override public boolean onTouchEvent(MotionEvent ev) {
        if (onScrollListeners != null) {
            for (OnScrollListener listner : onScrollListeners) {
                listner.onScroll(lastScrollY = this.getScrollY());
                listner.onTouchEvent(ev);
            }
        }
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                handler.sendMessageDelayed(handler.obtainMessage(), 20);
                break;
        }
        return super.onTouchEvent(ev);
    }

    public interface OnScrollStateListener {
        void onScrollStart();  //开始滑动

        void onScrollStop();   //停止滑动
    }

    /**
     * 滚动的回调接口
     */
    public interface OnScrollListener {

        /**
         * 回调方法, 返回MyScrollView滑动的Y方向距离
         */
        public void onScroll(int scrollY);

        /**
         * 回调方法, 返回MyScrollView滑动到Y方向上的位置
         */
        public void onScrollPosition(int position);

        public void onTouchEvent(MotionEvent ev);

    }

    @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        if (onScrollListeners != null) {
            for (OnScrollListener listner : onScrollListeners) {
                int scrollY = getScrollY();
                listner.onScrollPosition(scrollY);
            }
        }

        if (lastScrollTime == -1) {
            onScrollStart();
            postDelayed(scrollTask, DELAY_TIME);
        }
        lastScrollTime = System.currentTimeMillis();  //更新scrollView的滑动时间

        super.onScrollChanged(l, t, oldl, oldt);
    }

    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        //下滑
        if (scrollY > mCurrentY && scrollY > 0 && clampedY) {
            if (null != mOnScrollToBottom) {
                mOnScrollToBottom.onScrollBottom();
            }
        }
        mCurrentY = scrollY;
    }
}

但是如果使用起来的话还是会发现另一个问题,那就是当其中的RecyclerView的数据进行刷新或者发生变化时,这个时候会发现页面内容会滑动到RecyclerView的顶部,而它上面的内容会滑出屏幕,这个原因主要是因为RecyclerView获取到了焦点,从而进行了一个滑动的操作,要想解决这个问题的方法也很简单,那就是在xml文件中MyScrollView下的第一个子节点添加2个属性,举个例子:

<com.view.MyScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:overScrollMode="never"
    android:scrollbars="none">

    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="vertical"
      android:focusable="true"
      android:focusableInTouchMode="true"
      android:paddingBottom="24dp">
	
	......

其中

android:focusable=“true”
android:focusableInTouchMode=“true”

这2个属性表示LinearLayout会获取焦点,这个时候RecyclerView就不会拿到焦点了,从而在数据发生变化时不会自动的滑动到列表的顶部。


良好的记录习惯,方便他人,也方便自己~

发布了87 篇原创文章 · 获赞 161 · 访问量 87万+

猜你喜欢

转载自blog.csdn.net/woshizisezise/article/details/83582386