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

