前言

昨晚跟往常一样,饭后开了一局王者荣耀,前中期基本焦灼,到了后期一波决定胜负的时候,我果断射箭,射中对面,配合队友直接秒杀,打赢团战一波推完基地。那叫一个精彩,队友都发出了666666的称赞,我酷酷的点了一下抱拳:多谢!嘿嘿

赛后,手机上正在展示我的MVP动画,我不禁思考,这么精彩的团战我怎么能不记录下来?刚好最近了解到MotionLayout库,就用它实现吧。

动画效果

功能详解

MotionLayout 是一种布局类型,可帮助您管理应用中的运动和微件动画。MotionLayout 是 ConstraintLayout 的子类,在其丰富的布局功能基础之上构建而成。

如上述介绍,MotionLayoutConstraintLayout的子类,相当于加了动画功能的ConstraintLayout。MotionLayout作为一个动画控件的好处就在于基本不用写java代码,全部在xml文件中搞定。而且我们只需要设定起始位置,结束位置以及一些中间状态,就能自动生成动画。

先分析下我们的团战,主要分为三个场景

  • 后羿果断射大,射中在疯狂走位的亚瑟。
  • 妲己和钟无艳同时在草丛蹲伏,看到后羿的精彩射箭,从草丛走出,准备大战。
  • 妲己2技能晕眩住对面的鲁班七号,一套技能加上钟无艳的大招,将对面两个英雄KO。

场景一

包含控件:后羿,亚瑟,鲁班,后羿的箭

动画描述:走位的亚瑟,后羿射箭

首先在布局文件中,添加第一个MotionLayout,并添加上所有的控件,后羿和鲁班由于是静止状态,所以要写上位置约束,其他包含动画的控件可以暂时不用写位置约束:

    <androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene_01"
app:showPaths="false"
tools:showPaths="true"> <ImageView
android:id="@+id/houyi"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_marginLeft="180dp"
android:src="@drawable/houyi_model"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8" /> <ImageView
android:id="@+id/houyi_arrow"
android:layout_width="66dp"
android:layout_height="66dp"
android:src="@drawable/arrow" /> <ImageView
android:id="@+id/yase"
android:layout_width="66dp"
android:layout_height="66dp"
android:src="@drawable/yase_model" /> <ImageView
android:id="@+id/luban"
android:layout_width="66dp"
android:layout_height="66dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.58"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.26"
android:src="@drawable/luban_model" /> </androidx.constraintlayout.motion.widget.MotionLayout>

由于MotionLayout继承自ConstraintLayout,所以可以用ConstraintLayout的属性。

这里可以看到有两个新的属性:

  • app:layoutDescription,这个属性就是代表该MotionLayout对应的动画场景,引用的是一个MotionScene(XML资源文件),其中就会包括相应布局的所有运动动画描述。
  • app:showPaths,这个属性代表运动进行时是否显示运动路径,也就是所有动画的路径是否显示。默认是false。代码中也是可以设置是否显示动画路径,setDebugMode方法传入MotionLayout.DEBUG_SHOW_PATH属性即可。
后羿射箭

接下来就可以写动画场景(MotionScene)了,新建res-xml-scene_01.xml。

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> <Transition
app:constraintSetEnd="@+id/end"
app:constraintSetStart="@+id/start"
app:duration="2000"> </Transition> <ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/houyi_arrow"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_marginLeft="190dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8"> </Constraint>
</ConstraintSet> <ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/houyi_arrow"
android:layout_width="66dp"
android:layout_height="66dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.65"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35">
</Constraint>
</ConstraintSet> </MotionScene>

可以看到,MotionScene有两个主要的标签TransitionConstraintSet

  • Transition,包括运动的基本定义,其中motion:constraintSetStartmotion:constraintSetEnd 指的是运动的起始状态和结束状态。分别就对应下面ConstraintSet所配置的。app:duration代表完成运动所需的时间。
  • ConstraintSet,多个控件的端点约束集合,比如这里就有两个ConstraintSet,分别代表起始约束集和结束约束集。

其中Constraint属性指定了端点位置中某一个元素的位置和属性:

  • 支持所有ConstraintLayout 属性。
  • 支持alpha,rotation,visibility,translationX,scaleX等view基本属性。
  • 支持自定义属性,用子标签CustomAttribute表示,举个小栗子:
    <Constraint
