NestedScrollVew实现原理分析

NestedScrollView 是 Android 中的一个特殊滚动视图,它继承自 ScrollView,但添加了对嵌套滚动(nested scrolling)的支持。这使得 NestedScrollView 可以与其它支持嵌套滚动的视图(如 RecyclerView)协同工作,从而实现更复杂的滚动效果。

下面我们将结合源码来分析 NestedScrollView 的实现原理。

1. NestedScrollView 类定义

NestedScrollView 继承自 ScrollView,因此它也只接受一个直接子视图。与 ScrollView 不同的是,它支持嵌套滚动,这意味着它可以在内部滚动时允许其子视图也进行滚动。

1public class NestedScrollView extends ScrollView implements NestedScrollingParent, NestedScrollingChild {
2
3    // ...
4
5    // 构造函数
6    public NestedScrollView(Context context) {
7        super(context);
8        initScrollView();
9    }
10
11    public NestedScrollView(Context context, AttributeSet attrs) {
12        super(context, attrs);
13        initScrollView();
14    }
15
16    public NestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
17        super(context, attrs, defStyleAttr);
18        initScrollView();
19    }
20
21    private void initScrollView() {
22        // 初始化操作
23        setFillViewport(true);  // 填充整个视口
24        setFocusable(true);     // 可聚焦
25        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);  // 子视图优先获得焦点
26    }
27
28    // 其他方法...
29}

2. 测量过程(onMeasure)

NestedScrollView 的测量过程与 ScrollView 类似,但是它还需要考虑嵌套滚动的情况。

1@Override
2protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
3    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4
5    // 如果有嵌套滚动的子视图,则需要考虑子视图的滚动情况
6    // 如果子视图是嵌套滚动的,那么需要在测量时考虑到子视图的滚动距离
7    if (hasNestedScrollingChild()) {
8        final View child = getChildAt(0);
9        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
10                child.getMeasuredHeight() + getScrollY(), MeasureSpec.EXACTLY);
11        child.measure(getChildMeasureSpec(widthMeasureSpec,
12                getPaddingLeft() + getPaddingRight(), child.getLayoutParams().width),
13                childHeightMeasureSpec);
14    }
15}

3. 布局过程(onLayout)

布局过程与 ScrollView 相似,但需要考虑嵌套滚动的情况。

1@Override
2protected void onLayout(boolean changed, int l, int t, int r, int b) {
3    final int width = r - l;
4    final int height = b - t;
5    final int paddingLeft = getPaddingLeft();
6    final int paddingTop = getPaddingTop();
7    final int paddingRight = getPaddingRight();
8    final int paddingBottom = getPaddingBottom();
9
10    final int childWidth = width - paddingLeft - paddingRight;
11    final int childHeight = height - paddingTop - paddingBottom;
12
13    final View child = getChildAt(0);
14    if (child != null) {
15        final int childLeft = paddingLeft;
16        final int childTop = paddingTop;
17        child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight());
18    }
19}

4. 滚动处理

NestedScrollView 重写了 onTouchEvent 方法来处理触摸事件,并实现滚动功能。它还实现了 NestedScrollingParentNestedScrollingChild 接口中的方法。

1@Override
2public boolean onTouchEvent(MotionEvent ev) {
3    // ...
4    switch (ev.getActionMasked()) {
5        case MotionEvent.ACTION_DOWN:
6            // ...
7            break;
8        case MotionEvent.ACTION_MOVE:
9            // ...
10            break;
11        case MotionEvent.ACTION_UP:
12            // ...
13            break;
14        case MotionEvent.ACTION_CANCEL:
15            // ...
16            break;
17    }
18    return true;
19}

5. 嵌套滚动接口实现

NestedScrollView 实现了 NestedScrollingParentNestedScrollingChild 接口,以便支持嵌套滚动。

1@Override
2public boolean startNestedScroll(int axes) {
3    // ...
4    return true;
5}
6
7@Override
8public void stopNestedScroll() {
9    // ...
10}
11
12@Override
13public boolean hasNestedScrollingParent() {
14    // ...
15    return false;
16}
17
18@Override
19public void dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
20    // ...
21}
22
23@Override
24public void dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
25    // ...
26}
27
28@Override
29public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
30    // ...
31    return false;
32}
33
34@Override
35public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
36    // ...
37    return false;
38}

6. 回调方法

NestedScrollView 提供了 OnScrollChangeListener 接口,允许开发者监听滚动事件。

1public interface OnScrollChangeListener {
2    void onScrollChange(NestedScrollView who, int scrollX, int scrollY, int oldScrollX, int oldScrollY);
3}

7. 平滑滚动

NestedScrollView 使用 AutoScrollHelper 类来实现平滑滚动动画。

1public void smoothScrollTo(int destX, int destY) {
2    AutoScrollHelper.getInstance(this).ensureTarget(this);
3    AutoScrollHelper.getInstance(this).startAnimation(destX, destY);
4}

总结

NestedScrollView 的实现原理与 ScrollView 类似,但是它增加了对嵌套滚动的支持。这使得它可以更好地与其他支持嵌套滚动的视图协同工作。通过重写 onMeasureonLayout 方法,NestedScrollView 确保子视图正确地显示在屏幕上,并且可以通过处理触摸事件来响应用户的滚动操作。此外,它还实现了 NestedScrollingParentNestedScrollingChild 接口,以支持嵌套滚动场景。

  • 16
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值