是否可以禁用MotionLayout?

18

我有一个Fragment,其中包含MotionLayout内的RecyclerView。在Recycler View上面,我有一个视图可以折叠和展开,所有这些都在我的动画场景中完成。它由点击触发,并响应拖动Recycler View。到目前为止,这一切都按预期工作。

现在来到关键点:我希望在应用程序的某些状态下完全隐藏可折叠视图。但是,如果我设置视图的可见性或更改其高度,则当我拖动Recycler View时,它仍会重新出现。

那么,是否可能完全禁用MotionLayout(锁定约束),直到我再次启用它?禁用拖动识别器也将是一种可行的解决方案。

以下是一些简化的XML以演示该情况。

包含头部视图和Recyclerview的布局

<?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:id="@+id/favouritesMotionLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/favourites_motion_scene"
    android:animateLayoutChanges="true">

    <ImageView
        android:id="@+id/headerView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        ...
        />

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        ...
    >

        <androidx.recyclerview.widget.RecyclerVieww
            android:id="@+id/favoritesList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

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

对应的运动场景:

<?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"
>

    <Transition
        motion:constraintSetStart="@id/expanded"
        motion:constraintSetEnd="@id/collapsed"
        motion:duration="300">
        <OnSwipe
            motion:touchAnchorId="@id/swipeRefreshLayout"
            motion:touchAnchorSide="top"
            motion:dragDirection="dragUp" />
    </Transition>

    <ConstraintSet android:id="@+id/expanded">
        ... Constraints for expanded header ...
    </ConstraintSet>

    <ConstraintSet android:id="@+id/collapsed">
        ... ... Constraints for collapsed header ...
    </ConstraintSet>

</MotionScene>

使用约束布局版本“2.0.0-alpha3”

implementation "com.android.support.constraint:constraint-layout:2.0.0-alpha3"

因此,简要概括一下:我希望能够禁用运动布局,这样我就可以滚动RecyclerView而不会扩展标题,直到我再次启用它为止。不幸的是,这并不像favouritesMotionLayout.isEnabled = false那么简单,但这是我考虑的方法。

16个回答

19
现在,使用ConstraintLayout beta 2,您可以实现这一点!以下是官方的操作方式: motionLayout.getTransition(R.id.yourTransition).setEnable(false)

我正在使用2.0.0-beta7版本,但是使用你的代码片段无法运行动画场景中的两个转换中的一个。 - StayCool
2
这在constraintlayout:2.0.4上无法工作。 - AlexS
确保不要忘记为<Transition/>设置android:id=。 - Shwarz Andrei

6

实际上,我通过设置我的场景的起始状态为起始和结束状态,成功地使其工作。在 Kotlin 中:

motionLayout.setTransition(R.id.start, R.id.start)

当我需要启用我的布局时:
motionLayout.setTransition(R.id.start, R.id.end)

不需要更改您的场景XML,在alpha-03中完美运行。


1
谢谢,有一种官方的方法可以使用 enableTransition() 来实现,但是它有 bug。 - Rodrigo Salomao

3
我发现了一个很方便的方法来暂时禁用MotionLayout过渡效果。 在xml文件中将以下代码添加到你的MotionLayout中:
app:interactionEnabled="false"

该解决方案的优势在于可以直接在xml文件中使用它,这为您提供了连接到数据绑定的选项。
app:interactionEnabled="@{viewModel.shouldInteractionBeActive}"

无需任何额外的绑定适配器即可开箱即用。此功能已添加到constraintlayout:2.1.0-beta01。


3

最近我一直在研究MotionLayout,到现在为止我发现禁用MotionLayout的方法如下。

layoutContainer.setTransitionListener(object: MotionLayout.TransitionListener {
        override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {}
        override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {}
        override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {}
        override fun onTransitionChange(p0: MotionLayout?, startedScene: Int, endScene: Int, p3: Float) {
            when(currentFragmentTag) {
                TAG_DEFAULTBACKGROUND ->  {
                    if( startedScene==R.id.halfExpanded ) {
                        layoutContainer.transitionToState(startedScene)
                    }
                }
            }
        }
    })

基本上,我正在告诉MotionLayout在某些情况下返回。

我觉得onTransitionTriggeronTransitionStarted应该包含停止函数,但它们不会在那些时刻听取。

onTransitionChange是它实际开始移动的地方。 我认为你会理解这个技巧。

编码愉快!


2

8
这个东西没用,我是不是唯一一个不能通过这样禁用过渡效果的人? - Gnzlt
相同,不能禁用特定的转换并使用该代码运行另一个。 - StayCool

1
对于我而言,只有以下解决方案可行。
禁用:
motionLayout.definedTransitions.forEach {
    it.isEnabled = false
}

启用:

motionLayout.definedTransitions.forEach {
   it.isEnabled = true       
}

              

嗨,感谢您的贡献。当我提出这个问题时,我没有代码和所需的要求,因此我无法轻松地验证这是否会对我有所帮助。但我希望您的答案能帮助其他人。 :D - stephanmantel

1

我正在使用androidx.constraintlayout:constraintlayout:2.0.0-beta4,但是motionLayout.getTransition(R.id.yourTransition).setEnable(false)对我无效。如果要完全禁用MotionLayout,例如在我的情况下我需要在特殊情况下隐藏视图,但是该视图涉及动画,则可以使用cardMotionLayout.loadLayoutDescription(0)来完全禁用MotionLayout,要再次启用,可以使用cardMotionLayout.loadLayoutDescription(R.xml.fragment_buy_card_step_one_scene)


谢谢,我的问题已经解决了,但我很高兴你也找到了另一个解决方案。 - stephanmantel
谢谢,motionLayout.getTransition(R.id.yourTransition).setEnable(false) 对我也不起作用。 - giang nguyen

0

禁用MotionLayout动画过渡的最简单方法是在MotionLayout上设置属性app:applyMotionScene="false"

属性applyMotionScene是布尔格式,用于启用/禁用MotionLayout中的过渡。


这将禁用所有动画场景。如果您只想禁用一个转换,这种方法不起作用。 - Brian Chu

0

目前似乎无法禁用MotionLayout中的动画。希望在未来的版本中能够添加此功能。 为了解决我的使用情况,我使用了一个有点不太正规的方法:删除了构成标题的视图。

favouritesMotionLayout.removeView(headerView)

布局还好,但视图消失了,动画也没有触发。为了让视图重新出现,我只是重新填充整个布局。(另一种选择是以编程方式再次添加视图及其约束条件)。

我意识到这不是最美观的解决方案,目前正在寻找解决整个用例而不使用运动布局的方法。这很不幸,因为它对我的用例非常有用,可以解决90%的问题。


0

看起来有一种方法可以黑掉你的状态以禁用运动转换。它必须在xml文件中的上述转换之后添加。 我添加了另一个具有相同起始和结束状态的转换。 在你的情况下:

<Transition
        motion:constraintSetStart="@id/collapsed"
        motion:constraintSetEnd="@id/collapsed"
        motion:duration="0">

而在Java/Kotlin中

motion_layout.setTransition(R.id.collapsed, R.id.collapsed)

有趣的想法,但这不会动态地工作,对吧? - stephanmantel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接