android:id="@+id/button" ...>
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D81B60"/>
</Constraint>

attributeName属性就是与具有gettersetter方法的对象匹配,比如这里的backgroundColor就对应了view本身的基本方法getBackgroundColor()setBackgroundColor()

好了,回到后裔这边,由于后羿的箭是从后羿位置到亚瑟位置,所以我们设定好后羿箭的两个端点状态,配置好后,MotionLayout就会自动帮我们生成从起始状态到结束状态的动画了,后羿箭从后羿位置飞到了亚瑟位置。

等等,运行怎么没反应呢?动画怎么触发啊?

Motion提供了三动画触发方法:

1)onClick标签,表示点击场景中的某个控件来触发动画效果。其中有两个属性。

  • app:targetId,表示要触发动画的视图
  • app:clickAction,表示点击的效果,例如,toggle(循环动画),transitionToStart(过渡到开始状态)

2)OnSwipe标签,表示通过用户轻触控制动画,有点手势滑动的感觉

  • app:touchAnchorId,表示可以滑动并拖动的视图。
  • app:touchAnchorSide 表示从哪边开始拖动。
  • app:dragDirection 表示拖动的进度方向。例如,dragRight表示当向右拖动(滑动)。
  • app:onTouchUp 表示手势抬起的时候动作。例如,stop表示手势抬起的时候view动画停止。

3)java代码控制.

  • motionLayout.transitionToEnd(),过渡动画到结束位置。
  • motionLayout.setTransitionListener,监听动画。

这里我们就设置点击后羿触发动画:

        <OnClick
app:clickAction="toggle"
app:targetId="@id/houyi" />

好了,运行,点击后羿,后羿的箭成功射出去了。

但是这还不够,后羿箭到亚瑟位置肯定就会消失了,怎么表示这个消失呢?用透明度,直接设置结束位置的透明度为0就会消失了。

      android:alpha="0"

看看效果:

好像还是有点不对,箭在空中的时候就消失了,我们要的效果是射到亚瑟才消失

这是因为生成动画的时候是按照起始点到结束点过渡的流程平均分配到每个时间点,所以他就会从一开始就慢慢线性变化透明度,直到完全消失。

怎么办呢?就要用到关键帧KeyFrameSet了。

KeyFrameSet关键帧,可以设定动画过程中的某个关键位置或属性。设定关键帧后,MotionLayout会平滑地将视图从起点移至每个中间点,然后移至最终目标位置。

所以这里,我们需要设置两个关键属性

1)快射到亚瑟的时候,箭的透明度还是1。

2)射到亚瑟的时候,透明度改成0。

        <KeyFrameSet>
<KeyAttribute
app:framePosition="98"
app:motionTarget="@id/houyi_arrow"
android:alpha="1" />
<KeyAttribute
app:framePosition="100"
app:motionTarget="@id/houyi_arrow"
android:alpha="0" />
</KeyFrameSet>

KeyAttribute就是设置关键属性的标签,其中

  • app:framePosition 表示该关键帧的位置,相当于百分比。
  • app:motionTarget 表示作用于那个视图

这样设置好,后羿箭的动画也就完成了。

疯狂走位的亚瑟

到亚瑟了,亚瑟的动画效果是走位走位被射中。所以先设定好亚瑟的位置,从远处走到被射中的位置。

    <ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/houyi_arrow"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_marginLeft="190dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8"> </Constraint> <Constraint
android:id="@+id/yase"
android:layout_width="66dp"
android:layout_height="66dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.8"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2"> </Constraint>
</ConstraintSet> <ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/houyi_arrow"
android:layout_width="66dp"
android:layout_height="66dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.65"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35"> </Constraint> <Constraint
android:id="@+id/yase"
android:layout_width="66dp"
android:layout_height="66dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.65"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35"> </Constraint>
</ConstraintSet>

可以看到,一个端点状态,可以放置多个控件属性。

放好亚瑟的起始和结束状态后,再设定疯狂走位,怎么弄?——KeyCycle

KeyCycle,循环关键帧,可以给动画添加振动,其实就是波形图,比如sin,cos

这里我们就设定一个sin曲线给亚瑟,作为走位的路径。也是放到关键帧KeyFrameSet标签下。

    <KeyCycle
