欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

弹性ScrollView-仿微信弹性页面

程序员文章站 2022-05-31 09:05:55
...

一、先看效果

弹性ScrollView-仿微信弹性页面

二、准备工作

要实现这个效果,我的想法是给ScrollView包一个container,然后判断边界值,看当前的touch事件交给谁去处理。回弹的效果交给Scroller处理,甚至可以写出一个下拉刷新,看代码分析吧。

三、代码分析

在dispatchTouchEvent中去判断边界值,dispatchTouchEvent返回true或者false都是自己消费当前事件,否则super传递下去。代码如下:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        float currentY = ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastY = currentY;
                break;
            case MotionEvent.ACTION_MOVE:
                float distanceY = currentY - mLastY;
                if (mIsDraging) {
                    if (distanceY <= 0) {
                        if(mScrollView.isScrolledToTop()) {
                            scrollTo(0, 0);
                            mIsDraging = false;
                            return super.dispatchTouchEvent(ev);
                        }
                    }else{
                        if(mScrollView.isScrolledToBottom()){
                            scrollTo(0, 0);
                            mIsDraging = false;
                            return super.dispatchTouchEvent(ev);
                        }
                    }
                    scrollTo(0, (int) (-distanceY * ratio));
                    return true;
                } else {
                    if (Math.abs(distanceY) > mTouchSlop) {
                        if (distanceY > 0) {  // 向下
                            if (mScrollView.isScrolledToTop()) {
                                mLastY = currentY;
                                mIsDraging = true;
                            }
                        }else{
                            if(mScrollView.isScrolledToBottom()){
                                mLastY = currentY;
                                mIsDraging = true;
                            }
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mIsDraging) {
                    int scrollY = getScrollY();
                    mScroller.startScroll(0, scrollY, 0, -scrollY, duration);
                    mIsDraging = false;
                    invalidate();
                    return true;
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
  1. 首先DOWN下记录起始位置,MOVE下先判断当前是不是拖拽状态mIsDraing,否,进入else里面,如果手指滑动距离需要响应了,判断方向,如果向下,还要看ScrollView是不是在顶部(为什么?),如果在顶部,那么记录当前 y 坐标(因为在判断这个期间,手指已经向下滑了一段距离了,如果还记录之前的位置,会很突兀),并将拖拽mIsDraing = true,开始向下。
  2. 这个时候再进入MOVE,已经是拖拽中,这个时候可以直接调用
scrollTo(0, (int) (-distanceY * ratio));
return true;

ratio是弹性系数。但是考虑到一种情况就是,刚开始下滑的时候手指突然向上滑动,这个时候container到顶了,ScrollView也在顶部,这个时候事件需要交给ScrollView而不是container继续滑动。同样的道理在底部回弹的时候,也存在上拉container到底了这个时候手指向下滑动,同样需要把事件交给ScrollView处理。所以在scrollTo之前需要再一次判断distanceY,如果小于0并且ScrollView在底部,事件传递,container需要恢复开始位置,这个时候虽然手指没有抬起,但是也不能是拖拽状态:

                            scrollTo(0, 0);
                            mIsDraging = false;
                            return super.dispatchTouchEvent(ev);
  1. ScrollView需要能判断有没有到顶或到底,重写一个StretchScrollView:
@Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (getScrollY() == 0) {
            isScrolledToTop = true;
            isScrolledToBottom = false;
        } else if (getScrollY() + getHeight() - getPaddingTop()-getPaddingBottom() == getChildAt(0).getHeight()) {
            isScrolledToTop = false;
            isScrolledToBottom = true;
        } else {
            isScrolledToTop = false;
            isScrolledToBottom = false;
        }
    }

很简单对吧,同样适用于ListView,区别仅仅在于判断顶部底部的方法不一样。最后,可以扩展一点,凡是可以判断是顶部底部的view都可以加进来,抽象一个StretchView出来。附上gayHub地址。有兴趣拓展吧!

相关标签: 弹性ScrollView