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

Android MotionLayout滑动时如何修改其高度?

解决方案:修复MotionLayout滑动时的灰色残留问题并实现wrap_content高度

核心问题分析

你当前的实现是通过translationY将Header平移出视野,但MotionLayout本身高度未变化,导致其LightGray背景残留。同时未正确配置约束让MotionLayout自适应高度,无法满足wrap_content的需求。

步骤1:修改Composable代码

@OptIn(ExperimentalMotionApi::class)
@Composable
fun AddressableMotionLayout(
    item: XCCAddressableReference,
    listener: AddressableInfoContentAdapter.AddressableClickListener?,
) {
    val context = LocalContext.current
    val motionScene = remember {
        context.resources
            .openRawResource(R.raw.motion_scene)
            .readBytes()
            .decodeToString()
    }
    val motionState = rememberMotionLayoutState()
    val corners = 10f - ((motionState.currentProgress * 10)).coerceAtMost(10f)

    MotionLayout(
        motionScene = MotionScene(content = motionScene),
        motionLayoutState = motionState,
        modifier = Modifier.wrapContentHeight() // 关键:让布局高度由内部约束决定
    ) {
        Box(
            modifier = Modifier
                .layoutId("header")
                .background(colorResource(id = R.color.xelionMainColor))
        )

        Box(
            modifier = Modifier
                .fillMaxWidth()
                .background(Color.White, shape = RoundedCornerShape(topStart = corners, topEnd = corners))
                .layoutId("contentBg")
                .padding(top = 16.dp)
        )

        Button(
            onClick = {},
            colors = ButtonDefaults.buttonColors(contentColor = Color.White),
            modifier = Modifier.layoutId("menu"),
            contentPadding = PaddingValues(4.dp),
        ) {
            Icon(
                Icons.Default.ArrowBack,
                contentDescription = "返回"
            )
        }
    }
}

步骤2:更新motion_scene配置

替换原有的motion_scene内容,移除translationY平移逻辑,改用高度收缩实现Header隐藏:

{
  ConstraintSets: {
    start: {
      header: {
        width: 'parent',
        height: 250,
        top: ['parent', 'top', 0],
        start: ['parent', 'start', 0],
        end: ['parent', 'end', 0],
        alpha: 1
      },
      contentBg: {
        width: 'parent',
        top: ['header', 'bottom', 0],
        bottom: ['parent', 'bottom', 0],
        start: ['parent', 'start', 0],
        end: ['parent', 'end', 0]
      },
      menu: {
        width: 38,
        height: 38,
        start: ['parent', 'start', 16],
        top: ['parent', 'top', 16],
      },
    },
    end: {
      header: {
        width: 'parent',
        height: 0,
        top: ['parent', 'top', 0],
        start: ['parent', 'start', 0],
        end: ['parent', 'end', 0],
        alpha: 0
      },
      contentBg: {
        width: 'parent',
        top: ['parent', 'top', 0],
        bottom: ['parent', 'bottom', 0],
        start: ['parent', 'start', 0],
        end: ['parent', 'end', 0]
      },
      menu: {
        width: 38,
        height: 38,
        start: ['parent', 'start', 16],
        top: ['parent', 'top', 16],
      },
    }
  },
  Transitions: {
    default: {
      from: 'start',
      to: 'end',
      onSwipe: {
        anchor: 'contentBg',
        direction: 'up',
        side: 'top',
      },
      keyFrames: {
        header: {
          height: [
            {value: 250, progress: 0},
            {value: 0, progress: 1}
          ],
          alpha: [
            {value: 1, progress: 0},
            {value: 0, progress: 0.8}
          ]
        }
      }
    }
  }
}

步骤3:集成非Compose视图(如PreferenceCompat)

通过AndroidView将非Compose组件与MotionLayout组合,满足多Fragment复用需求:

@Composable
fun AddressableScreen(
    item: XCCAddressableReference,
    listener: AddressableInfoContentAdapter.AddressableClickListener?
) {
    Column(modifier = Modifier.fillMaxSize()) {
        AddressableMotionLayout(item, listener)
        AndroidView(
            factory = { context ->
                // 初始化PreferenceFragmentCompat并返回视图
                val fragment = object : PreferenceFragmentCompat() {
                    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
                        setPreferencesFromResource(R.xml.preferences, rootKey)
                    }
                }
                fragment.view ?: FrameLayout(context)
            },
            modifier = Modifier.fillMaxSize()
        )
    }
}

关键改动说明

  1. 移除固定背景:去掉MotionLayout的LightGray背景配置,避免残留空白区域
  2. wrap_content高度:通过Modifier.wrapContentHeight()让MotionLayout高度随内部约束动态变化
  3. 替换平移为高度收缩:直接修改Header的height属性实现隐藏,而非平移视图,从根源解决背景残留
  4. 约束联动:让内容区域的顶部与Header底部绑定,滑动时自动填充Header收缩后的空间

内容的提问来源于stack exchange,提问作者rosu alin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 22:37:19