文章目录

    • 实例
    • MotionLayout 与 CoordinatorLayout
    • MotionLayout 与 DrawerLayout
    • MotionLayout 与 ViewPager
    • MotionLayout 与 Lottie
    • 结论

上两章内容:
Android新控件MotionLayout介绍(一)
Android新控件MotionLayout介绍(二)

原文地址为:https://medium.com/google-developers/introduction-to-motionlayout-part-iii-47cd64d51a5

这一节主要讲述将MotionLayout与已经存在的Layout,比如CoordinatorLayout、DrawerLayout 或者ViewPager将如何一起使用。

实例

以下是Motionlayout与CoordinatorLayout 、 DrawerLayout 、 ViewPager 、 Lottie之间的相互集成:

CoordinatorLayout DrawerLayout ViewPager Lottie
Android新控件MotionLayout介绍(三)_第1张图片

MotionLayout 与 CoordinatorLayout

【MotionLayout可以实现CoordinatorLayout类似的效果,在接下来的文章中将会有例子】
我们很容易利用MotionLayout来指定屏幕中组件怎么实现动画。同样,你可以在已经存在的布局中添加更多有趣的操作,这样的操作并不会对你的程序造成损害。

举个例子,如果你想搞一个这样的动作:

我们可以将在AppBarLayout中的Toolbar替换成为MotionLayout,从而我们可以让CoordinatorLayout 来实现这种动画。

我们可以通过setProgress()来控制MotionLayout的动画的完成度,通过监听AppBarLayout的滚动改变来实现这个目的:
这个类名叫CollapsibleToolbar ,继承自MotionLayout

package com.google.androidstudio.motionlayoutexample.utilsimport android.content.Contextimport android.support.constraint.motion.MotionLayoutimport android.support.design.widget.AppBarLayoutimport android.util.AttributeSetclass CollapsibleToolbar @JvmOverloads constructor(        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr), AppBarLayout.OnOffsetChangedListener {    override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) {        progress = -verticalOffset / appBarLayout?.totalScrollRange?.toFloat()!!    }    override fun onAttachedToWindow() {        super.onAttachedToWindow()        (parent as? AppBarLayout)?.addOnOffsetChangedListener(this)    }}

替换掉CoordinatorLayout 中的Toolbar,换上我们自定义的CollapsibleToolbar

<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout 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:id="@+id/content"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fitsSystemWindows="false"    android:background="@color/contentBackground">    <android.support.design.widget.AppBarLayout        android:id="@+id/app_bar"        android:layout_width="match_parent"        android:layout_height="@dimen/app_bar_height"        android:theme="@style/AppTheme.AppBarOverlay">        <include layout="@layout/motion_09_coordinatorlayout_header"/>    android.support.design.widget.AppBarLayout>    <include layout="@layout/content_scrolling" />android.support.design.widget.CoordinatorLayout>

其中motion_09_coordinatorlayout_header布局如下,我们唯一要做的就是创建Motionlayout文件,包含需要动画的组件。

<?xml version="1.0" encoding="utf-8"?><com.google.androidstudio.motionlayoutexample.utils.CollapsibleToolbar    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:id="@+id/motionLayout"    app:layoutDescription="@xml/scene_09"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:minHeight="50dp"    android:fitsSystemWindows="false"    app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed">    <ImageView        android:id="@+id/background"        android:layout_width="match_parent"        android:layout_height="200dp"        android:background="@color/colorAccent"        android:scaleType="centerCrop"        android:src="@drawable/monterey"/>    <TextView        android:id="@+id/label"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:transformPivotX="0dp"        android:transformPivotY="0dp"        android:text="Monterey"        android:textColor="#FFF"        android:textSize="32dp" />com.google.androidstudio.motionlayoutexample.utils.CollapsibleToolbar>

同样,我们定义一下动画描述文件scene_09:

