MotionLayout系列之配合布局CoordinatorLayout, DrawerLayout, ViewPager使用

作者 : 开心源码 本文共16691个字,预计阅读时间需要42分钟 发布时间: 2022-05-12 共226人阅读

在前量部分我们引入了几个示例:

  • 基础使用

  • 滑动解决

  • 自己设置属性插值

  • 关键帧

在一,二部分已经大量的详情了 MotionLayout 的功能,在这部分我们将详情如何在已有的项目中使用 MotionLayout ,整合到已有的布局中(CoordinatorLayout, DrawerLayout, ViewPager)。

在 Coordinatorlayout 中使用 MotionLayout:

( MotionLayout 可以实现相似 CoodinatorLayout 的功能,我们将在以后的文章中提供示例)

可以通过 MotionLayout 指定一部分 View 的动画,将更多有趣的动画加到已经存在的布局中。

例如,下图中的效果:

可以使用 MotionLayout 替换 AppBarLayout 中的 Toolbar , 通过CoordinatorLayout 控制动画的进度。虽然你可以通过 setProgress() 方法控制 MotionLayout 转换的进度,也可以创立一个子类通过监听 AppBarlayout 的偏移自动修改:

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)    }}

而后在布局文件中使用:

<?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 的内容,一个 ImagerView作为背景和一个 TextView:

<?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>

创立自包含的 MotionScene 文件:

<?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>

DrawerLayout布局中使用MotionLayout

DrawerLayout 是安卓项目中一个提供侧抽屉功能的布局文件。不同于普通的 menu ,我们可以构建一个更有趣的动画:

CoordinatorLayout 类型我们也创立一个子类在自动的设置 MotionaLayout 的进度:

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)    }}

DrawerContent 将通过 onDrawerSlide 的回调slideOffset 自动设置进度,将 这个类整合进 DrawerLayout 布局中 :

<?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文件:

<?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>

MotionScene 文件:

<?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>

在 ViewPager 中使用 MotionLayout

同样我们想在 ViewPager 的头部增加更有趣的动画:

我们可以用一个小技巧来整合 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() 方法传入了位置,位置的变化从02,一共有三个界面。所以进度的计算应该是: progress = (position + positionOffset) / (numPages-1)

结合 MotionLayout 和 Lottie

前面的示例使用多个 ImageView 实现动画。也可以通过使用 Lottie文件整合进 MotionLayout 中直接设置进度。让我们使用 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>

MotionScene 中关键参数是 motion:progress :

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

由于 LottieAnimationView 存在 setProgress() 方法,所以我们可以在 MotionLayout 中调用它来控制 Lottie 的进度。完整的 MotionScene 文件:

<?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>

读者福利限时分享

Android开发资料+面试架构资料 免费分享 点击链接 就可领取

《Android架构师必备学习资源免费领取(架构视频+面试专题文档+学习笔记)》

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » MotionLayout系列之配合布局CoordinatorLayout, DrawerLayout, ViewPager使用

发表回复