首页 > 其他分享 >ScrollView实现原理分析

ScrollView实现原理分析

时间:2024-07-25 23:25:26浏览次数:15  
标签:分析 ... 滚动 int ScrollView 视图 原理 final

ScrollView 是 Android 中用于实现单向滚动功能的布局容器。它只能容纳一个子视图,并且能够使这个子视图在垂直方向(默认)或水平方向上滚动。下面我们将结合源码来分析 ScrollView 的实现原理。

1. ScrollView 类定义

ScrollView 继承自 FrameLayout,这意味着它本身是一个布局容器,可以包含一个子视图,并且提供了滚动功能。

1public class ScrollView extends FrameLayout implements NestedScrollingParent {
2
3    private static final String TAG = "ScrollView";
4
5    // 滚动状态
6    private static final int SCROLL_STATE_IDLE = 0;
7    private static final int SCROLL_STATE_DRAGGING = 1;
8    private static final int SCROLL_STATE_SETTLING = 2;
9
10    private static final int MAX_SCROLL_DURATION = 250;  // 最大滚动持续时间
11
12    // 内部状态
13    private int mScrollState = SCROLL_STATE_IDLE;
14
15    // 滚动监听器
16    private OnScrollChangeListener mOnScrollChangeListener;
17
18    // 构造函数
19    public ScrollView(Context context) {
20        super(context);
21        initScrollView();
22    }
23
24    public ScrollView(Context context, AttributeSet attrs) {
25        super(context, attrs);
26        initScrollView();
27    }
28
29    public ScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
30        super(context, attrs, defStyleAttr);
31        initScrollView();
32    }
33
34    private void initScrollView() {
35        // 初始化操作
36        setFillViewport(true);  // 填充整个视口
37        setFocusable(true);     // 可聚焦
38        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);  // 子视图优先获得焦点
39    }
40
41    // 设置滚动监听器
42    public void setOnScrollChangeListener(OnScrollChangeListener l) {
43        mOnScrollChangeListener = l;
44    }
45
46    // 获取当前滚动位置
47    public int getScrollY() {
48        return getScrollY();
49    }
50
51    // 滚动到指定位置
52    public void scrollTo(int x, int y) {
53        super.scrollTo(x, y);
54    }
55
56    // 平滑滚动到指定位置
57    public void smoothScrollTo(int destX, int destY) {
58        AutoScrollHelper.getInstance(this).ensureTarget(this);
59        AutoScrollHelper.getInstance(this).startAnimation(destX, destY);
60    }
61
62    // 其他方法...
63}

2. 测量过程(onMeasure)

ScrollViewonMeasure 方法负责测量其子视图,并根据子视图的大小和父容器的约束来确定 ScrollView 自身的大小。

1@Override
2protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
3    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4
5    int childWidthSize = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
6    int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
7
8    // 创建子视图的测量规格
9    int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
10    int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.UNSPECIFIED);
11
12    // 测量子视图
13    View child = getChildAt(0);
14    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
15
16    // 设置测量结果
17    setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
18}

3. 布局过程(onLayout)

ScrollViewonLayout 方法负责根据子视图的大小和滚动位置来确定子视图的位置。

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. 滚动处理

ScrollView 通过重写 onTouchEvent 方法来处理触摸事件,并实现滚动功能。

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. 滚动逻辑

滚动逻辑主要在 computeScrollDeltaToGetChildRectOnScreen 方法中实现,该方法计算滚动距离以确保指定的矩形可见。

1int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
2    // ...
3    // 计算需要滚动的距离
4    int scrollDelta;
5    if (rect.top < 0) {
6        scrollDelta = -rect.top;
7    } else if (rect.bottom > getHeight()) {
8        scrollDelta = getHeight() - rect.bottom;
9    } else {
10        scrollDelta = 0;
11    }
12    // 返回滚动距离
13    return scrollDelta;
14}

6. 平滑滚动

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

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

7. 回调方法

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

1public interface OnScrollChangeListener {
2    void onScrollChange(ScrollView who, int x, int y, int oldx, int oldy);
3}

8. NestedScrollingParent 支持

ScrollView 实现了 NestedScrollingParent 接口,支持嵌套滚动功能。

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}

总结

ScrollView 的实现原理涉及测量和布局其内部的子视图,以及处理触摸事件来实现滚动功能。通过重写 onMeasureonLayout 方法,ScrollView 确保子视图正确地显示在屏幕上。同时,它还通过处理触摸事件来响应用户的滚动操作,并提供了平滑滚动和滚动监听的支持。此外,ScrollView 还实现了 NestedScrollingParent 接口,以支持嵌套滚动场景。

