【Android】MotionLayout详解

MotionLayout

MotionLayout的介绍:

  1. 是什么?

    MotionLayout是一种布局类型,能够帮我们实现复杂的动画效果,还能与人进行交互;

    简单来说,我们可以在布局中定义多个状态,Motionlayout帮助我们在这些状态中平滑的过渡,从而实现复杂的动画效果;

  2. MotionLayoutConstraintLayout的子类,所以具有ConstraintLayout类同样强大的功能,所以我们只需要添加ConstraintLayout 的依赖,就可以使用 MotionLayout 了;

  3. 我们可以通过XML文件中声明性的设置,就可以完成动画效果了,当然我们也可以通过Java代码设置,拿到Motionlayout的实例,使用一些方法进行设置,后文会提到;

  4. MotionLayout 只能为它直接子级设置动画;

实现

添加依赖

java 复制代码
 implementation "androidx.constraintlayout:constraintlayout:2.2.0"

也可以去官网查看最新版

使用Motionlayout

方式一:

xml布局中component Tree > 点击第一个子项 > 点击Convert to MotionLayout

这种方法系统会自动把ConstraintLayout替换成MotionLayout,并且生成MotionSceneMotionScene可以在你的res/xml目录下找到类似于nihao\_scene.xml为名的文件;

方式二:

手动替换为MotionLayout,以及创建xml目录下MotionScene为根布局的文件;

MotionLayout文件如下:

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:layoutDescription="@xml/motionsence"
    android:background="@drawable/second">
    <ImageView
        android:id="@+id/page"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@drawable/first">

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

布局很简单,MotionLayout布局里面写了一个view;

MotionScene(运动性描述文件)

  1. 是什么?

    相应布局的所有运动描述;也就是说我们定义什么样的动画,就在这个文件里设置;

  2. 每个 MotionLayout 都引用一个单独的 MotionScene;那么怎么将二者联系起来呢?

app:layoutDescription="@xml/motionsence" :通过MotionLayoutapp:layoutDescription联系起来;

  1. MotionScene 中的定义的运动描述优先于 MotionLayout 中的任何相同定义;比如定义宽高,最终显示出来的效果是MotionScene 中定义的宽高;

    MotionLayout其他属性:

    • showPaths:布尔类型,表示在运动进行时是否显示运动路径。默认值为 false;
    • currentState:可指定具体的 ConstraintSet,告诉 MotionLayout 从哪个状态开始滑动;

其实重要的还是怎么使用动画达到我们想要的效果,所以我们来详细看看MotionScene

一个运动性描述文件框架上主要有Transition,ConstraintSet,Constraint,OnClick, OnSwipe;

简单分析完成一段动画需要什么,我们得确定动画的刚开始的状态,结束时的状态,怎么触发这个动画,一个布局里面有很多个view,如何正确的把刚开始的状态以及结束状态应用到理想的view上;那么我们围绕以上内容进行学习;

ConstraintSet
  1. 介绍: <ConstraintSet> 元素是用来指定视图在动画序列中某一点上的位置和属性(可同时指定多个视图),也叫约束条件集合;

通俗来讲:这就是决定动画某一时刻(比如起始)的状态,我们可以在这里设置很多元素的起始状态,所以这个就要做约束条件集合;

  1. 代码模式:
java 复制代码
 <ConstraintSet android:id = "@+id/end">
        <Constraint
          ...
        </Constraint>
        <Constraint
          ...
        </Constraint>
    </ConstraintSet>
  1. 属性:

    1. android:id = "@+id/end" 指定这个约束集的名字;
    2. deriveConstraintsFrom:值为另一个 ConstraintSet 的 ID;如果指定这个属性,ID 对应约束集合内的所有约束条件都将应用于此集合,除非此集明确替换它们;
  2. 里面必须包含一个或者多个Constraint元素;

Constraint
  1. 介绍:<Constraint> 元素用来声明运动序列其中一个视图的位置和属性,也就是视图的约束;

通俗来讲:规定具体的view的位置以及属性,为它设置状态;

  1. 代码:
java 复制代码
  <Constraint
            android:id = "@+id/TODO"
            android:layout_height="64dp"
            android:layout_width="64dp"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp">
            <CustomAttribute
                motion:attributeName="background"
                motion:customColorValue="#CCA4E3">
            </CustomAttribute>
