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

为何固定宽度的CustomView在MotionLayout旋转动画中尺寸异常?

问题

使用MotionLayout实现礼品卡的rotationY旋转动画效果,卡片是一个内部包含TickerView和两个TextView的CustomView。TextView的文本来自后端,长度不定但最多显示两行。CustomView在MotionScene和Fragment布局中均设置为match_parent宽度。

问题:执行旋转动画时,原本显示在第二行的TextView文字会跑到第一行,目前仅想到固定CustomView内部布局宽高的解决方式,求更优方案。

CustomView布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/openGiftContainer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_new_loyalty_rewarding_corners_card"
    android:gravity="center_horizontal"
    android:layout_gravity="center_horizontal"
    android:paddingVertical="@dimen/grid_3"
    android:orientation="vertical">

    <ru.secret.secret.customviews.v3.ticker.TickerView
        android:id="@+id/ivIconTicker"
        android:layout_width="@dimen/grid_9"
        android:layout_height="@dimen/grid_9"
        tools:srcCompat="@drawable/circle_blue" />

    <TextView
        android:id="@+id/tvTitle"
        style="@style/H0.TxtIconAlwaysBlack"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/grid_3_5"
        android:gravity="center"
        android:maxLines="2"
        android:ellipsize="end"
        android:paddingHorizontal="@dimen/grid_2"
        tools:text="word1 word2 word3 word4 word5 word6 word7 word8 word9 word10" />

    <TextView
        android:id="@+id/tvDescription"
        style="@style/Body1.Regular.TxtIconNeutrals2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/grid_2_5"
        android:gravity="center"
        android:maxLines="2"
        android:ellipsize="end"
        android:paddingHorizontal="@dimen/grid_3_5"
        tools:text="word1 word2 word3 word4 word5 word6 word7 word8 word9 word10"
        tools:visibility="visible" />

</LinearLayout>

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"
    motion:defaultDuration="10000">

    <Transition
        android:id="@+id/transitionOpenGiftToCongrats"
        motion:constraintSetEnd="@+id/endGiftScaleToCenter"
        motion:constraintSetStart="@id/startGiftScaleToCenter">
        <KeyFrameSet>
            ...
            <!--CongratsOpenGiftView-->
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="0"
                motion:motionTarget="@+id/congratsOpenGiftView" />
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="49"
                motion:motionTarget="@+id/congratsOpenGiftView" />
            <KeyAttribute
                android:alpha="1"
                motion:framePosition="50"
                motion:motionTarget="@+id/congratsOpenGiftView" />
            <KeyAttribute
                android:alpha="1"
                motion:framePosition="100"
                motion:motionTarget="@+id/congratsOpenGiftView" />
            ...
        </KeyFrameSet>
    </Transition>

    <ConstraintSet android:id="@+id/startGiftScaleToCenter">
        ...
        <Constraint
            android:id="@+id/congratsOpenGiftView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:alpha="0"
            android:rotationY="180"
            android:layout_marginStart="@dimen/grid_2"
            android:layout_marginEnd="@dimen/grid_2"
            motion:layout_constraintVertical_bias="0.5" />

    </ConstraintSet>

    <ConstraintSet android:id="@+id/endGiftScaleToCenter">
        ...
        <Constraint
            android:id="@+id/congratsOpenGiftView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:alpha="1"
            android:rotationY="360"
            android:layout_marginStart="@dimen/grid_2"
            android:layout_marginEnd="@dimen/grid_2"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintVertical_bias="0.5" />
</MotionScene>

Fragment布局代码

<?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"
    android:id="@+id/rootML"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:background="@drawable/bg_choice_rewarding_liner_gradient"
    app:layoutDescription="@xml/open_gift_to_congrats_scene">
    
    ...

    <ru.broker.feature.loyalty.impl.view.new_views.CongratsOpenGiftView
        android:id="@+id/congratsOpenGiftView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/grid_2"
        android:layout_marginEnd="@dimen/grid_2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.5"/>

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

解决方案

问题根源是动画过程中MotionLayout可能临时调整视图的测量约束,导致TextView可用宽度变大,原本需要换行的文字能一行放下,从而出现换行异常。以下是无需固定宽高的优化方案:

方案一:强制CustomView保持父容器宽度约束

在CustomView的代码中重写onMeasure方法,确保测量阶段始终使用父容器给出的精确宽度,避免动画时测量逻辑变化:

class CongratsOpenGiftView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 强制使用父容器提供的精确宽度,防止动画过程中宽度测量波动
        val fixedWidthSpec = MeasureSpec.makeMeasureSpec(
            MeasureSpec.getSize(widthMeasureSpec),
            MeasureSpec.EXACTLY
        )
        super.onMeasure(fixedWidthSpec, heightMeasureSpec)
    }
}

方案二:在MotionScene中明确宽度稳定性约束

在start和end的ConstraintSet中,给congratsOpenGiftView添加宽度约束的默认值和最大值,确保动画全程宽度约束一致:

<!-- 在startGiftScaleToCenter和endGiftScaleToCenter的Constraint中添加 -->
motion:layout_constraintWidth_default="match_parent"
motion:layout_constraintWidth_max="match_parent"

修改后的示例Constraint:

<Constraint
    android:id="@+id/congratsOpenGiftView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:alpha="0"
    android:rotationY="180"
    android:layout_marginStart="@dimen/grid_2"
    android:layout_marginEnd="@dimen/grid_2"
    motion:layout_constraintWidth_default="match_parent"
    motion:layout_constraintWidth_max="match_parent"
    motion:layout_constraintVertical_bias="0.5" />

方案三:优化TextView的换行稳定性

给两个TextView添加换行策略属性,让文本换行逻辑更稳定,减少因微小宽度变化导致的换行异常:

<TextView
    android:id="@+id/tvTitle"
    ...
    android:breakStrategy="balanced"
    android:hyphenationFrequency="normal" />

<TextView
    android:id="@+id/tvDescription"
    ...
    android:breakStrategy="balanced"
    android:hyphenationFrequency="normal" />

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:02:34