<?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:constraintSetStart="@+id/start"        motion:constraintSetEnd="@+id/end" />    <ConstraintSet android:id="@+id/start">        <Constraint            android:id="@+id/background"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:alpha="1.0"            motion:layout_constraintBottom_toBottomOf="parent"/>                    <Constraint            android:id="@+id/label"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:rotation="-90.0"                motion:layout_constraintBottom_toBottomOf="@+id/background"            motion:layout_constraintStart_toStartOf="parent"/>    ConstraintSet>        <ConstraintSet android:id="@+id/end">        <Constraint            android:id="@+id/background"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:alpha="0.2"            motion:layout_constraintBottom_toBottomOf="parent"/>        <Constraint            android:id="@+id/label"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginStart="8dp"            android:layout_marginBottom="8dp"            android:rotation="0.0"            motion:layout_constraintBottom_toBottomOf="@+id/background"            motion:layout_constraintStart_toStartOf="parent" />    ConstraintSet>MotionScene>

MotionLayout 与 DrawerLayout

DrawerLayout是Android5.0出来的控件,说新也不新,感觉也用的蛮多的。我们称之为抽屉式布局,结合Motionlayout可以做一下比较炫酷的情景:

就像上面的CoordinatorLayout/AppBarLayout例子一样,我们需要定义一个子类来设置Motionlayout的动画完成度progress.我们称之为DrawerContent:

package com.google.androidstudio.motionlayoutexample.utilsimport android.content.Contextimport android.support.constraint.motion.MotionLayoutimport android.support.v4.widget.DrawerLayoutimport android.util.AttributeSetimport android.view.Viewclass DrawerContent @JvmOverloads constructor(        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr), DrawerLayout.DrawerListener {    override fun onDrawerStateChanged(newState: Int) {    }    override fun onDrawerSlide(drawerView: View, slideOffset: Float) {        progress = slideOffset    }    override fun onDrawerClosed(drawerView: View) {    }    override fun onDrawerOpened(drawerView: View) {    }    override fun onAttachedToWindow() {        super.onAttachedToWindow()        (parent as? DrawerLayout)?.addDrawerListener(this)    }}

这个子类会自动的通过OnDrawerSlide()的回调slideOffset设置进度Progress
通过这个子类,在DrawaerLayout中使用MotionLayout:

<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.DrawerLayout    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"    android:id="@+id/motionLayout"    android:background="@color/colorPrimaryDark">    <include layout="@layout/motion_12_drawerlayout_content"/>    <include layout="@layout/motion_13_drawerlayout_menu"/>android.support.v4.widget.DrawerLayout>

motion_12_drawerlayout_content.xml是刚才上面例子的布局文件,我们简单拿过来用一下。
我们的菜单文件使用MotionLayout(或者我们刚刚自定义的DrawerContent):

[很简单,就是5个简单的TextView]

<?xml version="1.0" encoding="utf-8"?><com.google.androidstudio.motionlayoutexample.utils.DrawerContent    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:id="@+id/menu"    android:layout_width="180dp"    android:layout_height="match_parent"    android:layout_gravity="start"    app:layoutDescription="@xml/scene_13_menu"    android:background="@color/colorPrimaryDark">    <TextView        android:id="@+id/textView"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="32dp"        android:text="Monterey"        android:textSize="20sp"        android:textStyle="italic"        android:typeface="serif"        android:textColor="#FFF"        app:layout_constraintBottom_toTopOf="@+id/textView3"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.5"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toTopOf="parent"        app:layout_constraintVertical_bias="0.0"        app:layout_constraintVertical_chainStyle="packed" />    <TextView        android:id="@+id/textView2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="16dp"        android:text="Information"        app:fontFamily="sans-serif-smallcaps"        android:textColor="#FFF"        app:layout_constraintBottom_toTopOf="@+id/textView4"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.5"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toBottomOf="@+id/view" />    <TextView        android:id="@+id/textView4"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="16dp"        android:text="Directions"        app:fontFamily="sans-serif-smallcaps"        android:textColor="#FFF"        app:layout_constraintBottom_toTopOf="@+id/textView5"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.5"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toBottomOf="@+id/textView2" />    <TextView        android:id="@+id/textView5"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="16dp"        android:text="Sight-Seeing"        app:fontFamily="sans-serif-smallcaps"        android:textColor="#FFF"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.5"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toBottomOf="@+id/textView4" />    <View        android:id="@+id/view"        android:background="#c2c1c1"        android:layout_width="100dp"        android:layout_height="1dp"        android:layout_marginTop="16dp"        app:layout_constraintBottom_toTopOf="@+id/textView2"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.5"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toBottomOf="@+id/textView3" />    <TextView        android:id="@+id/textView3"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="California"        android:textColor="#FFF"        app:fontFamily="cursive"        app:layout_constraintBottom_toTopOf="@+id/view"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintHorizontal_bias="0.5"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toBottomOf="@+id/textView" />com.google.androidstudio.motionlayoutexample.utils.DrawerContent>