</Constraint>
  1. 属性:

    1. android:layout_height="64dp"这里设置宽高的优先级高于view自身;

    这里有一个疑问?为什么在主布局中定义了view的宽高,为什么这里还要再定义一遍?

    答案:主布局设置的是初始值,而这里设置的是在这个动画中这个状态下的view的大小;
    2. android:id = "@+id/TODO"这里的ID不是为了起名字,而是为了指定作用于哪个view;

CustomAttribute
  1. 介绍:对view变化过程中进行插值,是运行时属性,比如背景颜色,文字颜色,圆角半径等;

  2. 代码:

    java 复制代码
    <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#D8C7D5">
    </CustomAttribute>  
  3. 属性:

    1. motion:attributeName你要变化的属性名
    2. motion:customColorValue变化的数值

    常见的属性表如下:

    属性类型 对应的字段名 示例 说明
    颜色 customColorValue #FF4081 可对颜色进行渐变(自动补间 RGB)
    浮点数 customFloatValue 0.5 常用于透明度、圆角半径、进度等
    整数 customIntegerValue 100 如宽高、边距、进度值等
    布尔 customBooleanValue true 通常配合状态切换使用
    字符串 customStringValue "Hello" 很少用于动画,但可以保存状态
    维度 customDimension 16dp 常用于阴影半径、圆角、边距等
  4. 如果你要定义,那么这个view的全程都应该写CustomAttribute

Transition
  1. 介绍: 该元素是用来声明运动过程中的开始和结束状态,包括所有预期的过度状态、用户的触发的交互等。

    通俗来讲:我们可能会定义很多个状态,那么它就是将开始的状态和结束的状态连接到一起作为一个动画,就是Transition 的作用

  2. 代码:

java 复制代码
   <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/middle"
       >
       <OnClick
           motion:targetId="@+id/TODO"
           motion:clickAction="transitionToEnd">

       </OnClick>

    </Transition>
      <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/middle"
        motion:duration="500">
        <OnClick
            motion:targetId="@+id/TODO"
            motion:clickAction="transitionToEnd">

        </OnClick>
    </Transition>
  1. 属性:

    1. motion:constraintSetStart="@+id/start" 确实运动过程中的起始状态;
    2. motion:constraintSetEnd="@+id/middle" 同理结束状态;
    3. motion:duration="500" 动画持续的时间,如果没写,默认父元素中设置的时间,所以我们父元素也就是MotionScene中可以设置,属性是motion:defaultDuration="1000"
  2. Transition 可以包含的元素:OnClick,OnSwipe,KeyFrameSet

OnClick
  1. 介绍: 该元素用于指定当用户点按特定视图时要执行的操作,指定当用户点击视图时触发动画序列;

通俗来讲:就是点击触发动画;

  1. 代码
java 复制代码
   <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/middle"
        motion:duration="500">
        <OnClick
            motion:targetId="@+id/TODO"
            motion:clickAction="transitionToEnd">

        </OnClick>
    </Transition>
  1. 属性:

    1. motion:targetId="@+id/TODO" 你要滑动触发的view的对象;
    2. motion:clickAction="transitionToEnd" 代表点击触发的操作;

    clickAction属性值附表

    属性值 含义 行为描述 典型使用场景
    transitionToEnd 切换到 end 状态 从当前状态(通常是 start)平滑过渡到 end 点击按钮触发开启动画,例如菜单展开、视图滑出
    transitionToStart 切换到 start 状态 从当前状态(通常是 end)回到start 点击关闭按钮,恢复初始位置
    toggle 自动在 start / end 之间切换 如果当前在start 就去 end,反之返回 start 单个按钮控制展开 / 收起
    jumpToEnd 立即跳转到 end 状态 不进行动画过渡,直接到结束布局 测试时或某些需要立即切换的场景
    jumpToStart 立即跳转到 start 状态 不进行动画过渡,直接回到起始布局 重置布局
    none 无操作 点击无任何动画或状态变化 占位、禁用点击行为

​ 其实jumpToStarttransitionToStart是区别就是有无动画,下面是一个手动startend之间切换的实例:

java 复制代码
   <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/middle"
        motion:duration="500">
        <OnClick
            motion:targetId="@+id/TODO"
            motion:clickAction="transitionToEnd">
        </OnClick>
    </Transition>
    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/middle"
        motion:duration="500">
        <OnClick
            motion:targetId="@+id/TODO"
            motion:clickAction="transitionToStart">
        </OnClick>
    </Transition>

我们可以看到两个transition定义的起始和结束状态都是一样的,那么设立了两个点击触发,就可以达到点击到结束状态,再点击回到初始状态;

  1. 为什么不写在一个Transition里面?只能写一个,写多了编译器报错;

