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的主要内容,如果未能解决你的问题,请参考以下文章

Animation with MotionLayout

Animation with MotionLayout

《Character Animation with Direct3D》阅读笔记

《Character Animation with Direct3D》阅读笔记

《Character Animation with Direct3D》阅读笔记

《Character Animation with Direct3D》阅读笔记