浅谈View的滑动

要理解 View 的滑动,我们可以从「底层原理」和「生活类比」两个维度展开。先看透本质,再用故事串起来,保证大家都能秒懂。

一、底层原理:滑动的本质是什么?

View 的滑动,本质是改变 View 在屏幕上的「绘制位置」。而位置的计算,依赖于 Android 的「坐标系」和「View 的位置参数」。

1. 坐标系:屏幕上的 "地图"

Android 规定了一套坐标系:

  • 原点(0,0)在屏幕左上角;

  • 向右为 x 轴正方向,向下为 y 轴正方向(和数学坐标系的 y 轴相反,记住 "下为正" 即可)。

每个 View 在屏幕上的位置,都用 4 个核心参数描述:

  • left:View 左边缘到父容器左边缘的 x 距离;

  • top:View 上边缘到父容器上边缘的 y 距离;

  • right:View 右边缘到父容器左边缘的 x 距离(= left + 宽度);

  • bottom:View 下边缘到父容器上边缘的 y 距离(= top + 高度)。

这 4 个参数就像 View 的 "坐标标签",决定了它在父容器中的位置。

2. 滑动的底层逻辑:改参数 = 改位置

要让 View 滑动,本质是修改这些位置参数,或者通过「偏移绘制」让 View "看起来动了"。具体有 3 种核心方式,对应不同的参数修改逻辑:

滑动方式 底层修改的参数 效果
scrollTo/scrollBy 父容器的mScrollX/mScrollY 父容器 "滚动",内部子 View 的显示位置偏移(子 View 自身位置不变)
LayoutParams View 的布局参数(如marginxy 父容器重新布局,View 的left/top等参数改变(View 自身位置真的动了)
offsetLeftAndRight/offsetTopAndBottom View 的left/right/top/bottom 直接修改 View 的位置参数,强制刷新显示(View 自身位置真的动了)

3. 触摸事件:滑动的 "动力源"

用户的滑动操作(手指在屏幕上拖动),会被包装成「MotionEvent」对象(包含触摸点的 x/y 坐标)。这个事件会从 Activity 传递到 ViewGroup,再到具体的 View(类似 "快递派送")。

当 View 收到ACTION_MOVE事件(手指滑动中)时,就可以根据「起始位置」和「当前位置」的差值,计算出需要滑动的距离(dx、dy),然后用上面 3 种方式中的一种执行滑动。

二、生活类比:用 "房间 + 家具" 讲透滑动

把手机屏幕比作一个「房间」,View 是房间里的「家具」(比如桌子、椅子),滑动就是 "改变家具的位置" 或 "改变看家具的视角"。

1. 坐标系:房间的 "定位规则"

房间的左上角墙角是「原点(0,0)」:

  • 沿着墙向右(x 轴),每走 1 厘米记为 x=1;

  • 沿着墙向下(y 轴),每走 1 厘米记为 y=1。

每个家具(View)的位置用 4 个参数标记:

  • left:家具左边缘到房间左墙的距离;

  • top:家具上边缘到房间上墙的距离;

  • right:家具右边缘到房间左墙的距离(= left + 家具宽度);

  • bottom:家具下边缘到房间上墙的距离(= top + 家具高度)。

比如一张桌子左边缘离左墙 30cm,上边缘离上墙 40cm,宽 50cm,高 60cm,那它的参数就是:left=30,top=40,right=80(30+50),bottom=100(40+60)。

2. 触摸事件:"推动家具的手"

用户的手指滑动,就像 "用手推家具":

  • 手指刚碰到家具(ACTION_DOWN):记录初始位置(比如手指在桌子上的点,距离房间左墙 x1=50,上墙 y1=60);

  • 手指拖动(ACTION_MOVE):实时记录当前位置(x2=70,y2=80),计算出滑动距离 dx=20(70-50),dy=20(80-60);

  • 手指离开(ACTION_UP):结束滑动。

这个 "dx、dy" 就是家具需要移动的距离,接下来就是 "怎么移" 的问题 ------ 对应 3 种滑动方式。

3. 三种滑动方式:怎么移动家具?

我们用 "移动桌子" 的场景,对应三种底层实现:

方式 1:scrollTo/scrollBy("人不动,移动视线")

底层逻辑 :父容器(比如房间地面)记录一个 "滚动偏移量"(mScrollX/mScrollY),绘制子 View 时,会根据这个偏移量 "偏移绘制位置"。子 View 的left/top没变,只是看起来动了。

生活类比

你站在房间中间看桌子(初始位置),现在你不移动桌子,也不自己动,而是让 "视线平移"(比如戴了一副可以左右滑动的眼镜)。此时桌子在地面上的实际位置(left/top)没变,但你看到的桌子位置 "偏移" 了。

举例

手机里的ListView滑动时,其实是ListView(父容器)通过scrollBy(dx, dy)改变mScrollY,每个 item(子 View)的left/top没变,但绘制时会随父容器的偏移而 "向上 / 向下移动",看起来就是列表在滚。

方式 2:LayoutParams("移动桌子的地脚")

底层逻辑 :修改 View 的布局参数(比如marginLeftwidth),父容器会触发requestLayout()(重新测量、布局、绘制),最终改变 View 的left/top,实现位置变化。

生活类比

桌子的的脚是可以调节的(类似LayoutParams)。你想把桌子向右移 10cm,就把左侧的脚移动 10cm(增加marginLeft)。此时房间会重新计算桌子的位置(left = 原来的 left + 10),桌子的实际位置真的变了。

举例

拖动一个按钮时,我们可以通过LayoutParams修改它的leftMargintopMargin,按钮会真的在父容器中 "平移",left/top参数也会同步更新。

方式 3:offsetLeftAndRight("直接推桌子")

底层逻辑 :直接修改 View 的left/right(水平方向)或top/bottom(垂直方向),然后调用invalidate()刷新绘制。View 的位置参数直接改变,无需父容器重新布局。

生活类比

你直接用手推桌子,桌子在地面上的实际位置(left/top)被你硬生生推远了 10cm。此时桌子的left从 30 变成 40,right从 80 变成 90,位置参数直接变了,房间不需要重新规划布局,桌子自己动了。

举例

自定义一个可拖动的 View 时,在ACTION_MOVE中调用offsetLeftAndRight(dx),可以直接让 View 随手指移动,效率很高(无需重新测量布局)。

三、总结:三种方式的核心区别

方式 本质 适用场景 特点
scrollTo/scrollBy 父容器偏移绘制,子 View 位置不变 滚动列表(ListView/RecyclerView)、滑动面板 效率高,适合大量子 View 滑动
LayoutParams 改布局参数,父容器重新布局 拖动 View(如按钮、悬浮窗) 位置真的改变,适合单个 View 移动
offset 系列 直接改位置参数,强制刷新 快速拖动单个 View 效率最高,适合高频滑动(如手势拖动)

最后:一句话记住

View 滑动的本质是 "改变绘制位置",要么通过父容器的偏移让它 "看起来动了"(scroll),要么通过修改自身参数让它 "真的动了"(LayoutParams/offset)。就像移动家具,要么你戴个偏移眼镜看它(scroll),要么直接推它(offset),要么移动桌子的脚让它换位置(LayoutParams)------ 核心都是 "位置变了"。

相关推荐
爬虫程序猿2 小时前
利用爬虫按关键字搜索淘宝商品实战指南
android·爬虫
顾北川_野2 小时前
Android ttyS2无法打开该如何配置 + ttyS0和ttyS1可以
android·fpga开发
wzj_what_why_how5 小时前
Android网络层架构:统一错误处理的问题分析到解决方案与设计实现
android·架构
千里马学框架5 小时前
User手机上如何抓取界面的布局uiautomatorviewer
android·智能手机·aosp·uiautomator·布局抓取·user版本
阿巴~阿巴~6 小时前
操作系统核心技术剖析:从Android驱动模型到鸿蒙微内核的国产化实践
android·华为·harmonyos
hsx6667 小时前
使用 MaterialShapeDrawable 自定义各种形状的 View
android
用户2018792831677 小时前
滑动城堡的奇妙管家 ——ViewPager故事
android
用户2018792831677 小时前
📜 童话:魔法卷轴与 ScrollView 的奥秘
android
??? Meggie9 小时前
【SQL】使用UPDATE修改表字段的时候,遇到1054 或者1064的问题怎么办?
android·数据库·sql
用户2018792831679 小时前
代码共享法宝之maven-publish
android