实现的效果如下:

OnSwipe
  1. 介绍: 该元素用于指定当用户在不居中滑动时需要执行的操作;

通俗来讲:就是滑动触发动画;

  1. 代码
java 复制代码
  <OnSwipe
            motion:touchAnchorId="@id/page"
            motion:touchRegionId="@id/page"
            motion:dragDirection="dragLeft"
            motion:onTouchUp="autoComplete"
            >
 </OnSwipe>
  1. 属性:

    1. motion:touchAnchorId="@id/page" 你的目标view的ID;
    2. motion:touchRegionId="@id/page" 你的锚点区域;
    3. motion:dragDirection="dragLeft" 你朝哪个方向滑动触发
    4. motion:onTouchUp="autoComplete" 触摸结束之后的动画

实现的效果如下:

  1. 其他属性:

    motion:dragScale:控制目标视图的滑动距离和用户手指滑动距离的相对比例,默认值是1。通俗来讲,取值小于1时view比手滑动的慢,取值大于1时view比手快;

motion:maxVelocity:目标view的最大速度;

motion:maxAcceleration:目标view的最大加速度;

  1. motion:onTouchUp属性其他可选值:

stop(停止动画)、autoComplete(自动完成动画)、autoCompleteToEnd(自动完成到结束状态)、autoCompleteToStart(自动完成到开始状态)、decelerate(减速停止动画)、decelerateAndComplete(减速并完成动画);

KeyFrameSet
  1. 介绍: <KeyFrameSet> 就是提供声明更加复杂动画的元素,他声明了动画运动轨迹的关键点集合,平滑的连接这些关键点,可以得到动画;

通俗来讲:它是一个定义关键帧的容器,通过关键帧,可以精确定义动画的轨迹和行为;

  1. 代码
java 复制代码
<KeyFrameSet>
            <KeyPosition>
            </KeyPosition>
            <KeyAttribute>
            </KeyAttribute>
</KeyFrameSet>

它有很多关键帧类型,每种类型有不同的属性,触发不同的事件,这个关键帧通过动画的时间轴(frameposition)来定位;

