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

MotionLayout调整尺寸时子视图文本被截断,如何解决?

问题描述

首次尝试用MotionLayout解决嵌套RecyclerView条目展开/收起的动画问题。RecyclerView默认展开动画正常,但收起时内容会立即隐藏,之后才移动底部条目,所以改用MotionLayout实现流畅的高度变化动画。

当前实现中,展开/收起的高度动画正常,但动画过程中子视图的文本会被截断——父容器底部还未覆盖文本时,文字就提前被裁切了。

排查后确认是视图或其父视图的padding、margin导致,但无法直接移除这些属性:需要适配不同设备的字体缩放,必须保留动态间距,也不能固定宽高。已经尝试将整个视图层级的clipChildren和clipToPadding设为false、把TextView放入容器单独设置margin/padding、设置motion:layoutDuringTransition="honorRequest",均无效。

求解决动画过渡过程中文本被截断的方法。

布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipChildren="false"
    android:clipToPadding="false"
    app:layoutDescription="@xml/item_motion_scene">

    <LinearLayout
        android:id="@+id/headerContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#42A5F5"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        app:layout_constraintBottom_toTopOf="@id/bottom"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/label"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_weight="1"
            android:textColor="@color/white"
            tools:text="my label"/>

        <FrameLayout
            android:id="@+id/toggleSizeContainer"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:clickable="true"
            android:clipChildren="false"
            android:focusable="true">

            <ImageView
                android:id="@+id/toggleSize"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:layout_gravity="center"
                android:contentDescription="@string/app_name"
                android:duplicateParentState="true"
                android:foreground="?selectableItemBackgroundBorderless"
                android:src="@drawable/baseline_expand_more_24"
                app:tint="@color/white"/>

        </FrameLayout>

        <FrameLayout
            android:id="@+id/removeContainer"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:clickable="true"
            android:clipChildren="false"
            android:focusable="true">

            <ImageView
                android:id="@+id/remove"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:layout_gravity="center"
                android:contentDescription="@string/app_name"
                android:duplicateParentState="true"
                android:foreground="?selectableItemBackgroundBorderless"
                android:src="@drawable/baseline_close_24"
                app:tint="@color/white"/>

        </FrameLayout>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#E57373"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:orientation="vertical"
        android:visibility="visible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/headerContainer"
        tools:visibility="visible">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:background="#BA68C8">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:clipChildren="false"
                android:clipToPadding="false"
                android:layout_margin="16dp"
                android:text="a"
                tools:ignore="HardcodedText"/>

        </LinearLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#FFB74D"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:padding="16dp"
            android:text="b"
            tools:ignore="HardcodedText"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#81C784"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:padding="16dp"
            android:text="c"
            tools:ignore="HardcodedText"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#FF8A65"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:padding="16dp"
            android:text="d"
            tools:ignore="HardcodedText"/>

    </LinearLayout>

</androidx.constraintlayout.motion.widget.MotionLayout>

MotionScene文件

<?xml version="1.0" encoding="utf-8"?>
<MotionScene 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/bottom"
            motion:layout_constraintEnd_toEndOf="parent"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:visibility="gone"
            motion:layout_constraintTop_toBottomOf="@id/headerContainer"
            motion:layout_constraintStart_toStartOf="parent"/>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/bottom"
            motion:layout_constraintEnd_toEndOf="parent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:visibility="visible"
            motion:layout_constraintTop_toBottomOf="@id/headerContainer"
            motion:layout_constraintStart_toStartOf="parent"/>
    </ConstraintSet>

    <Transition
        motion:layoutDuringTransition="ignoreRequest"
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/end">
    </Transition>
</MotionScene>

解决方案

1. 调整根MotionLayout的裁剪与布局策略

在根MotionLayout中添加android:clipToOutline="false",并将motion:layoutDuringTransition改为honorRequest,确保过渡期间子视图的布局能正确更新,不被提前裁剪:

<androidx.constraintlayout.motion.widget.MotionLayout
    ...
    android:clipChildren="false"
    android:clipToPadding="false"
    android:clipToOutline="false"
    motion:layoutDuringTransition="honorRequest"
    ...>

2. 修改MotionScene,移除visibility切换

删除start约束集中的android:visibility="gone"属性,仅保留高度为0dp的设置——visibility切换会导致视图提前隐藏,是文本截断的核心诱因。同时添加动画时长与插值器,让过渡更平滑:

<MotionScene ...>
    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/bottom"
            motion:layout_constraintEnd_toEndOf="parent"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintTop_toBottomOf="@id/headerContainer"
            motion:layout_constraintStart_toStartOf="parent"/>
    </ConstraintSet>

    <Transition
        motion:layoutDuringTransition="honorRequest"
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/end"
        motion:duration="300"
        motion:interpolator="linear">
    </Transition>
</MotionScene>

3. 给底部容器设置最小高度

在bottom LinearLayout中添加android:minHeight="1dp",避免高度动画到0时视图被完全隐藏,确保子视图在整个过渡过程中保持测量状态:

<LinearLayout
    android:id="@+id/bottom"
    ...
    android:minHeight="1dp"
    ...>

4. 启用布局变化动画辅助

在底部容器中添加android:animateLayoutChanges="true",让系统自动处理子视图的显示过渡,进一步避免文本截断:

<LinearLayout
    android:id="@+id/bottom"
    ...
    android:animateLayoutChanges="true"
    ...>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:27:02