如何在MotionLayout的JSON5配置中实现组件高度动态适配?
MotionLayout中如何基于JSON5定义的高度实现屏幕尺寸动态适配
我在使用MotionLayout时,组件高度是通过JSON5配置定义的,现在需要根据不同设备的屏幕尺寸动态调整这些高度值。我已经写了一个计算缩放比例的函数:
fun getToDeviceWidthDpRatio(screenWidth: Int): Float { if (screenWidth == Constants.DEVICE_BASE_WIDTH) { return 1f } var ratio = screenWidth / Constants.DEVICE_BASE_WIDTH.toFloat() val maxRatio = 1.4f if (ratio >= maxRatio) { ratio = maxRatio } return ratio }
但问题是JSON5配置里没法直接调用这个函数,以下是我的JSON5配置片段,标记的两处height需要按比例缩放:
ConstraintSets: { start: { topBackground: { width: 'parent', height: 166, // 需要缩放的高度 start: ['parent', 'start'], end: ['parent', 'end'], top: ['parent', 'top'], custom: { gradientStart: '#EA1717', gradientEnd: '#FD7777', cornerRadius: 16 } } }, end:{ topBackground: { width: 'parent', height: 56, // 需要缩放的高度 start: ['parent', 'start'], end: ['parent', 'end'], top: ['parent', 'top'], custom: { gradientStart: '#EA1717', gradientEnd: '#EA1717', cornerRadius: 0 } } } }
请问有什么可行的方法实现高度的动态调整?
解决方案
以下是几种实用的实现方式:
1. 解析JSON5后动态修改高度值
先把JSON5配置解析成可修改的数据对象(比如用Gson、Moshi等序列化库),计算出缩放比例后直接修改对应节点的height值,再将修改后的配置加载到MotionLayout中。
示例代码(以Gson为例):
// 定义对应JSON结构的数据类 data class MotionConfig( val ConstraintSets: ConstraintSets ) data class ConstraintSets( val start: ConstraintSet, val end: ConstraintSet ) data class ConstraintSet( val topBackground: ViewConstraint ) data class ViewConstraint( val width: String, var height: Int, val start: List<String>, val end: List<String>, val top: List<String>, val custom: CustomAttrs ) data class CustomAttrs( val gradientStart: String, val gradientEnd: String, val cornerRadius: Int ) // 解析并修改配置 val json5Content = "你的JSON5配置字符串" val gson = Gson() val motionConfig = gson.fromJson(json5Content, MotionConfig::class.java) // 获取屏幕宽度并计算缩放比例 val screenWidth = resources.displayMetrics.widthPixels val ratio = getToDeviceWidthDpRatio(screenWidth) // 调整start和end状态下的高度 motionConfig.ConstraintSets.start.topBackground.height = (motionConfig.ConstraintSets.start.topBackground.height * ratio).toInt() motionConfig.ConstraintSets.end.topBackground.height = (motionConfig.ConstraintSets.end.topBackground.height * ratio).toInt() // 将修改后的配置转回JSON并加载到MotionLayout val modifiedJson = gson.toJson(motionConfig) motionLayout.loadMotionContent(modifiedJson)
2. 用代码构建ConstraintSet替代JSON5
放弃完全依赖JSON5配置,改用代码创建ConstraintSet,直接调用缩放函数计算高度值,同时保留其他配置逻辑。
示例代码:
// 创建start状态的ConstraintSet val startSet = ConstraintSet() startSet.clone(motionLayout) // 计算缩放后的高度 val screenWidth = resources.displayMetrics.widthPixels val ratio = getToDeviceWidthDpRatio(screenWidth) val scaledStartHeight = (166 * ratio).toInt() val scaledEndHeight = (56 * ratio).toInt() // 设置topBackground的高度 startSet.setHeight(R.id.topBackground, scaledStartHeight) // 渐变、圆角等自定义属性可通过自定义View的方法单独设置 // 创建end状态的ConstraintSet val endSet = ConstraintSet() endSet.clone(motionLayout) endSet.setHeight(R.id.topBackground, scaledEndHeight) // 构建MotionScene并绑定到MotionLayout val motionScene = MotionScene(motionLayout.context) motionScene.addTransition( TransitionBuilder.buildTransition( startSet, endSet, AutoTransition() ) ) motionLayout.setMotionScene(motionScene)
3. 自定义MotionLayout拦截配置加载
自定义MotionLayout子类,重写加载配置的方法,在加载JSON5后自动完成高度缩放,无需修改原有配置文件。
示例代码:
class ScalableMotionLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : MotionLayout(context, attrs, defStyleAttr) { override fun loadMotionContent(content: String): Boolean { // 解析配置并调整高度 val gson = Gson() val motionConfig = gson.fromJson(content, MotionConfig::class.java) val screenWidth = resources.displayMetrics.widthPixels val ratio = getToDeviceWidthDpRatio(screenWidth) motionConfig.ConstraintSets.start.topBackground.height = (motionConfig.ConstraintSets.start.topBackground.height * ratio).toInt() motionConfig.ConstraintSets.end.topBackground.height = (motionConfig.ConstraintSets.end.topBackground.height * ratio).toInt() // 加载修改后的配置 val modifiedContent = gson.toJson(motionConfig) return super.loadMotionContent(modifiedContent) } // 内置缩放比例计算逻辑 private fun getToDeviceWidthDpRatio(screenWidth: Int): Float { if (screenWidth == Constants.DEVICE_BASE_WIDTH) { return 1f } var ratio = screenWidth / Constants.DEVICE_BASE_WIDTH.toFloat() val maxRatio = 1.4f if (ratio >= maxRatio) { ratio = maxRatio } return ratio } }
之后在布局文件中使用这个自定义ScalableMotionLayout即可自动完成高度适配。
内容的提问来源于stack exchange,提问作者Sharz
相关产品推荐
相关产品推荐

