【问题标题】:Motion layout not animating views that are not its direct children运动布局不为不是其直接子级的视图设置动画
【发布时间】:2021-09-22 01:06:54
【问题描述】:

我最近尝试了MotionLayout,当它是MotionLayout 的直接子级时,我可以在按钮上正常工作,但是当我将按钮包含在另一个布局中时,相同的运动场景不起作用,但父级布局仍然是MotionLayout

按钮是直接子元素的第一个布局:-

<?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"
  app:layoutDescription="@xml/demo"
  android:layout_height="match_parent"
  tools:context=".Demo" >

  <Button
    android:layout_width="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_height="wrap_content"
    android:id="@+id/yellow_button"
     />


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

按钮是间接子元素的第二个布局:-

<?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"
app:layoutDescription="@xml/demo"
android:layout_height="match_parent"
tools:context=".Demo"
>
<LinearLayout
   android:layout_width="match_parent"
   android:id="@+id/l1"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintEnd_toEndOf="parent"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toTopOf="parent"
   android:layout_height="wrap_content">
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/yellow_button"/>
 </LinearLayout>

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

<ConstraintSet android:id="@+id/start">
    <Constraint android:id="@+id/yellow_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" >

        <CustomAttribute app:attributeName="alpha"
            app:customFloatValue="0.0"/>

    </Constraint>
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <Constraint android:id="@id/yellow_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:alpha="1.0"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        >

        <CustomAttribute app:attributeName="alpha"
            app:customFloatValue="1.0"/>

    </Constraint>

</ConstraintSet>

<Transition
    app:constraintSetEnd="@id/end"

    app:autoTransition="animateToEnd"
    app:constraintSetStart="@+id/start"
    app:duration="2000"/>

在这些情况下是否需要遵循任何准则?

这是否意味着只有MotionLayout 的直接子级可以使用它来制作动画?

【问题讨论】:

    标签: android animation android-motionlayout android-motionscene


    【解决方案1】:

    Google Developers 的这篇中等文章 (https://medium.com/google-developers/introduction-to-motionlayout-part-i-29208674b10d) 在“限制”部分下说: “MotionLayout 只会为其直接子级提供其功能 - 与 TransitionManager 不同,后者可以处理嵌套布局层次结构以及 Activity 转换。”

    【讨论】:

      【解决方案2】:

      我找到了一种让它工作的方法,它涉及几行代码,我还没有尝试过使用如此复杂的 MotionLayouts,但至少对于一个典型的漂亮的嵌套 XML,它就像一个魅力。
      看看这个animation example

      这就是 XML 层次结构的样子

      <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/root_container"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layoutDescription="@xml/activity_main_scene"
      tools:context=".MainActivity">
      
      <androidx.constraintlayout.motion.widget.MotionLayout
          android:id="@+id/header_container"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          app:layoutDescription="@xml/header_container_scene"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toTopOf="parent">
      
          <ImageView
              android:id="@+id/imageView"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:contentDescription="@string/header_background"
              android:foreground="@drawable/white_gradient"
              android:scaleType="centerCrop"
              android:src="@drawable/tonitan_unsplash"
              app:layout_constraintEnd_toEndOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintTop_toTopOf="parent" />
      
          <ImageView
              android:id="@+id/logo"
              android:layout_width="wrap_content"
              android:layout_height="50dp"
              android:layout_marginTop="12dp"
              android:contentDescription="@string/logo"
              android:scaleType="centerCrop"
              android:src="@drawable/ic_launcher_foreground"
              app:layout_constraintEnd_toEndOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintTop_toTopOf="parent" />
      
          <androidx.constraintlayout.motion.widget.MotionLayout
              android:id="@+id/child_header_container"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:padding="16dp"
              app:layoutDescription="@xml/child_header_container_scene"
              app:layout_constraintEnd_toEndOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintTop_toBottomOf="@id/logo">
      
              <TextView
                  android:id="@+id/header_text_1"
                  style="@style/Title.White"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@string/my_header"
                  app:layout_constraintStart_toStartOf="parent"
                  app:layout_constraintTop_toTopOf="parent" />
      
              <TextView
                  android:id="@+id/header_text_2"
                  style="@style/Title.White"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@string/header_value"
                  app:layout_constraintEnd_toEndOf="parent"
                  app:layout_constraintHorizontal_bias="1.0"
                  app:layout_constraintStart_toEndOf="@id/header_text_1"
                  app:layout_constraintTop_toTopOf="parent" />
      
              <TextView
                  android:id="@+id/another_text_1"
                  style="@style/Title.White"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_marginTop="12dp"
                  android:text="@string/my_subtitle"
                  app:layout_constraintStart_toStartOf="parent"
                  app:layout_constraintTop_toBottomOf="@id/header_text_1" />
      
              <TextView
                  android:id="@+id/another_text_2"
                  style="@style/Title.White"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_marginTop="12dp"
                  android:text="@string/subtitle_value"
                  app:layout_constraintEnd_toEndOf="parent"
                  app:layout_constraintHorizontal_bias="1.0"
                  app:layout_constraintStart_toEndOf="@id/another_text_1"
                  app:layout_constraintTop_toBottomOf="@id/header_text_1" />
      
          </androidx.constraintlayout.motion.widget.MotionLayout>
      
      </androidx.constraintlayout.motion.widget.MotionLayout>
      
      <TextView
          android:id="@+id/my_app_title"
          style="@style/Title.Black.25"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_marginStart="16dp"
          android:layout_marginTop="12dp"
          android:layout_marginEnd="16dp"
          android:text="@string/my_title"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toBottomOf="@id/header_container" />
      
      <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/recycler_view"
          android:layout_width="match_parent"
          android:layout_height="0dp"
          android:layout_marginTop="12dp"
          app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toBottomOf="@id/my_app_title"
          tools:listitem="@layout/rv_item" />
      
      </androidx.constraintlayout.motion.widget.MotionLayout>
      

      魔法

      在上面的 XML 中可以看到,共有三个场景:activity_main_scene、header_container_scene、child_header_container_scene。每个人都会照顾它的直接孩子,唯一缺少的就是同步它们:

      1. 在您的 Activity、Fragment 或独立类中实现 MotionLayout.TransitionListener。
      2. 创建您需要的场景(在示例中我有 3 个)
      3. 为每一个设置事务监听器(在 onCreate、onViewCreated 或类似名称中调用这个 fun):
      private fun setUpMotionLayoutListener() = with(binding) {
              rootContainer.setTransitionListener(this@MainActivity)
              headerContainer.setTransitionListener(this@MainActivity)
              childHeaderContainer.setTransitionListener(this@MainActivity)
          }
      
      1. 使用以下代码同步它们:
      private fun updateNestedMotionLayout(motionLayout: MotionLayout?) = motionLayout?.let {
              with(binding) {
                  if (it.id == rootContainer.id) {
                      headerContainer.progress = it.progress
                      childHeaderContainer.progress = it.progress
                  }
              }
          }
      

      仅此而已!

      The complete example can be found here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-04
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多