最新的动画布局来了,一文带你了解!

刘望舒

共 5477字,需浏览 11分钟

 ·

2022-05-10 00:14

 BATcoder技术群,让一部分人先进大厂

大家好,我是刘望舒,腾讯最具价值专家,著有三本业内知名畅销书,三本书被中国国家图书馆、各大985名校图书馆收藏,连续五年蝉联电子工业出版社年度优秀作者。

前华为面试官、独角兽公司技术总监。


想要加入 BATcoder技术群,公号回复BAT 即可。

作者:android超级兵 

https://blog.csdn.net/weixin_44819566

前言

环境


  • system : macOS
  • android studio : 4.1.3
  • constraintLayout : 2.0.4
  • gradle : gradle-6.7.1-bin
  • kotlin : 1.4.23



你需要知道什么

MotionLayout是ConstraintLayout的子类,具有ConstraintLayout所有功能。

implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

使用as预览MotionLayout,as版本必须 >= 4.0。

正文

使用 MotionLayout 创建动画

将原ConstraintLayout转换为MotionLayout布局。


转换之后:


可以看出,这里定义了一个Button,如果说在ConstraintLayout,这样写会有警告,让约束起来,但是这里并没有。

没有的原因是因为定义了MotionLayout的layoutDescription属性,需要在layoutDescription属性中写。

点击动画OnClick


onClick#clickAction说明

motion:clickAction=“toggle” 

效果:


motion:clickAction=“transitionToEnd”


motion:clickAction=“jumpToEnd”


motion:clickAction=“jumpToStart” 

不添加动画到开始。

motion:clickAction=“transitionToStart”

添加动画到开始。

多个view一起联动

例如这样:


效果图:


手指拖动OnSwipe


参数介绍:


  • motion:touchAnchorId 指的是您可以滑动并拖动的视图。
  • motion:touchAnchorSide 表示我们从右侧拖动视图。
  • motion:dragDirection 表示拖动的进度方向。



例如,motion:dragDirection=“dragRight”表示当您向右拖动时,进度会增加。

效果图:


辅助工具

motionLayout默认会自带调试工具。


官方说明图:



  • 圆圈代表一个视图的开始或结束位置。
  • 线条代表一个视图的路径。
  • 菱形代表KeyPosition修改路径。


还有一种使用视图的方式来调试:


修改路径(KeyPosition)

修改路径可以通过右侧可视化工具来进行:


详解图



再来看看自动生成的代码:


KeyPosition参数详解:


  • motion:motionTarget="@+id/button" 需要移动轨迹的view
  • motion:framePosition="[0-100]" framePosition是一个介于 0 和 100 之间的数字。它定义了在动画KeyPosition中的应用时间,1 表示 1% 的动画,99 表示 99% 的动画
  • motion:keyPositionType=“keyPositionType”这是如何KeyPosition修改路径。它可以是parentRelative,pathRelative,或deltaRelative
  • percentX | percentY是修改路径的量framePosition(值介于 0.0 和 1.0 之间,允许负值和值 >1)



这里motion:keyPositionType参数解释一下:


parentRelative

这里设置了4个点,分别为:


假设现在移动起始点1,deltaRelative会以开始点和起始点2来构建一个“贝塞尔环境”来生成对应的路径,这里和pathRelative效果类似但也有不同之处!

pathRelative和deltaRelative的区别:


  • pathRelative不需要依靠起始点和结束点就可以拖动
  • deltaRelative 起始点和结束点在同一x轴上会导致动画路径不按照贝塞尔路径执行
  • 而是执行一条直线



最终效果长这样:


效果都一样,就只放一个喽,需要的请下载源码观看。

KeyPosition配合pathMotionArc进阶

pathMotionArc见名之意就知道是用来画弧形的。先来看pathMotionArc简单的例子:


可以看出,想要画一条优雅的弧线很简单,只需要在开始点设置motion:pathMotionArc="startHorizontal"即可。

⚠️ motion:pathMotionArc是需要2个点才生效的,默认是开始点和结束点

如何设置多个点呢?例如这样,也可以设置两个弧形的比例大小等。


位置坐标说明:


pathMotionArc类型介绍:


  • startVertical 向下的弧形
  • startHorizontal 向上的弧形
  • none 直线
  • flip 和上一个点如果上一个点是startVertical,,那么当前就是startHorizontal,可以理解为反转



类型说明效果图

开始点:motion:pathMotionArc="startVertical"
起始点1:motion:pathMotionArc="startVertical"
起始点2:motion:pathMotionArc=“flip”


效果代码:

