内容简介:在本系列的第一部分和第二部分中,我们介绍了 MotionLayout,并给出了各种示例:第一部分和第二部分概述了 MotionLayout 中引入的基本概念。现在将介绍如何在现有应用程序中使用 MotionLayout,并将其集成到现有布局(如 CoordinatorLayout,DrawerLayout或ViewPager)中。
在本系列的第一部分和第二部分中,我们介绍了 MotionLayout,并给出了各种示例:
- basic motion
- swipe handling
- custom attribute interpolation
- keyframes
第一部分和第二部分概述了 MotionLayout 中引入的基本概念。
现在将介绍如何在现有应用程序中使用 MotionLayout,并将其集成到现有布局(如 CoordinatorLayout,DrawerLayout或ViewPager)中。
配合 CoordinatorLayout 使用
(注意,MotionLayout 可以用来实现类似于 CoordinatorLayout的行为。我们将在下一篇文章中展示这些例子)
利用 MotionLayout 的一个简单方法让屏幕中的部分内容做指定动画。通过这种方式,你可以向应用程序中的现有布局添加更多有趣的运动,而不必从头开始。
例如,你想要的效果可能如下:
这里的基本思路是用 MotionLayout 替换 AppBarLayout 中的 Toolbar 元素。然后让 CoordinatorLayout 驱动动画进度。
由于你可以通过调用 setProgress()
来控制 MotionLayout 的过渡进度,因此我们可以创建一个简单的子类,通过监听 AppBarLayout 偏移量来跟踪改变:
package com.google.androidstudio.motionlayoutexample.utils import android.content.Context import android.support.constraint.motion.MotionLayout import android.support.design.widget.AppBarLayout import android.util.AttributeSet class 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 XML 文件中的 Toolbar。
<?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> 复制代码
接下来唯一要做的就是创建一个包含了我们想要动画的控件的 MotionLayout 文件。这里我们有一个 作为背景的 ImageView 和一个 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 使用
DrawerLayout 是另一个 Android framework 中的类,用于打开一个侧边栏。
相比通常的菜单,我们可能希望来一些更有趣的东西:
和把 MotionLayout 整合到 CoordinatorLayout 中类似,我们需要创建一个设置 MotionLayout 进度的子类:
package com.google.androidstudio.motionlayoutexample.utils import android.content.Context import android.support.constraint.motion.MotionLayout import android.support.v4.widget.DrawerLayout import android.util.AttributeSet import android.view.View class 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()
回调来跟踪过渡进度。
使用这个子类,我们可以很容易地在DrawerLayout中集成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> 复制代码
这个 xml
文件像我们前面 CoordinatorLayout 例子一样,包含了几个简单控件。
下面是一个使用了 MotionLayout 的菜单文件 (menu file):
<?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 文件只是旋转不同的元素 (检查 rotation
属性)
<?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> 复制代码
在 ViewPager中使用
类似的,我们可能希望有一个有趣的 ViewPager 。
我们也可以使用类似的技巧来集成 ViewPager 。创建一个子类来传递当前的位置。
package com.google.androidstudio.motionlayoutexample.utils import android.content.Context import android.support.constraint.motion.MotionLayout import android.support.v4.view.ViewPager import android.util.AttributeSet class 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()
给我们提供了页面的位置索引(我们有三个页面的时候,这里是0,1或2),偏移量(对应偏移位置从0到1)。动画的进度更新我们可以通过下面的公式来得到:
progress = (position + positionOffset) / (numPages-1)
配合 Lottie 使用
前面的例子用了简单的图片来作为头部。你也可以 Lottie集成到你的 MotionLayout中,然后直接设置它的进度从而播放它。
让我们把上一个例子改成 LottieAnimationView:
简单起见,我们把基于 MotionLayout 的 ViewPager Header 改成只包含一个 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> 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- ASP.NET Core模块化前后端分离快速开发框架介绍之3、数据访问模块介绍
- 简编漫画介绍WebAssembly
- CGroup 介绍
- CGroup 介绍
- vue初步介绍
- Microbit MicroPython 介绍
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。