标签:分析,...,滚动,int,ScrollView,视图,原理,final
From: https://blog.csdn.net/VitorLiu/article/details/140702257

相关文章

  • ViewPager2实现原理分析
    ViewPager2 是Android开发中用于实现水平滑动视图的组件,它是 ViewPager 的一个改进版,提供了更多的功能和更好的性能。下面,我们将结合源码来简要分析 ViewPager2 的实现原理。1.基本架构ViewPager2 的主要架构基于 RecyclerView,它利用了 RecyclerView 的滚动、布......
  • 粗糙表面分析处理和三维表面形貌处理参数提取分析
    粗糙表面生成,三维形貌生成,三维表面生成,三维形貌仿真,表面形貌处理,表面形貌参数提取。粗糙表面分析处理,表面形貌处理:各种参数(高度参数,函数参数,空间参数,混合参数,体积参数,分层表面参数,特征参数等等表面参数计算提取(自相关长度,均值,均方根偏差,偏度峰度,峰谷,谷深,五点峰高,空度系数,均......
  • go和Spex编译分析
    为什么会报错某个GitHub文件被排除了呢 可能这个文件本身有+buildignore字段 go.mod类似于idea里的pom文件用于管理依赖dep   Gopkg.lock文件 为什么清楚依赖缓存会有用 引入代码版本v不是必须的,而是根据项目中有没有加v 私有受保护仓库......
  • 智能音箱的工作原理
    智能音箱的工作原理主要涉及到硬件和软件两个层面的协同工作,以及多个关键技术环节的配合。以下是对智能音箱工作原理的详细解析:一、硬件层面智能音箱的硬件组成通常包括主控芯片、麦克风阵列、扬声器、Wi-Fi模块和电源等部分。主控芯片:作为智能音箱的“大脑”,负责控制整个......
  • java的跨平台原理
    java的跨平台原理:Java跨平台的原理主要是通过Java虚拟机(JVM)来实现的。为啥需要跨平台:不同平台的机器码是不兼容的。在编译原理中,我们知道编译器将源代码翻译成特定平台的机器码,这样程序就可以在特定平台上运行。然而,不同平台的机器码是不兼容的,这就导致了跨平台的困难。......
  • Linux工作原理8深入了解进程和资源利用率
    8深入了解进程和资源利用率本章将带你深入了解进程、内核和系统资源之间的关系。有三种基本的硬件资源:CPU、内存和I/O。进程会争夺这些资源,而内核的工作就是公平地分配资源。内核本身也是一种资源--进程用来执行创建新进程和与其他进程通信等任务的软件资源。本章中的许多工......
  • 过采样SMOTE逻辑回归、SVM、随机森林、AdaBoost和XGBoost对不平衡数据分析预测
    全文链接:https://tecdat.cn/?p=37115原文出处:拓端数据部落公众号分析师:YimengLi近几年,伴随着互联网的发展,在线食品配送业务成为了新潮流。在此背景下,我们帮助客户对“在线食品交付偏好-班加罗尔地区”数据开展研究,建立印度在线食品配送平台消费者的用户画像,研究影响顾客购买意......
  • 计算机组成原理
    计算机系统概述计算机系统=硬件+软件硬件的发展:1.电子管时代 2.晶体管时代 3.中小规模集成电路 4.大规模、超大规模集成电路计算机硬件的基本组成1.早期冯诺依曼结构冯诺依曼计算机的特点:①计算机由五大部件组成②指令和数据以同等地位存于存储器,可按地址寻访③指令和数据......
  • 一个典型的性能分析案例
    知识星球一位同学问了这样一个性能问题:需求场景:车端上传触发类信号数据到TOS桶,持续1分钟会产生1G左右的包,但不是每分钟都会传。然后云服务Kafka监听到数据产生,处理这些数据。测试方式:等比例准备20T数据,按单位时间的量,从桶间复制到监听的桶来模拟并发。问题一:车多了以后同时上......
  • 关于在循环体中使用lambda表达式拿到错误结果的问题分析
    前言:大家好,我是代码小白Susume,这是我在cnblogs的第一篇博客。 背景:今天在工作的时候突然发现公司的代码有一个地方好像可以优化一下。然后火树就是啪啪一顿改,自信满满的改完后直接开始测试。 问题:那么首先放代码(ps:已经做了去敏感信息的处理)键盘啪啪一顿敲是敲爽了,但是......