Android MotionLayout无法适配TextView动态内容变更如何解决
问题根源
MotionLayout加载关联的MotionScene时,会提前缓存两个ConstraintSet里所有控件的尺寸、约束参数。你在end约束集里给TextView配置的wrap_content高度,会在布局首次inflate的时候,就把当时短文本对应的1行高度算好存下来。后续你动态修改TextView的文本内容,MotionLayout不会自动重新计算wrap_content对应的新尺寸,执行动画时只会读最初缓存的旧值,自然没法展开到完整内容高度。
之前试的invalidate()、postInvalidate()只会触发控件重绘,不会走测量、布局流程,根本碰不到尺寸缓存;rebuildScene()只是重新加载XML里写死的MotionScene配置,感知不到代码里动态改的文本内容,所以都没用。你找到的切GONE再切VISIBLE的野路子,本质是强制触发了控件的全局布局重算,但在低端机上很容易出现一帧的闪烁,属于临时取巧方案,不建议正式环境用。
规范实现方案
方案1:手动更新约束集高度(最推荐,零闪烁全版本兼容)
不要让MotionLayout自己猜wrap_content的高度,改完文本后手动算出TextView的实际内容高度,直接更新到end约束集里再触发动画,完全可控:
binding.button.setOnClickListener { binding.tvLorem.text = LongText // 等TextView完成新文本的测量布局,拿到准确高度 binding.tvLorem.post { val endConstraintSet = binding.motionLayout.getConstraintSet(R.id.end) // 把约束集里TextView的高度改成实际测量值 endConstraintSet.constrainHeight(R.id.tvLorem, binding.tvLorem.height) binding.motionLayout.transitionToEnd() } }
方案2:触发全局布局刷新(代码量最少)
如果不想手动改约束集,可以在设置完文本后主动请求MotionLayout重新走布局流程,刷新所有控件的尺寸缓存,等布局完成再触发动画:
binding.button.setOnClickListener { binding.tvLorem.text = LongText binding.tvLorem.post { binding.motionLayout.requestLayout() binding.motionLayout.post { binding.motionLayout.transitionToEnd() } } }
避坑提示
- 绝对不能在设置文本后立刻调用
transitionToEnd(),这时候TextView还没完成新文本的测量,MotionLayout拿到的还是旧高度 - 如果文本是异步加载返回的(比如接口拿的文案),同样要等TextView设置完文本、走完layout流程再触发动画
- 如果需要支持折叠回去的动画,记得在转场到start状态前,把end约束集的高度改回
wrap_content,避免后续重复展开时高度固定死
内容的提问来源于stack exchange,提问作者gastonbs
相关产品推荐
相关产品推荐

