Animation with MotionLayout

Posted xiaoqiang_0719

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Animation with MotionLayout相关的知识,希望对你有一定的参考价值。

文章目录

简介

MotionLayout是ConstraintLayout的子类,它能够为布局内的控件添加动画效果

它的实现方式简单来讲就是通过处理两个ConstraintSet之间的切换(start,end),并根据ConstraintSet定义的参数来自动生成切换动画

使用入门

添加 ConstraintLayout 依赖

在 build.gradle 文件添加 ConstraintLayout 2.0 的依赖,注意 MotionLayout 是 ConstraintLayout 2.0 以后才有的功能,所以ConstraintLayout版本必须是大于等于2.0

    implementation "androidx.constraintlayout:constraintlayout:2.0.4"

将ConstraintLayout转为MotionLayout

如下图,打开布局文件进入split或者Design模式,右键选择Convert to MotionLayout这样就会进入MotionLayout的布局模式 ,ConstraintLayout会被转换成MotionLayout,并且在res下创建xml文件夹新增一个MotionScene文件,动画的相关配置都是储存在这个文件中

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/test_scene">

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

自动生成的MotionScene文件代码:

<?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:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
       <KeyFrameSet>
       </KeyFrameSet>
    </Transition>

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

    <ConstraintSet android:id="@+id/end">
    </ConstraintSet>
</MotionScene>

开始创建动画

动画的设置可以在xml文件中进行编辑,也可以在MotionLayout Editor中进行编辑,推荐大家使用第二种方式(操作简单,提示智能)

在编辑区域可以看到两个可以点击的区域,一个名字是start,一个是end。 他们代表的是动画效果的开始状态结束状态

定义开始和结束动画

Motion编辑器中点击start、end,选择下方需要执行动画的控件修改其开始、结束的参数,可以修改位置,大小,透明度,以及自定义属性(get、set方法)等等。。

定义过渡动画

定义动画应花费的时间以及如何处理用户触摸以滑动动画。

<Transition
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">

    <OnClick
        motion:targetId="@id/button"
        motion:clickAction="toggle" />
        
    //<OnSwipe /> 只有两种操作,点击(OnClick)和滑动(OnSwipe)
    
</Transition>

在Motion编辑器中预览动画

图片变换

使用ImageFilterView设置srcCompat和altSrc, 分别代表显示的第一张图片和第二张图片

    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/image_filter_view"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:altSrc="@mipmap/back_local_hdd_cover"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.2"
        app:srcCompat="@mipmap/back_local_usb_cover" />

KeyFrameSet 定义关键帧

位置关键帧 KeyPosition

motion:framePosition=“20” 数值区间是[0-100]
motion:keyPositionType=“parentRelative” 相对布局坐标系
motion:motionTarget="@+id/image_filter_view" 目标控件

keyPositionType有三种坐标系, deltaRelativeparentRelativepathRelative

parentRelative

deltaRelative

pathRelative

<KeyPosition
    motion:framePosition="20"
    motion:keyPositionType="parentRelative"
    motion:motionTarget="@+id/image_filter_view"
    motion:percentX="0.2"
    motion:percentY="0.4" />

属性关键帧 KeyAttribute

        <KeyAttribute
            android:alpha="0.5"
            motion:framePosition="50"
            motion:motionTarget="@+id/image_filter_view" />

标准属性的列表:

  • android:visibility
  • android:alpha
  • android:elevation
  • android:rotation
  • android:rotationX
  • android:rotationY
  • android:scaleX
  • android:scaleY
  • android:translationX
  • android:translationY
  • android:translationZ

触发器关键帧 KeyTrigger

当到达某个关键帧之后触发该motionTarget的某个方法

            <KeyTrigger
                motion:framePosition="10"
                motion:motionTarget="@+id/image_button"
                motion:onNegativeCross="revertImage"
                motion:onPositiveCross="changeImage" />

循环关键帧 KeyCycle

用来给动画添加振动,他表示在动画过程中的循环动画

            <KeyCycle
                motion:framePosition="30"
                motion:motionTarget="@id/button_first"
                motion:wavePeriod="1"
                motion:waveShape="sin"
                android:rotation="50" />

循环动画关键帧 KeyTimeCycle

表示循环动画,他会一直显示在View上,不会随着动画的变化而消失

            <KeyTimeCycle
                android:rotation="50"
                motion:motionTarget="@id/button_first"
                motion:wavePeriod="1"
                motion:waveShape="sin" />

触发动画的几种方式

OnClick

        <OnClick
            motion:clickAction="toggle"
            motion:targetId="@id/button_first" />
            
       targetId表示点击对应的View触发。
       clickAction标识view在start状态和end状态切换的方式,它有5个值:
        
       toggle:来回切换
       jumpToStart:瞬间跳转到start状态,没有动画
       jumpToEnd:瞬间跳转到end状态,没有动画
       transitionToStart:动画过渡到start状态
       transitionToEnd:动画过渡到end状态

OnSwipe

        <OnSwipe
            motion:dragDirection="dragRight"
            motion:touchAnchorId="@id/button_first"
            motion:touchAnchorSide="bottom" />

代码调用

            motion_layout.setTransitionDuration(3000)
            motion_layout.transitionToState(R.id.end)
            motion_layout.transitionToEnd()
    motion_layout.addTransitionListener(object : MotionLayout.TransitionListener 
            override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) 
                Log.i(TAG, "onTransitionStarted: $p1..$p2")
            

            override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) 
                Log.i(TAG, "onTransitionChange: $p1..$p2..$p3")
            

            override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) 
                Log.i(TAG, "onTransitionCompleted:  $p1 ")
            

            override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) 
                Log.i(TAG, "onTransitionTrigger: $p1..$p2..$p3")
            
        )

注意事项

  1. MotionScene 中的定义的布局优先于 MotionLayout 中的任何类似定义

  2. 当MotionLayout侦听拖动事件时,侦听器将在MotionLayout视图上注册,而不是在touchAnchorId指定的视图上touchAnchorId

  3. 使用OnSwipe一定要设置dragDirection,不然会有回弹

  4. 直接通过xml代码的情况下,无法设置动画的衔接,设定动画的先后顺序,但是可以通过别的途径来修改
    比如:触发器关键帧 KeyTrigger到100 之后执行某个动作,或者在代码中使用addTransitionListener监听来处理两个动画衔接(虽然可以通过上述方式实现,但是MotionLayout的使用场景不是用来衔接动画的,而是作为一个过渡动画,应该适用于一些控件切换,界面变化之类的动画

Demo示例

Animation with MotionLayout Demo

以上是关于Animation with MotionLayout的主要内容,如果未能解决你的问题,请参考以下文章