关键帧`
KeyPosition
  1. 介绍:是一个关键帧类型,用于定义视图在动画路径上的位置和大小变化;

  2. 代码:

    java 复制代码
     <KeyPosition
                    motion:framePosition="40"
                    motion:keyPositionType="parentRelative"
                    motion:motionTarget="@+id/TODO"
                    motion:percentX="0.45"
                    motion:percentY="0.75"
                    >
    
    </KeyPosition>
  3. 属性:

    1. motion:framePosition="40" 定义了动画帧的位置,值为0-100,代表在动画过程中的百分比位置;
    2. motion:motionTarget="@+id/TODO" 目标view
    3. motion:percentX="0.45"取值在-1 - 1之间,坐标轴取决于:keyPositionType
    4. motion:keyPositionType="parentRelative" 计算关键帧的位置,有三种计算方式:
    三种计算方式:

    第一种parentRelative:相当于父容器来计算关键帧,左上角为(0,0);

go 复制代码
第二种:`deltaRelative`: 基于相对于起始点和结束点的相对偏移量来计算路径,起始坐标和终点坐标分别为(0,0)和(1,1)
复制代码
第三种:以路径为x轴,以垂直方向为Y轴
KeyAttribute
  1. 介绍: <KeyAttribute> 元素是用来在运动序列的特定时刻,设置视图的任何标准属性;

    通俗来讲:KeyPosition是规定view的位置和大小,KeyAttribute是规定view的UI元素属性的关键帧类型;可以在动画的某些时刻对元素的多个属性进行变换,例如透明度、旋转、缩放、平移等,从而实现复杂的动画效果;

  2. 代码:

java 复制代码
<KeyAttribute
    motion:motionTarget="@+id/TODO"
    motion:framePosition="50"
    android:scaleX="1.5"
    android:scaleY="1.5"
    android:rotation="359"
    >
</KeyAttribute>
  1. 属性:

    1. motion:motionTarget="@+id/TODO" 目标view
    2. motion:framePosition="50" 关键帧在动画进度中的位置
    3. android:scaleX="1.5" 表示将view的weight扩大到1.5倍
    4. android:scaleY="1.5" 同理
    5. android:rotation="359" 将view旋转359度
  2. 其他属性:

    1. android:alpha 设置透明度

    2. android:translationXandroid:translationY:控制视图的 X、Y 方向偏移;

    3. android:transformPivotXandroid:transformPivotY:控制旋转和缩放的中心点;

    4. transformPivotTarget: 指定其他视图作为旋转和缩放的中心点;

    5. motion:transitionEasing="easeInOut" 设置速度

      取值:加速(ease-in)减速(ease-out)先快后慢(easeInOut

有这样的效果:

代码如下:

java 复制代码
<?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"
    motion:duration="2000">
    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/middle"
        >
        <OnClick
            motion:targetId="@+id/TODO"
            motion:clickAction="transitionToEnd">

        </OnClick>
        <KeyFrameSet>
            <KeyPosition
                motion:framePosition="40"
                motion:keyPositionType="parentRelative"
                motion:motionTarget="@+id/TODO"
                motion:percentX="0.45"
                motion:percentY="0.75"
                >

            </KeyPosition>
            <KeyPosition
                motion:framePosition="50"
                motion:keyPositionType="parentRelative"
                motion:motionTarget="@+id/TODO"
                motion:percentX="0.5"
                motion:percentY="0.5"
                >

            </KeyPosition>
            <KeyPosition
                motion:framePosition="60"
                motion:keyPositionType="parentRelative"
                motion:motionTarget="@+id/TODO"
                motion:percentX="0.65"
                motion:percentY="0.45"
                >

            </KeyPosition>
            <KeyAttribute
                motion:motionTarget="@+id/TODO"
                motion:framePosition="50"
                android:scaleX="1.5"
                android:scaleY="1.5"
                android:rotation="359"
                >

            </KeyAttribute>
            <KeyAttribute
                motion:motionTarget="@+id/TODO"
                motion:framePosition="100"
                android:scaleX="1"
                android:scaleY="1"
                android:rotation="360"
                >

            </KeyAttribute>
        </KeyFrameSet>
    </Transition>
    <ConstraintSet android:id = "@+id/start">
        <Constraint
            android:id = "@+id/TODO"
            android:layout_height="100dp"
            android:layout_width="100dp"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginLeft="10dp"
            android:layout_marginBottom="10dp">
        </Constraint>

    </ConstraintSet>
    <ConstraintSet android:id = "@+id/middle">
        <Constraint
            android:id = "@+id/TODO"
            android:layout_height="100dp"
            android:layout_width="100dp"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp">
        </Constraint>
    </ConstraintSet>
</MotionScene

动态设置

我们也可以在代码中动态设置

方法 功能
transitionToEnd() 触发当前 Transition 到 end 状态
transitionToStart() 触发当前 Transition 到 start 状态
setProgress(float progress) 直接设置动画进度,0~1
getProgress() 获取当前 progress
setTransition(int startId, int endId) 动态切换当前 Transition

示例:

java 复制代码
MotionLayout motionLayout = findViewById(R.id.motionLayout);

// 手动设置动画进度到一半
motionLayout.setProgress(0.5f);

// 动态切换 Transition
motionLayout.setTransition(R.id.start, R.id.end);

// 播放动画到结束
motionLayout.transitionToEnd();

好啦,本次介绍到此结束,谢谢大家;VV

相关推荐
Jammingpro3 小时前
【Git版本控制】Git初识、安装、仓库初始化与仓库配置(含git init、git config与配置无法取消问题)
java·git·elasticsearch
wydaicls3 小时前
AIDL 接口的定义与生成,使用
java·开发语言
云草桑3 小时前
C#入坑JAVA 使用XXLJob
java·开发语言·c#
悟能不能悟3 小时前
springboot在DTO使用service,怎么写
java·数据库·spring boot
Uluoyu3 小时前
支持Word (doc/docx) 和 PDF 转成一张垂直拼接的长PNG图片工具类
java·pdf·word
__XYZ3 小时前
RedisTemplate 实现分布式锁
java·spring boot·redis·分布式·junit
闭着眼睛学算法3 小时前
【双机位A卷】华为OD笔试之【模拟】双机位A-新学校选址【Py/Java/C++/C/JS/Go六种语言】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
java·c语言·javascript·c++·python·算法·华为od
源码_V_saaskw4 小时前
JAVA校园跑腿校园外卖源码校园外卖小程序校园代买帮忙外卖源码社区外卖源码小程序+公众号+h5
java·开发语言·微信小程序·小程序
源码哥_博纳软云4 小时前
JAVA同城预约服务家政服务美容美发洗车保洁搬家维修家装系统源码小程序+公众号+h5
java·开发语言·微信小程序·小程序