"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">
            motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        
                    motion:clickAction="toggle"
            motion:targetId="@id/button6" />
        
            
                            motion:framePosition="40"
                motion:keyPositionType="deltaRelative"
                motion:motionTarget="@+id/button6"
                motion:pathMotionArc="startVertical"
                motion:percentX="0.358"
                motion:percentY="0.17" />
            
                            motion:framePosition="79"
                motion:keyPositionType="deltaRelative"
                motion:motionTarget="@+id/button6"
                motion:pathMotionArc="flip"
                motion:percentX="0.675"
                motion:percentY="0.568" />
        

    

    
    "@+id/start">
        
                    android:id="@+id/button6"
            android:layout_width="80dp"
            android:layout_height="80dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:pathMotionArc="startVertical" />
    

    
    
    "@+id/end">
                    android:id="@+id/button6"
            android:layout_width="64dp"
            android:layout_height="64dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
    


这里比例也是能改变的,例如这样:


这里还是比较简单的,自己动手试试就懂啦!

改变属性状态(KeyAttribute)

看一眼代码:

"1.0" encoding="utf-8"?>
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

            motion:constraintSetEnd="@+id/结束了"
        motion:constraintSetStart="@id/开始了"
        motion:duration="1000">
        

            
                            motion:motionTarget="@+id/button7"
                motion:framePosition="22"
                android:alpha="0.2" />
        


                    motion:clickAction="toggle"
            motion:targetId="@id/button7" />
    

    
    "@+id/开始了">
                    android:id="@+id/button7"
            android:layout_width="60dp"
            android:layout_height="60dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    

    
    "@+id/结束了">
                    android:id="@+id/button7"
            android:layout_width="60dp"
            android:layout_height="60dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    

mlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">
            motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        
                    motion:clickAction="toggle"
            motion:targetId="@id/button6" />
        
            
                            motion:framePosition="40"
                motion:keyPositionType="deltaRelative"
                motion:motionTarget="@+id/button6"
                motion:pathMotionArc="startVertical"
                motion:percentX="0.358"
                motion:percentY="0.17" />
            
                            motion:framePosition="79"
                motion:keyPositionType="deltaRelative"
                motion:motionTarget="@+id/button6"
                motion:pathMotionArc="flip"
                motion:percentX="0.675"
                motion:percentY="0.568" />
        

    

    
    "@+id/start">
        
                    android:id="@+id/button6"
            android:layout_width="80dp"
            android:layout_height="80dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:pathMotionArc="startVertical" />
    

    
    
    "@+id/end">
                    android:id="@+id/button6"
            android:layout_width="64dp"
            android:layout_height="64dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
    


改变alpha值:


当然也可以设置多个属性,例如这样:


自行探索


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



KeyAttribute配合CustomAttribute设置颜色

CustomAttribute常用来设置view颜色的,是KeyAttribute的属性。来看看代码:

"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

            motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        
                    motion:clickAction="toggle"
            motion:targetId="@id/imageView8" />

        

            
                            android:rotation="-720"
                motion:framePosition="100"
                motion:motionTarget="@id/imageView8" />

            
                            motion:framePosition="0"
                motion:motionTarget="@id/imageView8">
                                    motion:attributeName="colorFilter"
                    motion:customColorValue="#000000" />
            

            
                            motion:framePosition="50"
                motion:motionTarget="@id/imageView8">
                                    motion:attributeName="colorFilter"
                    motion:customColorValue="#E91E63" />
            

            
                            motion:framePosition="100"
                motion:motionTarget="@id/imageView8">
                                    motion:attributeName="colorFilter"
                    motion:customColorValue="#000000" />
            
        

    

    "@+id/start" ... />

    "@+id/end" ... />


在里面CustomAttribute你必须指定一个attributeName和一个值来设置。

motion:attributeName是此自定义属性将调用的setter的名称。在这个例子中, setColorFilteronDrawable将被调用。

motion:customColorValue是名称中注明的类型的自定义值,在此示例中,自定义值是指定的颜色。

自定义值可以具有以下任何类型:


  • Color
  • Integer
  • Float
  • String
  • Dimension
  • Boolean



来看效果图就懂了。


设置抖动[KeyCycle]

如何创建


关键代码

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

        
            
                              android:alpha="0.5"
                android:scaleY="1.2"
                android:scaleX="1.2"
                motion:framePosition="51"
                motion:motionTarget="@+id/imageView8"
                motion:waveOffset="2"
                motion:wavePeriod="1"
                motion:waveShape="sin" />
        

    

参数介绍:


  • motion:waveOffset 添加到属性的偏移值
  • motion:wavePeriod 要在此区域附近循环的循环数
  • motion:waveShape=“cos”
  • sin|square|triangle|sawtooth|reverseSawtooth|cos|bounce