android:translationY="50dp"
app:framePosition="70"
app:motionTarget="@id/yase"
app:wavePeriod="0.5"
app:waveShape="sin" />
  • app:waveShape 表示波动类型,比如sin,cos
  • app:wavePeriod表示波动周期,比如0.5就表示半个sin周期,可以理解为震动0.5次。

好了,第一个场景搞定,看看效果:

场景二

包含控件:妲己,钟无艳

动画描述:从草丛走出来的妲己和钟无艳

这一个场景主要是描述在草丛蹲伏的妲己和钟无艳,看到后羿射箭后,走出草丛准备接技能

直接上代码:

    <androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motionLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene_02"
tools:showPaths="true"> <ImageView
android:id="@+id/daji"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/daji_model" /> <ImageView
android:id="@+id/zhongwuyan"
android:layout_width="75dp"
android:layout_height="75dp"
android:src="@drawable/zhongwuyan_model" /> </androidx.constraintlayout.motion.widget.MotionLayout> //scene_02.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> <Transition
app:constraintSetEnd="@+id/end"
app:constraintSetStart="@+id/start"
app:duration="2000"> <OnClick
app:clickAction="toggle"
app:targetId="@id/daji" />
</Transition> <ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/daji"
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.75"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.85"> </Constraint> <Constraint
android:id="@+id/zhongwuyan"
android:layout_width="70dp"
android:layout_height="70dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.25"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1"> </Constraint>
</ConstraintSet> <ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/daji"
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.65"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.65"> </Constraint> <Constraint
android:id="@+id/zhongwuyan"
android:layout_width="70dp"
android:layout_height="70dp"
android:alpha="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.42"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2"> </Constraint>
</ConstraintSet> </MotionScene>

这里,我想给钟无艳一个异形走位,就是先在草丛里走,再出来。这时候就要用到另一个关键帧标签——KeyPosition

KeyPosition,表示关键帧的位置,也就是动画必经的一个点。该属性用于调整默认的运动路径。

1) motion:percentX、motion:percentY指定视图应到达的位置。keyPositionType 属性指定如何解释这些值。

2) keyPositionType有三种设置

  • parentRelative,相对于父视图的位置,x为横轴(0左-1右),y为纵轴(0顶-1底)比如要设置位置到右端中部位置,就设定app:percentY="0.5" app:percentX="1"即可。
  • deltaRelative,相对于视图在整个运动序列过程中移动的距离,(0,0)为视图起始位置,(1,1)为结束位置。x为横轴,y为纵轴
  • pathRelative,x轴方向为视图在路径范围内移动的方向,0位视图起始位置,1为结束位置(即x轴为起点和终点的连接线)。y轴垂直于x轴,正值为路径左侧,负值为右侧。所以,这个和deltaRelative相比,就是x轴和Y轴的不同,相同的是都是按照起始位置到结束位置为参考。

这里我们给钟无艳一个parentRelative。

    <KeyPosition
app:motionTarget="@id/zhongwuyan"
app:framePosition="30"
app:keyPositionType="parentRelative"
app:percentY="0"
app:percentX="0.4"
/>

最后加上两个英雄从草丛走出来,由半透明到不透明的过程:

            <KeyAttribute
app:framePosition="0"
app:motionTarget="@id/daji"
android:alpha="0.7" />
<KeyAttribute
app:framePosition="70"
app:motionTarget="@id/daji"
android:alpha="1" /> <KeyAttribute
app:framePosition="0"
app:motionTarget="@id/zhongwuyan"
android:alpha="0.7" />
<KeyAttribute
app:framePosition="60"
app:motionTarget="@id/zhongwuyan"
android:alpha="1" />

场景三

包含控件:妲己的一技能,妲己的二技能,钟无艳

动画描述:钟无艳闪现到人群中使用大招转转转,妲己二技能晕眩住鲁班,一技能跟上。

钟无艳闪现,我用的是消失再出现的方式,也就是改变alpha

钟无艳的大招,用到的是android:rotationY,设定绕y轴旋转。

妲己的一技能和二技能都是用的普通位置移动,注意控制透明度也就是出现和消失即可。

上代码:

//scene_03.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> <Transition
app:constraintSetEnd="@+id/end"
app:constraintSetStart="@+id/start"
app:duration="4000"> <KeyFrameSet> <!-- 钟无艳-->
<KeyAttribute
app:framePosition="20"
app:motionTarget="@id/zhongwuyan2"
android:rotationY="0" /> <KeyAttribute
app:framePosition="1"
app:motionTarget="@id/zhongwuyan"
android:alpha="1" /> <!-- 妲己2技能--> <KeyPosition
app:motionTarget="@id/daji_2"
app:framePosition="20"
app:keyPositionType="deltaRelative"
app:percentY="0"
app:percentX="0"
/> <KeyAttribute
app:framePosition="20"
app:motionTarget="@id/daji_2"
android:alpha="1" /> <KeyPosition
app:motionTarget="@id/daji_2"
app:framePosition="60"
app:keyPositionType="deltaRelative"
app:percentY="1"
app:percentX="1"
/> <KeyAttribute
app:framePosition="40"
app:motionTarget="@id/daji_2"
android:alpha="1" /> <KeyAttribute
app:framePosition="61"
app:motionTarget="@id/daji_2"
android:alpha="0" /> <!-- 妲己1技能--> <KeyAttribute
app:framePosition="55"
app:motionTarget="@id/daji_1"
android:alpha="1" /> <KeyPosition
app:motionTarget="@id/daji_1"
app:framePosition="55"
app:keyPositionType="deltaRelative"
app:percentY="0"
app:percentX="0"
/> <KeyAttribute
app:framePosition="85"
app:motionTarget="@id/daji_1"
android:alpha="1" /> </KeyFrameSet> <OnClick
app:clickAction="toggle"
app:targetId="@id/zhongwuyan2" />
</Transition> //。。。
</MotionScene>

想看完整源代码的可以文末附件自取。

实际应用场景

其实做下来可以发现,Motionlayout实现动画真的很简便,大大提高了开发效率,这也是jetpack组件开发的初心。

但是,Motionlayout还是有缺点的,比如直接通过xml代码的情况下,无法设置动画的衔接,设定动画的先后顺序。

所以到底motionlayout应用场景是什么呢?

motionlayout作为一个过渡动画,应该适用于一些控件切换,界面变化之类的动画。比如DrawerLayout,viewpager切换的时候,可以设置一些view过渡的动画。

官网有一个类似youtube中运动动画的案例,我这边搬过来简单说下。先看看效果

效果不错吧,特别是手势滑动的那个丝滑感,太爽了,以前做这种动画效果少说也要半个小时吧,想想就头疼。现在,MotionLayout:so easy

来一起分析下:

包含控件:顶部布局控件topLayout(包含顶部图片topImage,播放按钮topPlay,关闭按钮topClose),中部布局midlayout(包含文字部分midView),下部菜单控件bottomView

动画描述(某些具体数值由代码中得知):

  • topLayout从上方依附parent位置,变化到下方bottomView的上方。高度由320dp变成54dp。
  • topImage从满铺父布局,到最后长度不满铺(长度设置为高度2.5倍),高度距离父布局上下2dp。关键帧:到90%进度的时候,还是满铺,再慢慢缩小长度。
  • topPlay,topClose从不显示(alhpa为0)到最后显示完全(alhpa为1)。关键帧:到90%进度的时候,不透明还是为10%(alpha0.1),再慢慢变不透明。
  • midlayout,白色布局,从底部依附父布局到bottomView的上方,这个layout是为了让toplayout下来的时候更加自然,因为recycleview会变完全透明,就需要这个白色布局过渡,让动画更完整。
  • midView,从toplayout下方位置到最后和toplayout重合,透明度从不透明到完全透明。关键帧:到75%进度的时候,就完全透明。
  • bottomView,从父布局视图下面(看不到)到父布局底部(看得见)

就这么多,分析好每个布局的起始位置,结束位置,再调整一下关键帧。一个跟随手势滑动的过渡动画布局就完成了。

贴下MotionScene关键代码,想看完整源代码可以去文末附件自取,官网案例和我的demo都包含。


