MotionLayout系列之一

1,293 阅读4分钟

这里主要介绍Motionlayout的使用,他是ConstraintLayout的子类,所以我们需要添加对ConstraintLayout的引用

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'

既然是子类那么就可以像ConstraintLayout一样的使用,比如这里

<androidx.constraintlayout.motion.widget.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <ImageView
            android:id="@+id/red_star"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:srcCompat="@drawable/ic_star_outline"
            android:tint="?colorSecondary"
            android:contentDescription="@string/star"
    />

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

MotionLayout的主要作用是提供动画,所以我们让这个ImageView从屏幕的左上角移动到右下角(动画效果),既然是xml动画,我们需要创建一个xml来描述这个动画 在res上右键选择new-AndroidResourceFile,在弹出的菜单中选择及填写如下信息

为了定义这个动画我们需要知道是哪个view要进行移动,以及他的起始和结束位置,这里通过ConstraintSet来定义 比如定义开始位置

<ConstraintSet android:id="@+id/start" >
    <Constraint
            android:id="@+id/red_star"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>

这里的内容跟在ConstraintLayout中填写的view布局没什么区别,就是约束位置,唯一需要注意的是,这里的Constraint中的id,他必须与在MotionLayout中定义的需要做动画的view的id一致,这里的constraint其实就是原来布局中的每一个view,同理结束位置为

<ConstraintSet android:id="@+id/end">
    <Constraint
            android:id="@+id/red_star"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>

定义好位置之后就可以做动画了,通过Transition标签来做

<Transition
        app:constraintSetStart="@+id/start"
        app:constraintSetEnd="@+id/end"
        app:autoTransition="animateToEnd"
        app:motionInterpolator="bounce"
        app:staggered="1.2"
        app:duration="2000">
    <!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
    <OnClick
            app:targetId="@id/start_btn"
            app:clickAction="toggle" />
</Transition>

可以看到transition标签中有这些属性,其中属性值含义如下

  • constraintSetStart:动画开始位置
  • constraintSetEnd:动画结束位置
  • autoTransiton:是否开启自动的动画,也就是不通过点击或者touch事件来触发动画,它的值一共有这么几种animateToStart(动画过渡到start),animateToEnd(动画过渡到end),jumpToStart(直接跳到start),jumpToEnd(直接跳到end),none(没有任何效果),其实这个属性是替代了下面的或者是标签
  • motionInterpolator:动画差值器,也就是从开始到结束位置动画的效果,比如线性linear,回弹效果bounce,easeOut,easeIn,easeInOut几种值
  • staggered:它是一个float值,它是一种快速移动目标的方法
  • duration:动画持续时间
  • :动画的触发方式,这里是通过点击
    • targetId:则是哪个控件触发这个动画就是哪个控件的id
    • clickAction:点击后动画如何运行。一共有这几种值toggle(类似于开关,点击一次到终点,再点一次回起点,如此往复)、transitionToEnd(动画的方式过渡到结束位置)、transitionToStart(动画的方式过渡到开始位置)、jumpToEnd(直接跳到结束位置)、jumpToStart(直接跳到开始位置)
  • :通过拖动的方式开启动画,这个后续介绍
  • :关键帧,即在ConstraintSet之间的一些关键位置,后续介绍 所以当前的xml完整为
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
             xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- A transition describes an animation via start and end state -->
    <Transition
            app:constraintSetStart="@+id/start"
            app:constraintSetEnd="@+id/end"
            app:autoTransition="animateToEnd"
            app:motionInterpolator="bounce"
            app:duration="2000">
        <!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
        <OnClick
                app:targetId="@id/star"
                app:clickAction="toggle" />
        
    </Transition>

    <!-- Constraints to apply at the start of the animation -->
    <ConstraintSet android:id="@+id/start" >
        <Constraint
                android:id="@+id/red_star"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="30dp"
                android:layout_marginStart="30dp"
                android:layout_marginBottom="30dp"
                android:layout_marginEnd="30dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>

    <!-- Constraints to apply at the end of the animation -->
    <ConstraintSet android:id="@+id/end">
        <Constraint
                android:id="@+id/red_star"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent" />
    </ConstraintSet>
</MotionScene>

定义完这个xml之后就可以应用了,直接在刚才那个activity的布局中根节点加入

app:layoutDescription="@xml/step1_completed"

这样在运行的时候就可以看到效果了。需要说明的是,如果给view定义动画,那么在原有的xml中不用写它的约束,即使写了,最后也会以在动画xml中Constraint节点中定义的位置为准。除了定义ConstraintSet之外,我们还可以直接定义两个xml,这两个xml就是普通的layout文件,当然根节点得是ConstraintLayout,在transition中通过@layout来引用这两个文件

<MotionScene
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetStart="@layout/motion_01_cl_start"
        motion:constraintSetEnd="@layout/motion_01_cl_end"
        motion:duration="1000">
        <OnSwipe
            motion:touchAnchorId="@+id/button"
            motion:touchAnchorSide="right"
            motion:dragDirection="dragRight" />
    </Transition>

</MotionScene>