是否可以禁用MotionLayout?

时间:2019-03-12 10:33:56

标签: android android-motionlayout

我有一个Fragment,它在RecyclerView中托管一个MotionLayout。在回收器视图的顶部,我有一个可以折叠和展开的视图,所有这些都在我的运动场景中完成。它是通过单击触发的,并且响应拖动回收站视图。到目前为止,这一切都按预期进行。

现在到了关键时刻:我想完全隐藏应用程序中某些状态的折叠视图。 但是,如果我设置视图的可见性或更改其高度,则在拖动recyclerview时它仍会重新显示。

因此有可能完全禁用MotionLayout(锁定约束),直到再次启用它为止。禁用拖动识别器也是一种可行的解决方案。

现在使用一些简化的XML来演示这种情况。

包含带有标题视图和recyclerview的运动布局的布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.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"
        ...
        />

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

        <pinch.nrcnext.view.scroll.NRCRecyclerView
            android:id="@+id/favoritesList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.SwipeRefreshLayout>

</android.support.constraint.motion.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那样容易,但这就是我的想法。

13 个答案:

答案 0 :(得分:2)

最近,我在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实际上是它开始移动的地方。 反之,我想您会明白这个窍门的。

编码愉快!

答案 1 :(得分:2)

现在有了ConstraintLayout beta 2,您可以了!这是官方的方法:

motionLayout.getTransition(R.id.yourTransition).setEnable(false)

答案 2 :(得分:1)

实际上,我通过为场景的开始和结束设置场景的开始状态来使其正常工作。在科特林:

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

当我需要启用布局时:

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

无需更改您的场景xml,它在alpha-03中对我来说都是完美的

答案 3 :(得分:1)

请注意,从最新的beta(beta2)开始,可以禁用过渡: https://androidstudio.googleblog.com/2019/06/constraintlayout-200-beta-2.html

在Kotlin中的用法示例:

(view as? MotionLayout)?.getTransition(R.id.swipe_transition)?.setEnable(false)

答案 4 :(得分:1)

我找到了一个方便的解决方案来临时禁用运动布局转换。 将此设置为xml中的MotionLayout:

app:interactionEnabled="false"

该解决方案的优点是直接在xml文件中使用它,这使您可以选择连接到数据绑定

app:interactionEnabled="@{viewModel.shouldInteractionBeActive}"

无需任何其他绑定适配器即可直接使用

答案 5 :(得分:0)

目前,似乎无法禁用MotionLayout中的运动。我希望这会在将来的版本中添加。 为了解决用例,我使用了一些肮脏的技巧:我删除了构成标头的视图。

favouritesMotionLayout.removeView(headerView)

此布局还可以,视图消失并且没有触发运动。为了找回视图,我只是重新放大了整个布局。 (一种替代方法是再次以编程方式添加视图及其约束)。

我意识到这不是最漂亮的解决方案,并且我目前正在研究无需运动布局即可解决整个用例的方法。不幸的是,在我多达90%的用例中,它都非常有用。

答案 6 :(得分: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)

答案 7 :(得分:0)

它对我有用:

  if (list.isNullOrEmpty()) {
                    //disable
                    motionLayout.apply {
                        setTransition(R.id.expanded, R.id.expanded)
                        setTransitionDuration(0)
                    }
                } else {
                   //enable
                    motionLayout.apply {
                        setTransition(R.id.expanded, R.id.collapsed)
                        setTransitionDuration(200)
                    }
                }





 p.s.: 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'

答案 8 :(得分:0)

如果要在用户操作完成后禁用过渡。只需设置   motionLayout.transitionToState(0)。您可能要在转换完成的回调中执行此操作。

检查以下代码是否适合您。

motionLayout.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 onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
        }

        override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
            (motionLayout as MotionLayout).transitionToState(0)
        }

    })

答案 9 :(得分:0)

好吧,我必须这样做,我同时尝试了beta1和beta2库,但无法禁用motionlayout,然后最终当列表为空时,我这样做以禁用motionlayout动画:-

解决方案

// this fix the transition but other parts of layout will still able to trigger transition somehow to R.id.end!!
motionLayout.setTransition(R.id.start, R.id.start);
// disable all other views touch handling except view clicks.
motionLayout.findViewById(R.id.view_ids).setOnTouchListener(
          (v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
motionLayout.findViewById(R.id.view_ids).setOnTouchListener(
          (v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
     .... disable all the views touch handling cause they can trigger the motion layout animation!

// And then finally has to disable touch handling of motionlayout itself to avoid the space exposed by motionlayout due to padding or margin which can trigger the transition!
motionLayout.setOnTouchListener(
          (v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);

简而言之,我们需要setTransition作为开始和结束的开始ID。但是我观察到这还不够。由于以某种方式motionlayout禁用了recylerview的触摸处理,但是motionlayout的所有其他部分都可以触发过渡,因此我需要禁用所有其他视图的触摸处理,并让它们仅处理单击事件然后最后也必须对motionlayout做同样的事情。导致由运动布局处理的视图之间留出空间,因此当您触摸运动布局所在的部分时,它将触发过渡。因此也需要禁用其触摸处理。

最后,它与该解决方案一起使用!希望这个答案对某人有所帮助! :)享受!

答案 10 :(得分:0)

LPirro接受的答案对我不起作用,我现在正在使用beta3。 而是按照我的要求更改了layoutDescription:

public void enableTransition(boolean enable) {
    if (enable) loadLayoutDescription(R.xml.scene);
    else loadLayoutDescription(0);
}

答案 11 :(得分:0)

我正在使用androidx.constraintlayout:constraintlayout:2.0.0-beta4motionLayout.getTransition(R.id.yourTransition).setEnable(false)对我不起作用,但是要完全禁用运动布局,例如,在我的情况下,我需要在特殊情况下隐藏视图,但是该视图涉及动画。因此,cardMotionLayout.loadLayoutDescription(0)完全禁用了MotionLayout,然后再次启用cardMotionLayout.loadLayoutDescription(R.xml.fragment_buy_card_step_one_scene)

答案 12 :(得分:0)

是的,这是可能的 先

motionLayout.getTransition(R.id.yourTransition).setEnable(false)

现在用一个没有任何作用的 Transistion 替换它

motionLayout.setTransition(R.id.your_second_Transistion);