<?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"
motion:motionInterpolator="linear"> <OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorId="@+id/top_image_container"
motion:touchAnchorSide="bottom" /> <KeyFrameSet>
<KeyPosition
motion:curveFit="linear"
motion:framePosition="90"
motion:motionTarget="@id/top_image"
motion:percentWidth="0"
motion:percentX="0" />
<KeyPosition
motion:curveFit="linear"
motion:framePosition="90"
motion:motionTarget="@id/top_image_container"
motion:percentWidth="0" /> <KeyPosition
motion:curveFit="linear"
motion:framePosition="90"
motion:motionTarget="@id/recyclerview_container"
motion:percentWidth="0" /> <KeyAttribute
android:alpha="0"
motion:framePosition="75"
motion:motionTarget="@id/recyclerview_front" /> <KeyAttribute
android:alpha="0.10"
motion:framePosition="90"
motion:motionTarget="@id/image_clear" /> <KeyAttribute
android:alpha="0.10"
motion:framePosition="90"
motion:motionTarget="@id/image_play" />
</KeyFrameSet>
</Transition> </MotionScene>

这里有几个新属性说下:

  • motion:curveFit,表示用哪种线条轨迹经过该关键帧,默认是曲线(spline),更加圆滑。这是设置的linear为直线过渡,因为本身就是直线,所以没什么影响。
  • motion:percentWidth,表示视图相对大小,取值为0-1,0代表初始位置宽度,1代表结束位置宽度。这里为0就代表宽度到该位置还是和初始宽度一致。
  • motion:motionInterpolator,表示动画的插值器。这里的linear就是线性运动,还可以设置bounce弹簧运动等等。

关于过渡动画

关于过渡动画,其实之前也是存在的——TransitionManager

TransitionManager可以提供不同场景之间的过渡转换动画,需要设定两个场景(布局文件),然后两个场景中对应的控件id要对应上。最后通过java代码执行过渡动画。

上个代码:

//两个场景的布局
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/scene_root"> <include layout="@layout/a_scene" />
</FrameLayout> //场景一
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scene_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="26sp"
android:id="@+id/text_view1"
android:text="Text Line 1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="26sp"
android:id="@+id/text_view2"
android:text="Text Line 2" />
</LinearLayout> //场景二
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scene_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_view2"
android:textSize="22sp"
android:text="Text Line 2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:id="@+id/text_view1"
android:text="Text Line 1" />
</LinearLayout> //获取场景,开始场景间的动画,从场景一变化为场景二 val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this) titletv.setOnClickListener {
TransitionManager.go(anotherScene)
}

咦,跟MotionLayout还是蛮像的,思路也差不多,都是通过不同场景的控件完成过渡动画。那么问题来了,既然有为什么还要出个MotionLayout呢?

  • 前者(TransitionManager)无法设置关键帧,动画只有两个状态。MotionLayout就可以随意设置关键帧,设置不同的位置,属性等等。
  • 前者不能跟随手势滑动,MotionLayout就丝滑的多。
  • MotionLayout全部用xml代码就可以完成整个动画,不需要调用一句java代码。
  • 前者布局控件重复太多,需要不同的xml文件,写重复的控件

所以MotionLayout还是很优秀的,快用起来吧!

附件

官网MotionLayout案例

文章相关源代码


我的公众号:码上积木,每天三问面试题,详细剖析,助你成为offer收割机。

谢谢你的阅读,如果你觉得写的还行,就点个赞支持下吧!感谢!

你的一个,就是我分享的动力️。