各大类型效果图:

(具体效果可以去原文进行查看)

设置抖动(KeyTimeCycle)

KeyTimeCycle和KeyCycle使用起来是一样的,参数也是一样的。

有一点不同的是,一般KeyTimeCycle是三个一起使用,通过3个KeyTimeCycle定义一个准确的循环关键帧。


可以看出,只有在第50帧的时候,会发生改变,因为第50帧的时候设置motion:wavePeriod为1。


  • motion:wavePeriod:要在此区域附近循环的循环数



改变控件属性(KeyTrigger)

什么叫改控件属性?如何控制?

首先自定义ImageView,里面就2个方法,show和hide。

class KeyTriggerImageView @JvmOverloads constructor(
    contextContextattrsAttributeSet
null, defStyleAttr: Int = 0
) :
    AppCompatImageView(context, attrs, defStyleAttr) {

    // 显示 view
    fun show() {
        visibility = View.VISIBLE
    }
    // 隐藏当前 view
    fun hide() {
        visibility = View.GONE
    }
}

如何使用:

"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

            motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="2000">
        
                    motion:clickAction="toggle"
            motion:targetId="@id/imageView11" />
        
                            motion:framePosition="0"
                motion:motionTarget="@+id/imageView11"
                motion:onCross="show" />
                            motion:framePosition="20"
                motion:motionTarget="@+id/imageView11"
                motion:onCross="hide" />
                            motion:framePosition="60"
                motion:motionTarget="@+id/imageView11"
                motion:onCross="show" />
                            motion:framePosition="79"
                motion:motionTarget="@+id/imageView11"
                motion:onCross="hide" />
                            motion:framePosition="100"
                motion:motionTarget="@+id/imageView11"
                motion:onCross="show" />
        

    

    "@+id/start" .../>
    "@+id/end" .../>


KeyTrigger参数介绍:


  • motion:onCross 调用的方法名字
  • motion:framePosition 当前是第几帧 (0-100)
  • motion:motionTarget 设置的控件id



来看一眼效果:


根据这个思路,是不是就可以在滑动的过程中替换图标显示,例如这样:


再来一张辅助图,现在应该是非常清晰了!!


加速与减速(Easing)

也是一个参数的效果,使用很简单,直接看代码:

"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

            motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        
                    motion:clickAction="toggle"
            motion:targetId="@id/imageView13" />
        

        

    
    
    "@+id/start">
          
                    android:id="@+id/imageView13"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:transitionEasing="acclerate" />
    
    
    "@+id/end">
                    android:id="@+id/imageView13"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    


实战

先来看看实战效果:


布局长这样:


需求分析:


  • 类似皮皮虾点击评论功能
  • 点击评论按钮时候,图片缩小,底下弹出一个recyclerview来显示评论
  • recyclerview显示的时候,评论按钮不显示
  • recyclerview不显示的时候,评论按钮显示



activity_motion_layout_9_scene.xml

"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

            motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="400">

                    motion:clickAction="toggle"
            motion:targetId="@id/imageComment" />

                    motion:dragDirection="dragUp"
            motion:touchAnchorId="@id/recyclerView" />
    

    
    "@+id/start">
                    android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />

                    android:id="@+id/imageComment"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginRight="@dimen/dp_10"
            android:alpha="1"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintVertical_bias="0.7" />
                    android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            motion:layout_constraintTop_toBottomOf="@id/image" />
    

    
    "@+id/end">
                    android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toTopOf="@id/recyclerView"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
                    android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="500dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintTop_toBottomOf="@id/image" />

                    android:id="@+id/imageComment"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginRight="@dimen/dp_10"
            android:alpha="0"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintVertical_bias="0.7" />
    


走到这里基本上就完事了。

/   总结   /

大致结构:

    app:constraintSetStart="@+id/start"
    app:constraintSetEnd="@+id/end"
    app:duration="1000">
    
    

    
    
    

            
                    
            


            

            

            
    

        
       "@id/viewId"
>
                
                
                
                
                
                
                
                
                
                
    

    
    "@+id/end"../>


完整代码地址:
https://gitee.com/lanyangyangzzz/android_ui/tree/master




耗时2年,Android进阶三部曲第三部《Android进阶指北》出版!

『BATcoder』做了多年安卓还没编译过源码?一个视频带你玩转!

『BATcoder』我去!安装Ubuntu还有坑?

重生!进阶三部曲第一部《Android进阶之光》第2版 出版!

为了防止失联,欢迎关注我的小号

  微信改了推送机制,真爱请星标本公号👇
浏览 57
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报