【学习】弹性ScrollView-仿微信弹性页面

一、先看效果

【学习】弹性ScrollView-仿微信弹性页面_第1张图片
stretchScrollView.gif

二、准备工作

要实现这个效果,我的想法是给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-仿微信弹性页面)