后羿:我射箭了快上—用MotionLayout实现王者荣耀团战的更多相关文章

  1. 夜空中最靓的二狗子是如何让 HTTPS 快上加快的?

    二狗子是某不知名网站的站长,他热衷于通过博客分享日常的一些工作.生活.技术等,立志要成为夜空中最靓的仔. 但是前段时间有几个用户反馈,网站总是莫名会跳转到一个 xx 网站,除此之外访问速度也有点慢.作 ...

  2. 编写Java程序,使用 dom4j 解析上一节王者荣耀“英雄”对应的Xml文件数据内容,打印输出,具体格式

    查看本章节 查看作业目录 需求说明: 使用 dom4j 解析上一节王者荣耀"英雄"对应的Xml文件数据内容,打印输出,具体格式如图所示 实现思路: 创建ParseHeroXML用于 ...

  3. 比MySQL快6倍 深度解析国内首个云原生数据库POLARDB的“王者荣耀”

    随着移动互联网.电子商务的高速发展,被使用最多的企业级开源数据系统MySQL面临着巨大挑战——为迎接“双11"的高并发要提前做好分库分表;用户不断激增要将读写分离才能应对每天上亿次的访问,但 ...

  4. [1]尝试用Unity3d制作一个王者荣耀(持续更新)->选择英雄-(上)

    如果已经看过本章节:目录传送门:这是目录鸭~ 1.场景搭建: 首先我们去AssetStore逛淘宝~~~ 我淘到的是这个资源,其他好看的场景(消耗不高的都行). 然后我们导入了这个资源后,把资源根文件 ...

  5. 微信分享 JSSDK的使用

    我现在做过的在微信中运行的项目,基本上都有微信分享功能,所以,会使用JSSDK分享页面是非常重要的.分享功能的代码一般会放在beforeCreate或mounted钩子中,代码如下: this.$ht ...

  6. PSP Daily软件Alpha版本——基于spec评论

    题目要求:每个小组评论其他小组Alpha发布作品的软件功能说明书.要求和提交在[https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/122 ...

  7. MySQL数据迁移到MSSQL-以小米数据库为例-测试828W最快可达到2分11秒

    这里采用.NET Framework 4.0以上版本中新出现的 ConcurrentQueue<T> 类 MSDN是这样描述的: ConcurrentQueue<T> 类是一个 ...

  8. Github上安卓榜排名第2的程序员教你如何学习【转载,侵删】

    来自:峰瑞资本(微信号:freesvc)文章作者:代码家(微信 ID:daimajia_share) 软件早已吞噬整个世界,程序员是关键角色.过去 40 年中,许多伟大的公司都由程序员缔造,比如比尔· ...

  9. Codeforces Round #369 (Div. 2) D. Directed Roads dfs求某个联通块的在环上的点的数量

    D. Directed Roads   ZS the Coder and Chris the Baboon has explored Udayland for quite some time. The ...

随机推荐

  1. JS红宝书笔记——第一章 JavaScript简介

    1.JavaScript简史 Netscape公司决定开发一种客户端语言用来处理浏览器端简单的表单验证. Netscape公司派布兰登·艾奇(BrendanEich)为计划于1995年2月发布的Net ...

  2. 关于java中循环的学习

    switch 值是固定的 效率高语法:switch (表达式) { case 常量 1: 语句; break; case 常量 2: 语句; break; … default: 语句; break;} ...

  3. k8s运行容器之Job(四)

    Job 容器按照持续运行的时间可分为两类:服务类容器和工作类容器. 服务类容器通常持续提供服务,需要一直运行,比如 http server,daemon 等.工作类容器则是一次性任务,比如批处理程序, ...

  4. jenkins结合cygwin软件实现从centos发布代码rsync到windows server2019的过程

    jenkins结合cygwin软件实现从centos发布代码rsync到windows server2019的过程 1.下载cygwin这个软件打开https://cygwin.com/install ...

  5. 集群数据ID生成之美团叶子生成

    转自https://tech.meituan.com/2017/04/21/mt-leaf.html 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店. ...

  6. 【知识分享】Navicat Premium for Mac的破解教程

    转自Navicat Premium for Mac v12.0.22.0 破解教程,macOS上手动破解,无需补丁,无毒下载了Navicat,没有注册码,突然发现了这篇破解教程,竟爱不释手,顾Copy ...

  7. Python列出指定目录下的子目录/文件或者递归列出

    1.python只列出当前目录(或者指定目录)下的文件或者目录条目 import os files,dirs=[],[] for item in os.listdir(): if os.path.is ...

  8. Asp.Net Core 选项模式的三种注入方式

    前言 记录下最近在成都的面试题, 选项模式的热更新, 没答上来 正文 选项模式的依赖注入共有三种接口, 分别是 IOptions<>, IOptionsSnapshot<>, ...

  9. 一台电脑配置多个GigHub账号

    换了新的公司,原来的公司用SVN(比较老了),自己平时用码云(Gitee),新公司使用GitHub.前天通知我注册GitHub账号,但是并未通知用户名的事情(要求用自己的名字),原来的GitHub账号 ...

  10. 利用new Object方式创建对象

    var obj = new Object();      //创建了一个空的对象obj.uname = 'zhangsanfeng';obj.name = 18;       //字面量方式创建对象不 ...