You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:31:20