看一看ScrollView的源码

2020-05-04  本文已影响0人  凤鸣游子
欣赏
你对明天有没有美好的期待呢,它是否会如期而来呢.....
线索
  1. 主要的成员标记
  2. 核心功能与方法
1. 成员
mScroller: 用来计算内部内容滚动的辅助类OverScroller,但是主要的滚动并没有使用到他哦

mEdgeGlowTop: 当滚动到最顶部的时候的上边界阴影效果

mEdgeGlowBottom: 当滚动到最底部时候的下边界阴影效果

mChildToScrollTo: 记录拥有焦点的view, 在layout的时候会将位置滚动到他的地方

mIsBeingDragged: true表示scrollview在滚动;注意在嵌套滚动的时候,不属于scrollView的滚动,他为false;

mFillViewport: true, ScrollView模式不是MeasureSpec.UNSPECIFIED, 会用scrollView的高度来当自己的高度,重新在给子view测量一遍;一般他都不会是这个模式, 什么情况下ScrollView是UNSPECIFIED模式呢,从scollView的测量可以看出,他给子view测量的规格就是UNSPECIFIED,因此scrollView嵌套scrollView的时候, 第二个scrollView如果设置了mFillViewport,依然是不会重新测量他的子view的哦。

mActivePointerId: 当前激活的手指; 这个是scrollView处理多手指滑动的原则。他的基本规则是这样的,当第二个手指落下滑动的时候,那么前面第一个手指滑动就失效了,因为将激活的手指给到了第二个手指,当第二个手指抬起的时候,又会把激活的点给到第一个手指。

其他的就不一一列出来了......
2. 功能与方法

普通的LinearLayout, RelativeLayout, FrameLayout他们的页面内容都是有限的,限于一屏之内,而ScrollView却不是哦,这就是他主要功能。

public ScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    initScrollView();
    final TypedArray a = context.obtainStyledAttributes(
        attrs, com.android.internal.R.styleable.ScrollView, defStyleAttr, defStyleRes);
  //读取是否设置了viewport属性,让子view填充scrollView高度
    setFillViewport(a.getBoolean(R.styleable.ScrollView_fillViewport, false));

    a.recycle();
}


private void initScrollView() {
    mScroller = new OverScroller(getContext());
    setFocusable(true);
    //优先让子view获得焦点
    setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
    setWillNotDraw(false);
    final ViewConfiguration configuration = ViewConfiguration.get(mContext);
    //滚动识别距离,
    mTouchSlop = configuration.getScaledTouchSlop();
    //fling滑行的最小速度
    mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
     //fling滑行的最大速度
    mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
    mOverscrollDistance = configuration.getScaledOverscrollDistance();
    mOverflingDistance = configuration.getScaledOverflingDistance();
}

protected void onOverScrolled(int scrollX, int scrollY,
            boolean clampedX, boolean clampedY) {
    //.
    if (!mScroller.isFinished()) {//还没滚动完
        final int oldX = mScrollX;
        final int oldY = mScrollY;
        mScrollX = scrollX;
        mScrollY = scrollY;
        invalidateParentIfNeeded();
        onScrollChanged(mScrollX, mScrollY, oldX, oldY);
        if (clampedY) {
            mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
        }
    } else {
        //进行内容的滚动哦!
        super.scrollTo(scrollX, scrollY);
    }

    //唤醒scrollbar
    awakenScrollBars();
}

 case MotionEvent.ACTION_UP://主手指抬起
    if (mIsBeingDragged) {
        //计算速度
        final VelocityTracker velocityTracker = mVelocityTracker;
        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
        int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
      //判断速度能不能达到滑行的标准
        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
            //滑行
            flingWithNestedDispatch(-initialVelocity);
        } else if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0,
                                        getScrollRange())) {
            postInvalidateOnAnimation();
        }
      //清除手指活动目标
        mActivePointerId = INVALID_POINTER;
        endDrag();
    }
    break;
    case MotionEvent.ACTION_CANCEL:
    if (mIsBeingDragged && getChildCount() > 0) {
        if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
            postInvalidateOnAnimation();
        }
        //清除手指活动目标
        mActivePointerId = INVALID_POINTER;
        endDrag();
    }
    break;
    case MotionEvent.ACTION_POINTER_DOWN: {//副手指手指放下
        final int index = ev.getActionIndex();
        mLastMotionY = (int) ev.getY(index);
        //将副手指替换成主手指
        mActivePointerId = ev.getPointerId(index);
        break;
    }
    case MotionEvent.ACTION_POINTER_UP://副手指抬起
  //将原来的第一个手指替换成现在的主手指,或者抬起的是第一个手指,那么什么都不用做了。
    onSecondaryPointerUp(ev);
    mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
    break;

private void onSecondaryPointerUp(MotionEvent ev) {
    final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
        MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    final int pointerId = ev.getPointerId(pointerIndex);
    //如果当前抬起的是主手指,就要进行主手指的重新定位,如果不是那么就啥都不做.....
    if (pointerId == mActivePointerId) {
        // This was our active pointer going up. Choose a new
        // active pointer and adjust accordingly.
        // TODO: Make this decision more intelligent.
        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
        mLastMotionY = (int) ev.getY(newPointerIndex);
        mActivePointerId = ev.getPointerId(newPointerIndex);
        if (mVelocityTracker != null) {
            mVelocityTracker.clear();
        }
    }
}
3. 结束
上一篇 下一篇

猜你喜欢

热点阅读