先看效果:
(懒得视频转gif,就截三张图吧)
动画前:
动画中:
动画结束:
这里4个view分别做了动画:
- 文字由黑色变红色
- 拖动圆形块由做到右
- 宽度改变块的宽度变宽
- 左下角弹出四个view
先了解一下背景:
MotionLayout继承自ConstraintLayout,所以布局里面的内容不需要改变,直接替换掉ConstraintLayout也是没有问题的。
实现步骤:
替换完之后布局会报问题,根据提示,让它自己生成我们的场景文件,并且会有一个属性:
app:layoutDescription="@xml/activity_main_scene"
再加一个id,后面会用到,整块如下:
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
app:layoutDescription="@xml/activity_main_scene">
</androidx.constraintlayout.motion.widget.MotionLayout>
layout里面的view如果需要做位置动画变换的话可以先不声明类似layout_constraintStart_toStartOf这种位置,因为生成的场景文件里面声明优先级会比较高。
activity_main_scene文件如下:
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--原view的各种属性,比如位置大小透明度之类的-->
<ConstraintSet android:id="@+id/start">
<!--声明view的各个属性,注意这里的id要跟布局里面声明的那个view一致,以表同一个view。-->
<Constraint android:id="@id/tv1" >
<!--布局,view有的属性这里才有-->
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!--view没有的属性这里声明,如java里面的setXxxx的形式-->
<CustomAttribute
app:attributeName="TextColor"
app:customColorValue="#000000" />
</Constraint>
</Constraint>
<!--可以有多个,比如:-->
<Constraint android:id="@id/btn1" >
...
</Constraint>
</ConstraintSet>
<!--动画完成之后view的各种属性,中间的变换过程它自己会计算-->
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@id/tv1" >
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<CustomAttribute
app:attributeName="TextColor"
app:customColorValue="#ff0000" />
</Constraint>
</ConstraintSet>
<!--声明动画,指定哪个ConstraintSet是开始动画,哪个ConstraintSet是结束动画,并且可以指定动画时间、插值器等-->
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@id/start"
app:duration="700">
<!-- 滑动触发动画-->
<OnSwipe app:touchRegionId="@id/tv2" app:touchAnchorSide="top" app:dragDirection="dragEnd"/>
<!-- 点击触发动画 -->
<OnClick app:targetId="@id/tv_btn" app:clickAction="toggle"/>
</Transition>
</MotionScene>
一些说明看注释,简单点理解就是,在start的ConstraintSet块里面,声明view(一个view对应一个Constraint)的各种属性,到了end那里对应的view的属性值不同,中间它就会自己过滤。
比如,宽度由start的android:layout_width="100dp"到end的android:layout_width="match_parent",他就会自己伸缩这个宽度,同理位置也是这样,还有大小、旋转角度之类的都可以。
另外根据我个人总结再补充Transition的几点:
1、除了滑动触发和点击触发,还有个KeyFrameSet的方式,不过我还没研究
2、定义Constraint的id不需要用加号如@+id/tv1,改成@id/tv1,我看网上很多都是有那个+号,加上之后在我这里动画就不生效了
3、滑动触发动画的控件用touchRegionId指定,网上用说touchAnchorId,但是我试了touchAnchorId是整一个界面滑动
4、touchAnchorSide定义哪一边我貌似没有看出区别
5、dragDirection:滑动方向,这个效果挺明显
6、点击触发动画这里好像只能指定一个按钮,不过我用代码实现点击事件触发动画也不是难事:
btn.setOnClickListener {
if(motion_layout.targetPosition == 1.0f){
motion_layout.transitionToStart()
}else{
motion_layout.transitionToEnd()
}
}
就可以啦。as自带的layout界面可以预览动画,但是我不太会用,貌似怎么点都没有效果,不研究了,老老实实自己敲。知道的大佬可以留言一起学习。
最后附上项目的demo地址(虽然是用kotlin项目,但是xml直接用在java的项目上是完全可以的,按钮点击事件一样的方法。GitHub太慢了,所以我上传的码云gitee):