尧图精选

Android ScrollView从入门到实战:原理、用法、嵌套冲突与性能优化

🕒 发布时间:2026/10/1 4:43:49 📁 来源:尧图网络
做Android开发的人不管是自学还是科班出身基本都逃不过ScrollView这一关。它太常用了凡是内容超出屏幕的情况你第一个想到的往往就是它。但正因为它“基础”反而有很多细节被忽略了等到实际项目里出了性能问题、嵌套滑动失灵、和RecyclerView打架的时候才回头补课。这篇就彻底讲透ScrollView从一个入门视角出发把原理、用法、坑点、经验一次理清看完你就能在项目里放心地用它了。先交代一下背景这篇内容适合刚学完四大布局、准备开始写实际界面的初学者也适合那些用ScrollView写过几个页面、但遇到问题只能靠猜的初级开发者。我会先讲ScrollView的本质和设计思路再讲实操写法然后用一个仿电商首页的例子把真实开发场景串起来最后集中梳理常见问题。全程没有需要额外付费的工具就一个Android Studio你跟着一步步来就行。1. ScrollView的核心概念与使用场景1.1 它到底是什么一个可以滚动的容器ScrollView的本质是一个FrameLayout只是在FrameLayout的基础上增加了滚动能力。你甚至可以把它理解成“加了轮子的FrameLayout”。正因为继承自FrameLayout它有一个非常关键的约束只能包含一个直接子View。如果你想放多个控件进去必须先用一个LinearLayout或RelativeLayout把它们包起来再放进ScrollView。很多初学者在这里会犯迷糊。以我现在手里的一个登录页为例我需要放Logo、两个输入框、一个按钮、一个“忘记密码”链接一共四个子元素。如果直接往ScrollView里塞四个View编译期不报错运行就崩Logcat会抛java.lang.IllegalStateException: ScrollView can host only one direct child。所以正确的结构必须是这样ScrollView LinearLayout ImageView / !-- Logo -- EditText / !-- 账号 -- EditText / !-- 密码 -- Button / !-- 登录 -- TextView / !-- 忘记密码 -- /LinearLayout /ScrollView这算不算设计缺陷其实不算。FrameLayout的设计哲学就是“一个孩子放中间或者指定位置”ScrollView在其之上加滚动逻辑约束单一子View是为了明确滚动的边界和测量规则。你要是真需要多个子View官方也给了解决方案——用LinearLayout包一层这本来就是Android推荐的组合方式。1.2 竖向滚动与横向滚动的选择ScrollView默认是竖向滚动的处理的是高度超出屏幕的问题。与之对应的是HorizontalScrollView处理的是宽度超出屏幕的问题。这两个类的关系很微妙HorizontalScrollView的直接父类是FrameLayout而ScrollView的直接父类也是FrameLayout它们俩并不存在继承关系是两个平行的滚动容器。这就有个很实际的问题怎么实现“上下能滚、左右也能滚”用嵌套的ScrollView加HorizontalScrollView实测下来很容易出问题手势冲突会非常明显逻辑也会变得难以维护。更推荐的方式是用RecyclerView配合不同的LayoutManager或者直接使用支持双向滚动的自定义容器。但如果是简单的“一页内容上下看不完”ScrollView仍然是最优先的选择。它实现简单、内存占用低、行为可预期远比为了一个静态页面引入整个RecyclerView划算。我在项目里判断的标准很简单内容量固定、不需要复用、不需要动态增删就用ScrollView内容量大、需要复用Item、需要局部刷新就用RecyclerView。1.3 和ListView/RecyclerView的本质区别很多人会问既然都能滚动为什么还要分ScrollView和RecyclerView这个问题问到点子上了它们的性能模型完全不同。ScrollView是一次性把所有的子View都测量、布局、绘制出来也就是说它无论看不看得见都会把内容渲染出来。如果内容里放了一个很长的列表或者放了一大堆图片内存和渲染压力都会很大。这也是为什么我从来不建议在ScrollView里嵌套一个ListView或者一个没有限制高度的RecyclerView——那相当于在ScrollView里又塞了一个完整的滚动系统双重重叠的滚动逻辑会导致滑动冲突而且RecyclerView为了测量高度还会经历两次完整的布局过程性能损耗极其严重。RecyclerView则采用了View回收复用机制屏幕上能看到的Item才去创建和绑定滑出去的Item会被回收复用给滑进来的新Item。这套机制让它能承载成千上万条数据。所以定位不同ScrollView适合内容有限的静态页面RecyclerView适合数据量大的动态列表。各司其职不要跨界。2. 基础用法与实操步骤2.1 从XML布局开始的完整示例不用花里胡哨的案例我从实际项目里截取一个商品详情页的头部信息区块来演示。场景是这样的用户从列表点击一个商品进入详情页页面底部是固定的购买栏中间区域需要展示图片、标题、价格、规格参数、图文详情等一大段内容整体高度远远超过屏幕必须滚动。布局伪代码如下?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical !-- 顶部标题栏 -- androidx.appcompat.widget.Toolbar android:idid/toolbar android:layout_widthmatch_parent android:layout_height?attr/actionBarSize / !-- 中间可滚动区域 -- ScrollView android:idid/scrollView android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 android:fillViewporttrue android:scrollbarsvertical LinearLayout android:idid/scroll_content android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationvertical !-- 商品轮播图 -- androidx.viewpager2.widget.ViewPager2 android:idid/viewPager android:layout_widthmatch_parent android:layout_height260dp / !-- 价格与标题 -- TextView android:idid/tv_price android:layout_widthwrap_content android:layout_heightwrap_content android:text¥1999 android:textColor#FF4444 android:textSize24sp / TextView android:idid/tv_title android:layout_widthmatch_parent android:layout_heightwrap_content android:text商品标题在这里 android:textSize16sp android:maxLines2 android:ellipsizeend / !-- 分割线其余内容省略 -- View android:layout_widthmatch_parent android:layout_height1dp android:background#EEEEEE / /LinearLayout /ScrollView !-- 底部购买栏 -- LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationhorizontal !-- 按钮省略 -- /LinearLayout /LinearLayout这里面有几个关键点敲代码的时候要特别留心第一ScrollView的高度不要写wrap_content在详情页这种场景里应该用0dp加layout_weight1去占据剩余空间。这一步的目的是让ScrollView的边界和底部购买栏明确分离滚动只在中间区域发生。第二android:fillViewporttrue这个属性值得多说一句。它的作用是让ScrollView的内容高度不足一屏时子布局自动撑满整个ScrollView。举个例子你写了android:layout_heightwrap_content的LinearLayout内容只有200dp高屏幕却有800dp默认情况下这个LinearLayout就真的只有200dp高底部会留一大块空白。设置fillViewport为true之后LinearLayout会被拉长到和ScrollView一样高背景色就能铺满整屏。这个细节在实现空页面、加载失败页时很管用。2.2 在Java/Kotlin里动态添加内容时的写法有时候界面的内容是动态加载的比如从服务器拉取一组标签动态添加到内容区域。这时候很多人的第一反应是直接在Java代码里new TextView()然后addView()。这个方向没错但有细节要注意。ScrollView里的直接子View是LinearLayout你要往这个LinearLayout里动态添加View。添加完之后必须调用invalidate()请求重绘否则界面可能不刷新。用Kotlin写的话大致是这样val container findViewByIdLinearLayout(R.id.scroll_content) val newView TextView(this) newView.text 动态标签${position} newView.setTextColor(ContextCompat.getColor(this, R.color.text_main)) newView.textSize 14f val lp LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT ) lp.setMargins(0, dp2px(8f), 0, 0) container.addView(newView, lp) container.invalidate()如果数据量非常大比如一次需要添加好几百个TextView这里就暴露出ScrollView的短板了。所有View都会一次性被创建并加入视图树内存和布局时间都会明显上升。出现这种情况就应该评估改用RecyclerView了。但如果是几十个以内的标签ScrollView完全应付得来。另外如果是异步加载完数据再往ScrollView里添加View还要注意线程切换的问题不要在子线程直接操作UI要用runOnUiThread或者协程切回主线程。2.3 fillViewport属性实战拆解fillViewport是ScrollView里我最想让初学者搞懂的一个属性因为它的行为反直觉。正常情况下ScrollView的高度是固定的内容比它高就滚动内容比它矮就只是顶部对齐。但加了android:fillViewporttrue当内容比ScrollView矮的时候ScrollView会强制要求子View填满整个可用高度。听起来好像是“把子View的height改成match_parent”其实不是。它的实现原理是在onMeasure阶段如果子View的测量高度小于ScrollView的可用高度ScrollView会把子View重新测量一次并把测量高度设置为ScrollView的可用高度。这有什么用最常见的一个场景是实现“空数据页面垂直居中”。假如你有一个空购物车页面中间要显示“购物车还是空的”加一个“去逛逛”按钮你希望它整体居中而不是贴顶。如果直接在ScrollView里放一个wrap_content的LinearLayout默认是贴顶的。加上fillViewport之后LinearLayout会占满整个ScrollView再配合android:gravitycenter子元素就自然居中了。ScrollView android:layout_widthmatch_parent android:layout_heightmatch_parent android:fillViewporttrue LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:gravitycenter android:orientationvertical ImageView ... / TextView ... / Button ... / /LinearLayout /ScrollView这个写法比用RelativeLayout或者计算屏幕高度去动态设置margin要干净得多。在我实测过的多个项目里它都是处理空页面最优雅的解法。3. 嵌套滚动与协调者布局的高级玩法3.1 ScrollView与Toolbar的滚动联动入门阶段你也许用不上太复杂的效果但一个实际项目里很少有人只是干巴巴地放一个ScrollView就完事。最常见的是“标题栏渐变”和“滚动到顶部隐藏/显示某些元素”。这些效果的实现都依赖于监听ScrollView的滚动位置。这里要用到setOnScrollChangeListener注意这是Android 6.0API 23之后新增的API。如果你的项目minSdk低于23需要自己写OnTouchListener或者继承ScrollView重写onScrollChanged。简单场景监听Toolbar背景透明度变化scrollView.setOnScrollChangeListener { _, scrollY, _, _, _ - val toolbar findViewByIdToolbar(R.id.toolbar) when { scrollY 0 - toolbar.alpha 0f scrollY 200 - toolbar.alpha scrollY / 200f else - toolbar.alpha 1f } }这里我设置了一个200dp的阈值意味着用户向下滚超过200dpToolbar就从透明逐渐变成完全不透明。这个数值不是固定的取决于你的布局高度和Title区域大小。实际操作中一般会根据实际视觉效果去调整没有绝对标准。3.2 NestedScrollView与RecyclerView的嵌套纯ScrollView嵌套RecyclerView是性能灾难但如果是NestedScrollView嵌套RecyclerView情况就不一样了。NestedScrollView是Android Support Library里提供的增强版ScrollView它实现了嵌套滑动机制Nested Scrolling能够与子View协同处理滑动事件。最常见的场景是“外层整体滚动内部一块区域是商品列表”。比如一个活动页面顶部是banner中间是一段活动规则说明下面是一个商品列表希望整个页面统一滚动而不是内部列表自己滚。做法是把RecyclerView放在NestedScrollView里并且强制关闭RecyclerView自己的滚动recyclerView.isNestedScrollingEnabled false这样RecyclerView不会拦截触摸事件而是把滑动事件上抛给NestedScrollView整个页面就表现为一个完整的滚动体。RecyclerView的所有Item也都能正常显示因为此时RecyclerView的高度会按照内容完全展开等价于一个LinearLayout。但这里必须敲响警钟如果列表数据量很大这个方式会让所有Item一次性全部加载内存暴涨不推荐。我自己实测过一个包含500个Item的RecyclerView嵌套进NestedScrollView后初始化时间会从几十毫秒上涨到几百毫秒内存占用也会明显增加。只有当你确认列表只有一二十个Item的时候才适合这样处理。数据量大的场景正确做法有两个一是直接放弃外层滚动让页面只有RecyclerView自己滚动Toolbar用CoordinatorLayout的Behavior去联动。这也是原生推荐的方式性能最好。二是使用CoordinatorLayout加AppBarLayout加CollapsingToolbarLayout构建一个标准的“可折叠标题栏加滚动内容”结构。这也是现在绝大多数的首页和商城详情页采用的方案。下面给一个NestedScrollView配合RecyclerView的典型用法注意关键是isNestedScrollingEnabledfalseandroidx.core.widget.NestedScrollView android:layout_widthmatch_parent android:layout_heightmatch_parent LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationvertical TextView android:layout_widthmatch_parent android:layout_heightwrap_content android:text活动规则说明 android:padding16dp / androidx.recyclerview.widget.RecyclerView android:idid/recyclerView android:layout_widthmatch_parent android:layout_heightwrap_content android:nestedScrollingEnabledfalse / /LinearLayout /androidx.core.widget.NestedScrollView用NestedScrollView而不用ScrollView的原因在于NestedScrollView本身实现了NestedScrollingParent和NestedScrollingChild接口它对外层父布局的嵌套滑动处理得更好配合CoordinatorLayout使用时表现也稳定。所以做嵌套场景我一般直接选NestedScrollView。3.3 与ViewPager2同时滑动的冲突处理ScrollView里面放ViewPager2也是常见需求。比如商品详情页里ScrollView滚动到某个区域区域里是一个横向的图片轮播。这种情况下垂直滑动手势和水平滑动手势各自独立看起来没什么好冲突的但实际运行起来还是会遇到边界问题图片轮播左右滑的时候很容易把ScrollView的上下滑动也给触发。ViewPager2本身解决了大部分事件冲突它用的是RecyclerView做底层容器对父容器的事件分发处理得很完善。我实测下来ScrollView里直接放ViewPager2正常的垂直滑动和横向滑动基本都能识别。但有一个特例当ScrollView滚动到顶部或底部此时用户恰好有一个斜向45度左右的滑动事件分发的边界会变得模糊。这时候ViewPager2可能会把事件拦截掉导致页面无法继续向上滚动。这个问题的解决方案之一是重写ScrollView的onInterceptTouchEvent在水平移动距离大于垂直移动距离时不拦截事件class CustomScrollView JvmOverloads constructor( context: Context, attrs: AttributeSet? null ) : ScrollView(context, attrs) { private var lastX 0f private var lastY 0f override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { if (ev null) return super.onInterceptTouchEvent(ev) when (ev.action) { MotionEvent.ACTION_DOWN - { lastX ev.x lastY ev.y return super.onInterceptTouchEvent(ev) } MotionEvent.ACTION_MOVE - { val dx ev.x - lastX val dy ev.y - lastY if (abs(dx) abs(dy)) { return false // 水平滑动交给ViewPager2处理 } } } return super.onInterceptTouchEvent(ev) } }注意onInterceptTouchEvent返回false不是“不处理事件”而是“不拦截事件”事件会继续传递给子View。这个技巧在ScrollView和横向滑动控件嵌套时很通用不只是ViewPager2换成HorizontalScrollView、横向SeekBar也适用。4. 常见问题与性能优化实战4.1 ScrollView里的RecyclerView高度显示不全这个问题的表现是RecyclerView嵌在ScrollView里只显示了第一两个Item剩下的完全看不到也没有滚动。原因在于ScrollView测量子View高度时RecyclerView返回的测量高度是0或者只测量了一部分。老一点的解决方案是重写RecyclerView的onMeasure方法让它测量所有Item的总高度class NestedRecyclerView JvmOverloads constructor( context: Context, attrs: AttributeSet? null ) : RecyclerView(context, attrs) { override fun onMeasure(widthSpec: Int, heightSpec: Int) { val expandSpec MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE shr 2, MeasureSpec.AT_MOST ) super.onMeasure(widthSpec, expandSpec) } }这个方法能让RecyclerView在ScrollView里完整展开高度。但我不建议一上来就套这个方案因为它相当于把RecyclerView的回收机制废掉了性能损失很大。更好的方式是在布局阶段就控制数据量或者在逻辑上把RecyclerView的Item数量限制在可接受范围内。如果你的项目已经用了NestedScrollView那直接把isNestedScrollingEnabled设为false就够用了不需要重写onMeasure。这个属性在布局和代码里都能设。4.2 ScrollView嵌套ScrollView的滑动冲突ScrollView里面再放一个ScrollView这种结构在正规系统里几乎不会出现但总有人用在这里外层是订单信息里面某个条目需要展示一段很长的协议文本开发者图省事直接又套了一个ScrollView。嵌套ScrollView的默认表现是内层的ScrollView优先消费滑动事件当内层滚到底或者滚到顶的时候外层才开始响应。这看起来“能滚”但体验非常割裂因为用户永远不知道当前动的是哪一层。要处理这个问题先想清楚业务需求如果内层内容不需要独立滚动直接把内层ScrollView去掉换成一个wrap_content的TextView就好。这是最干净的做法。如果内层内容确实需要独立滚动那就不能简单嵌套需要使用NestedScrollView配合合适的android:overScrollMode等属性来协同。关于这一点我的个人经验是能去嵌套就去嵌套。大多数情况下你看似需要内层滚动实际只是忘了把外层的高度约束对。调整布局结构之后问题自然消失。4.3 ScrollView的滑动卡顿与性能优化ScrollView滑动卡顿绝大多数原因是内容太复杂。一个常见的错误做法是在ScrollView里放了N张大图每个ImageView的宽高都是match_parent高度是wrap_content。图片没做压缩直接加载原图内存吃紧滑动时每一帧都要重新合成图像不卡才怪。优化的优先级如下使用图片加载库如Glide或Coil并明确指定override尺寸不要让系统加载原图到内存。压缩布局层级避免ScrollView里嵌套多层LinearLayout多用ConstraintLayout。避免在滚动过程里频繁调用findViewById和创建对象结合onScrollChanged还想做动画时尽量用属性动画并注意动画对象的回收。如果页面内容生命周期里基本不变化可以尝试把ScrollView的滚动缓存打开android:scrollbarFadeDuration和smoothScrollTo配合能提升一些主观的流畅感。还有一个很少人知道但很实用的小技巧当ScrollView的内容包含大量文本时给文本设置android:textColor和android:textSize的时候注意不要使用sp之外的非标准单位否则在字体缩放模式下重绘压力会加倍滑动帧率会有肉眼可见的下降。4.4 与软键盘弹出时的调整ScrollView在表单页面极其常见但有个细节容易踩坑软键盘弹出来把输入框挡住了甚至整个ScrollView被顶得乱七八糟。出现这种问题的原因是windowSoftInputMode设置不对。正确的设置是在AndroidManifest.xml的Activity节点上配置activity android:name.LoginActivity android:windowSoftInputModeadjustResize /adjustResize会让Activity的可用高度在软键盘弹出时缩小ScrollView的高度也会跟着缩小输入框被顶上去用户能看到自己正在输入的内容。如果你用的是adjustPan整个页面的根布局会被平移经常把标题栏顶出屏幕体验很糟糕。如果你的根布局是CoordinatorLayout或者使用EdgeToEdge全面屏模式windowSoftInputMode的某些行为会被改变这时候需要配合WindowCompat.setDecorFitsSystemWindows(window, false)和OnApplyWindowInsetsListener做手动适配。这块属于进阶内容新手先掌握adjustResize就够用。4.5 边缘效果与滚动条的自定义ScrollView默认在某些Android版本上会有边缘光晕效果fading edge底部会有蓝色或彩色的过度滚动阴影。有的设计师会强烈要求去掉这种“安卓味很重”的视觉效果。去掉方法有两个在ScrollView上设置android:overScrollModenever。在代码里调用scrollView.overScrollMode View.OVER_SCROLL_NEVER。滚动条默认是位于右侧的细长条不操作时自动淡出。如果你觉得它丑可以隐藏android:scrollbarsnone隐藏滚动条不影响触摸滚动手势只是视觉上没有了。很多页面在ScrollView外层还会包一个圆角背景的容器滚动条会露出圆角边缘这时候隐藏滚动条配合圆角背景效果会协调很多。我自己的建议是除非你是做聊天记录那种需要明确位置提示的场景否则现代应用的滚动条基本都是隐藏的。5. 避坑指南与开发经验谈5.1 滑动到底部自动加载更多ScrollView有一个高频需求滚动到底部时触发加载更多。虽然现在的项目更倾向于用RecyclerView实现列表加载更多但如果你的页面结构就是ScrollView承载依然可以做到。核心是监听滚动变化判断是否滚动到了底部scrollView.setOnScrollChangeListener { view, _, maxScrollY, _, oldScrollY - val currentY view.getScrollY() val isAtBottom currentY maxScrollY - 10 val isScrollingDown currentY oldScrollY if (isAtBottom isScrollingDown !isLoading) { isLoading true // 触发加载更多数据的逻辑 loadMore() } }注意这里的maxScrollY参数直接来自回调它代表当前内容总高度减去ScrollView的高度也就是理论上最大可滚动距离。拿getScrollY() maxScrollY - 10判断留出10px的容错区间防止手势到底但还没完全触发的情况。另一个写法是重写onScrollChanged在里面做减法原理一样。但我推荐用OnScrollChangeListener代码更简洁也不容易破坏原有的滚动逻辑。5.2 自动滚动到顶部或指定位置点击标题栏的某个按钮希望ScrollView平滑滚动到页面顶部。可以用smoothScrollTo它的效果是带过渡动画的体验比scrollTo的直接跳转好很多scrollView.smoothScrollTo(0, 0)注意smoothScrollTo是异步执行动画在动画过程里如果你同时调用了scrollTo或者恢复内容高度动画位置会被打断。一个更稳妥的替代方案是post执行scrollView.post { scrollView.smoothScrollTo(0, 0) }还有一种需求是滚动到页面中某个子View的位置。这时可以用scrollToChild的思路先拿到子View的Top坐标再调用scrollToval targetY scrollView.getChildAt(0).getBottom() scrollView.smoothScrollTo(0, targetY)但如果页面布局复杂直接按坐标滚动很容易定位不准更傻瓜的做法是调用targetView.getTop()取出相对父容器的坐标再和ScrollView当前的滚动位置做差得到精确的偏移量。5.3 初始化时自动滚动到某个位置Activity启动后希望ScrollView直接跳过顶部的banner区域定位到内容区。直接在onCreate里调用smoothScrollTo往往不生效因为此时视图还没有完成首次布局测量高度可能为0。需要在窗口焦点变化或者布局完成后执行scrollView.doOnPreDraw { scrollView.scrollTo(0, 400) }doOnPreDraw是ViewGroup的一个扩展方法只有在视图即将绘制时才会回调这时候布局测量已经完成。如果你不想引入扩展库也可以用view.post {}实现但实测在极少情况下post时机还是太早doOnPreDraw更可靠。5.4 一个容易忽略的细节默认焦点抢占有时候打开一个页面ScrollView会莫名其妙自动滚到中间某个位置让人很困惑。这通常是因为页面内有输入框系统焦点自动落到了输入框上并且ScrollView会尝试滚动到焦点所在的位置以便用户输入。解决方法是输入框所在区域设置android:focusablefalse或者更稳妥的做法是把初始焦点交给ScrollView自身ScrollView android:idid/scrollView android:focusabletrue android:focusableInTouchModetrue /这样页面打开时焦点默认在ScrollView上不会跳转。这个坑在登录注册页极其常见我以前就遇到过好几次不是自己踩进去就是看同事踩进去所以现在写表单页基本默认带上这两行配置。5.5 实战心得ScrollView的定位正在变化现在很多新项目里ScrollView的核心场景正在被CoordinatorLayout和NestedScrollView慢慢替代因为后两者的联动能力和事件协同更强。但ScrollView没有过时它依然是最简单的滚动容器适合那些“不需要复杂联动、内容固定”的场景。就我的使用经验来说项目中大概有20%的滚动场景会用到ScrollView另外80%会用RecyclerView或NestedScrollView。你不要因为“基础”就轻视它也不要因为“高级”就非得用框架。选型的唯一标准永远是业务需求页面结构简单且内容固定就用ScrollView省心省力结构复杂且需要联动就上NestedScrollView或CoordinatorLayout。6. 常见问题速查表我把日常开发里遇到的高频问题整理成一张表方便你在项目里查漏补缺。这里面每一条都是实际踩过或者帮别人排查过的值得收藏。问题现象根本原因解决方法ScrollView崩溃提示只能有一个直接子View放了多个直接子View用LinearLayout等容器包一层内容不满一屏但背景色没铺满没有设置fillViewport添加android:fillViewporttrue打开页面自动跳转到中间输入框抢占焦点设置focusabletrue或focusableInTouchModetrue内嵌RecyclerView只显示部分Item测量高度为0或被截断NestedScrollView配合nestedScrollingEnabledfalse限制数据量滑动卡顿掉帧图片未压缩、布局层级过深Glide压缩、ConstraintLayout减少层级、避免滚动中频繁findViewById软键盘遮挡输入框windowSoftInputMode配置不当设置adjustResize页面底部有多余空白ScrollView高度设置错误外层用LinearLayoutScrollView使用0dp和layout_weight边缘光晕/过度滚动蓝色阴影系统默认效果overScrollModeneverToolbar/标题栏渐变不生效没有监听滚动位置setOnScrollChangeListener计算透明度自动加载更多触发不灵敏底部判断阈值设置过大用scrollY maxScrollY - 10作为判断条件这些内容看着零散但每一条背后都有具体的业务场景。遇到问题的时候回头翻一下这张表大多数情况都能对号入座。哪怕方案不是最优雅的至少能让你先跑起来后面有机会再优化也不迟。ScrollView看起来是个很小的知识点但它牵扯到事件分发、测量规则、布局优化、嵌套滚动等等一系列底层逻辑把它真正吃透你对Android整个View体系的理解都会深一层。如果你正在入门阶段建议照着上面的代码自己动手敲一遍改一改参数看看效果差异这种手感比光看文章要牢靠得多。等你跑通了这几个典型场景再去碰CoordinatorLayout和嵌套滚动机制会发现自己的底气完全不一样。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →