首页 > 解决方案 > 以编程方式更改约束时,Android MotionLayout 状态未更新

问题描述

我有一个非常简单的 MotionLayout,我正在尝试以编程方式更改其中一个约束的可见性,并让视图反映该更改。但是,直到我从当前状态转换然后回到它之前,更改才会反映出来。如何让视图立即根据新的约束进行自我更新?我试过了updateState()rebuildScene()invalidate()他们似乎都没有做任何事情。

这是视图:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout
        android:id="@+id/motion_layout"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:showIn="@layout/activity_main"
        app:layoutDescription="@xml/motion_scene"
        tools:context=".MainActivity">

    <TextView
            android:id="@+id/left_to_right_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Left to right"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    <TextView
            android:id="@+id/top_to_bottom_text"
            android:text="Top to bottom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

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

这是动态场景:

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


    <Transition
            app:constraintSetStart="@id/base"
            app:constraintSetEnd="@id/bottom">
        <OnClick
                app:targetId="@id/top_to_bottom_text">

        </OnClick>

    </Transition>

    <Transition
            app:constraintSetStart="@id/base"
            app:constraintSetEnd="@id/right">
        <OnSwipe
                app:dragDirection="dragRight"
                app:touchAnchorId="@id/left_to_right_text"
                app:touchAnchorSide="right">

        </OnSwipe>

    </Transition>


    <ConstraintSet android:id="@+id/base">

        <Constraint
                android:id="@id/left_to_right_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
                android:id="@id/top_to_bottom_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/bottom">

        <Constraint
                android:id="@id/left_to_right_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
                android:id="@id/top_to_bottom_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
        />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/right">

        <Constraint
                android:id="@id/left_to_right_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
                android:id="@id/top_to_bottom_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
    </ConstraintSet>
</MotionScene>

这是我尝试在 2 秒后更新文本视图之一的可见性的代码:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        Completable.timer(2, TimeUnit.SECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                motion_layout.getConstraintSet(R.id.base)?.let {
                    it.setVisibility(R.id.left_to_right_text, View.INVISIBLE)
                }
                motion_layout.updateState()
                // These don't work either
//                motion_layout.rebuildScene()
//                motion_layout.invalidate()
            }
    }

你可以在下面的 gif 中看到,我等了 2 秒,没有任何反应。直到我转换到一个新状态,然后回到原来的状态,可见性才会更新。

标签: androidandroid-motionlayout

解决方案


我找到了解决办法!

Phan Van Linh 提供的解决方案对我不起作用(我使用的是 2.0.0-beta1 约束版本)

只需在您刚刚更改可见性(或其他视图属性)的视图上调用 .requestLayout() 所以在您的示例中:

motion_layout.getConstraintSet(R.id.base)?.let {
                it.setVisibility(R.id.left_to_right_text, View.INVISIBLE)
                yourView.requestLayout() // <-- this line
            }

由于 requestLayout 方法的文档,它将通过当前布局传递中的所有视图层次结构。


推荐阅读