然后我们编写layoutDescription文件,来定义每个TextView的动画结构:

<?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="250" />    <ConstraintSet android:id="@+id/start">        <Constraint            android:id="@+id/textView"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="32dp"            android:rotation="90"            android:translationX="100dp"            motion:layout_constraintBottom_toTopOf="@+id/textView3"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toTopOf="parent"            motion:layout_constraintVertical_chainStyle="spread" />        <Constraint            android:id="@+id/textView2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="16dp"            android:rotation="90"            android:translationX="100dp"            motion:layout_constraintBottom_toTopOf="@+id/textView4"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/view" />        <Constraint            android:id="@+id/textView4"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="16dp"            android:rotation="90"            android:translationX="100dp"            motion:layout_constraintBottom_toTopOf="@+id/textView5"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView2" />        <Constraint            android:id="@+id/textView5"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="16dp"            android:rotation="90"            android:translationX="100dp"            motion:layout_constraintBottom_toBottomOf="parent"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView4" />        <Constraint            android:id="@+id/view"            android:layout_width="100dp"            android:layout_height="1dp"            android:layout_marginTop="16dp"            android:rotation="90"            android:translationX="100dp"            motion:layout_constraintBottom_toTopOf="@+id/textView2"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView3" />        <Constraint            android:id="@+id/textView3"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:rotation="90"            android:translationX="100dp"            motion:layout_constraintBottom_toTopOf="@+id/view"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView" />    ConstraintSet>    <ConstraintSet android:id="@+id/end">        <Constraint            android:id="@+id/textView"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="32dp"            motion:layout_constraintBottom_toTopOf="@+id/textView3"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintHorizontal_bias="0.5"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toTopOf="parent"            motion:layout_constraintVertical_bias="0.0"            motion:layout_constraintVertical_chainStyle="packed" />        <Constraint            android:id="@+id/textView2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="16dp"            motion:layout_constraintBottom_toTopOf="@+id/textView4"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintHorizontal_bias="0.5"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/view" />        <Constraint            android:id="@+id/textView4"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="16dp"            motion:layout_constraintBottom_toTopOf="@+id/textView5"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintHorizontal_bias="0.5"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView2" />        <Constraint            android:id="@+id/textView5"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="16dp"            motion:layout_constraintBottom_toBottomOf="parent"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintHorizontal_bias="0.5"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView4" />        <Constraint            android:id="@+id/view"            android:layout_width="100dp"            android:layout_height="1dp"            android:layout_marginTop="16dp"            motion:layout_constraintBottom_toTopOf="@+id/textView2"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintHorizontal_bias="0.5"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView3" />        <Constraint            android:id="@+id/textView3"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            motion:layout_constraintBottom_toTopOf="@+id/view"            motion:layout_constraintEnd_toEndOf="parent"            motion:layout_constraintHorizontal_bias="0.5"            motion:layout_constraintStart_toStartOf="parent"            motion:layout_constraintTop_toBottomOf="@+id/textView" />    ConstraintSet>MotionScene>

MotionLayout 与 ViewPager

类似的,我们可以实现一个非常有趣的ViewHeader头布局信息:

我们可以使用子类记录当前位置的窍门实现ViewPager的架构:

package com.google.androidstudio.motionlayoutexample.utilsimport android.content.Contextimport android.support.constraint.motion.MotionLayoutimport android.support.v4.view.ViewPagerimport android.util.AttributeSetclass ViewpagerHeader @JvmOverloads constructor(        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr), ViewPager.OnPageChangeListener {    override fun onPageScrollStateChanged(state: Int) {    }    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {        var numPages = 3        progress = (position + positionOffset) / (numPages - 1)    }    override fun onPageSelected(position: Int) {    }}

这个数学非常的直接,在onPageScrolled方法中给我们一个位置position,然后加上一个positionOffset,运动的偏移量,就可以实现我们的动画了。那么动画完成度progress 可以描述为:

progress = (position + positionOffset) / (numPages-1)

MotionLayout 与 Lottie

Lottie动画框架是Airbnb出品的动画框架(旨在解决复杂的动画问题),官网为:https://github.com/airbnb/lottie-android
通过设置动画的完成度可以直接打到效果:
Android新控件MotionLayout介绍(三)_第2张图片
直接在ViewPagerHeader中定义这个LottieAnimationView,就可以直接打到效果了。

<?xml version="1.0" encoding="utf-8"?><com.google.androidstudio.motionlayoutexample.utils.ViewpagerHeader xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:id="@+id/motionLayout"    app:layoutDescription="@xml/scene_23"    android:layout_width="match_parent"    app:progress="0"    android:layout_height="230dp">    <com.airbnb.lottie.LottieAnimationView        android:id="@+id/animation_view"        android:layout_width="match_parent"        android:layout_height="match_parent"        app:lottie_rawRes="@raw/walkthrough"/>com.google.androidstudio.motionlayoutexample.utils.ViewpagerHeader>

通过使用motion:progress的属性来控制LottieAnimationView的动画播放:

<Constraint    android:id="@+id/animation_view"    android:layout_width="match_parent"    android:layout_height="match_parent"    motion:progress="0"/>

LottieAnimationView有一个setProgress()的功能,可以直接被MotionLayout作用,从而播放出动画的。
scene_23.xml 动画的结构如下:

<?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:constraintSetStart="@+id/start"        motion:constraintSetEnd="@+id/end">    Transition>    <ConstraintSet android:id="@+id/start">        <Constraint            android:id="@+id/animation_view"            android:layout_width="match_parent"            android:layout_height="match_parent"            motion:progress="0"/>    ConstraintSet>    <ConstraintSet android:id="@+id/end">        <Constraint            android:id="@+id/animation_view"            android:layout_width="match_parent"            android:layout_height="match_parent"            motion:progress="1"/>    ConstraintSet>    MotionScene>

结论

通过上面的例子,我们可以很容易的将Motionlayout集成到你已有的项目中了。
例子的中所有代码都可以在https://github.com/googlesamples/android-ConstraintLayoutExamples中找到,
初次翻译文章,有不好的地方请多见谅。

更多相关文章

  1. 后台动态添加布局文件、控件与动态设置属性
  2. 安卓的下拉控件Spinner
  3. 动画效果编程基础--AnimationAndroid
  4. 淡入淡出动画
  5. 上接系出名门Android(8) - 控件(View)之TextSwitcher, Gallery,
  6. Android 动画监听器
  7. android —— 自定义控件 竖向的ViewPager,上下滑动的组件,android
  8. 【特效】【自定义控件】关键字飘飞效果
  9. android tween动画

随机推荐

  1. android:screenOrientation的取值含义
  2. android中pull解析xml和生成xml
  3. Android(安卓)开发(04)高级UI组件
  4. [置顶] Android(安卓)LockScreen
  5. Android(安卓)技术专题系列之二 -- telepho
  6. Android(安卓)省市县 三级联动(android-wh
  7. An error has occurred. See error log f
  8. Android(安卓)图片处理之图片叠加--Bitma
  9. 启动android project时报resources.ap_ d
  10. Android(安卓)Camera子系统之进程/文件Vi