MotionLayout动画使用参考

MotionLayout

ConstraintLayout的子类,是一种可以管理应用中的运动和微件动画的布局类型,关联手势和组件动画。
支持在XML中完全描述一个复杂的动画。

常规使用

依赖

implementation ‘androidx.constraintlayout:constraintlayout:2.0.0-rc1’

布局

<androidx.constraintlayout.motion.widget.MotionLayout 
    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/layout_motion_demo_scene"
    app:motionDebug="SHOW_PATH"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        android:textSize="12dp"/>
          
   <View
        android:id="@+id/view"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</androidx.constraintlayout.motion.widget.MotionLayout>

不需要添加效果的子视图按需要的位置添加约束,有效果的可以在描述文件中声明,MotionLayout不会修改未被Constraint引用修饰的视图。

运动描述

layoutDescription

<?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"
        motion:motionInterpolator="linear"
        motion:duration="2000">

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button"
            motion:layout_constraintStart_toStartOf="parent"
            android:layout_marginLeft="8dp"
            android:layout_marginStart="8dp"
            motion:layout_constraintBottom_toBottomOf="parent" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/button"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent">
        </Constraint>
    </ConstraintSet>
</MotionScene>

自动生成的默认xml描述运动的文件,主要标签 <Transition、、。​
Transition:运动行为过渡的描述,每个MotionScene都至少有一个,包括起始及结束位置,时间。
motionInterpolator添加过渡动画的插值器:

  • linear:线性
  • easeIn:缓入
  • easeOut:缓出
  • easeInOut:缓入缓出
  • bounce:弹簧

也可以直接使用布局作为约束:

<Transition
        motion:constraintSetStart="@layout/layout_motion_demo"
        motion:constraintSetEnd="@layout/layout_motion_demo"
        motion:motionInterpolator="linear"
        motion:duration="2000">

ConstraintSet…:描述 子视图们 初始位置的约束、结束位置的约束,应用于动画开始、结束。
Constraint…: 子视图各自约束位置描述。

动画编辑器

AS提供了MotionLayout动画的界面工具,基本所有效果都可以界面操作完成。
在这里插入图片描述

点击选中start、end,下部会出现相应的约束列表,包含视图中所有子视图,
点击画笔可以添加约束或者删除约束。
点击上部箭头连线开启预览视图
在这里插入图片描述
右边为创建关键帧按钮。

Attributes视图可以调整约束代码,避免手写。

右击预览视图可以自动将ConstraintLayout 转化 MotionLayout
在这里插入图片描述

开启动画

基于点击事件响应

通过编辑器添加

在这里插入图片描述

选择动画响应点击事件的控件
在这里插入图片描述

xml中会生成

 <OnClick motion:targetId="@+id/button" />

app:targetId:设置触发动画的view的Id,前缀可以使用@+id、@id;
app:clickAction:

 <attr name="clickAction">
            <flag name="toggle" value="0x0011"/> //切换
            <flag name="transitionToEnd" value="0x0001"/> //过渡到end
            <flag name="transitionToStart" value="0x0010"/>
            <flag name="jumpToEnd" value="0x100"/> //无过渡切换到end
            <flag name="jumpToStart" value="0x1000"/>
        </attr>

基于拖动事件响应

MotionLayout可以跟踪用户拖动事件或滑动,以创建基于物理的“fling”动画。

在这里插入图片描述


在这里插入图片描述

xml生成

 <OnSwipe
            motion:touchAnchorId="@+id/button"
            motion:touchAnchorSide="right"
            motion:dragDirection="dragRight" />

touchAnchorId:拖动动作关联的子视图对象;
dragDirection:拖动的方向

 <attr format="enum" name="dragDirection">
        <enum name="dragUp" value="0"/> //由下到上
        <enum name="dragDown" value="1"/>
        <enum name="dragLeft" value="2"/>
        <enum name="dragRight" value="3"/>
        <enum name="dragStart" value="4"/>
        <enum name="dragEnd" value="5"/>
        <enum name="dragClockwise" value="6"/>
        <enum name="dragAnticlockwise" value="7"/>
    </attr>

touchAnchorSide:设置触摸操作将会拖动对象的哪一边。
MotionLayout监听屏幕布局内任何地方的拖动事件,保持touchAnchorSide的一边和手指距离不变。

场景如:折叠效果
xml布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/layout_tuoch_demo_scene">

    <FrameLayout
        android:id="@+id/fl_top"
        android:background="@color/black"
        android:layout_width="match_parent"
        android:layout_height="0dp">
    </FrameLayout>


    <FrameLayout
        android:id="@+id/fl_center"
        android:background="#FF4C7F"
        android:layout_width="50dp"
        android:layout_height="50dp">

    </FrameLayout>


    <FrameLayout
        android:id="@+id/fl_bottom"
        android:layout_width="match_parent"
        android:background="@color/black"
        android:layout_height="0dp"/>

</androidx.constraintlayout.motion.widget.MotionLayout>

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:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">

        <OnSwipe
            motion:touchAnchorId="@+id/fl_bottom"
            motion:touchAnchorSide="top"
            motion:dragDirection="dragUp">
        </OnSwipe>


    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/fl_top"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toTopOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintEnd_toEndOf="parent" />
        <Constraint
            android:id="@+id/fl_bottom"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintTop_toBottomOf="parent"
            android:layout_width="match_parent"
            android:layout_height="0dp" />

        <Constraint
            android:id="@+id/fl_center"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            android:scaleX="1"
            android:scaleY="1"
            android:layout_width="50dp"
            android:layout_height="50dp" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/fl_bottom"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_width="match_parent"
            android:layout_height="120dp" />

        <Constraint
            android:id="@+id/fl_top"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            android:layout_width="match_parent"
            android:layout_height="60dp" />

        <Constraint
            android:id="@+id/fl_center"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            android:scaleY="0"
            android:scaleX="0"
            android:layout_width="50dp"
            android:layout_height="50dp" />
    </ConstraintSet>
</MotionScene>

动画路径

xml布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:motionDebug="SHOW_PATH" //开启路径调试
    app:layoutDescription="@xml/layout_scene_path_demo_scene">

    <ImageView
        android:id="@+id/iv"
        android:src="@drawable/icon_1"
        android:layout_width="50dp"
        android:layout_height="50dp"/>
    
    <ImageView
        android:id="@+id/iv2"
        android:src="@drawable/icon_1"
        android:layout_width="50dp"
        android:layout_height="50dp"/>

</androidx.constraintlayout.motion.widget.MotionLayout>
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:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <KeyFrameSet>

        </KeyFrameSet>
    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/iv"
            android:layout_width="50dp"
            android:layout_height="50dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            android:layout_marginLeft="20dp"
            android:layout_marginStart="20dp"
            android:layout_marginBottom="20dp" />

        <Constraint
            android:id="@+id/iv2"
            android:layout_width="50dp"
            android:layout_height="50dp"
            motion:layout_constraintStart_toStartOf="parent"
            android:layout_marginLeft="20dp"
            android:layout_marginStart="20dp"
            motion:layout_editor_absoluteY="20dp"
            motion:layout_constraintBottom_toTopOf="@+id/iv"
            android:layout_marginBottom="50dp" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/iv"
            android:layout_width="50dp"
            android:layout_height="50dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_editor_absoluteY="661dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginRight="20dp"
            android:layout_marginEnd="20dp"
            android:layout_marginBottom="20dp" />
        <Constraint
            android:id="@+id/iv2"
            android:layout_width="50dp"
            android:layout_height="50dp"
            motion:layout_constraintEnd_toEndOf="parent"
            android:layout_marginRight="20dp"
            android:layout_marginEnd="20dp"
            motion:layout_editor_absoluteY="20dp"
            motion:layout_constraintBottom_toTopOf="@+id/iv"
            android:layout_marginBottom="50dp" />
    </ConstraintSet>
</MotionScene>

上例默认路径为 起始约束位置到 结束约束位置
![image.png](https://img-blog.csdnimg.cn/img_convert/f67085de3b718371e9c2e266f9264581.png#clientId=udab1f27c-7db4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=119&id=ud5a7a3ce&margin=[object Object]&name=image.png&originHeight=177&originWidth=376&originalType=binary&ratio=1&rotation=0&showTitle=false&size=9300&status=done&style=none&taskId=uaa17e181-a151-4e89-92fb-7d426968b8d&title=&width=252)

KeyFrameSet

关键帧:对两个状态之间的插值进行一个修改,是临时的修改,ConstraintSet是静止的状态。

  1. 位置关键帧 KeyPosition
  2. 属性关键帧 KeyAttribute
  3. 循环关键帧 KeyCycle
  4. 周期关键帧 KeyTimeCycle

KeyPosition

如把其中一个视图的动画路径改为一个向上的抛物曲线
Transition标签作修改

<Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">

        <KeyFrameSet>
            <KeyPosition
                motion:motionTarget="@+id/iv"  //受影响的目标
                motion:framePosition="50"  //作用时机(0-100)
                motion:keyPositionType="parentRelative" //坐标系
                motion:transitionEasing="linear" //差值器,默认linear
                motion:percentX="0.5"   // 坐标位置
                motion:percentY="0.5" />
        </KeyFrameSet>
    </Transition>

路径预览:
![image.png](https://img-blog.csdnimg.cn/img_convert/9dfb5ee391c1ddc85fe7ada65455a3b4.png#clientId=udab1f27c-7db4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=167&id=u9a55a90c&margin=[object Object]&name=image.png&originHeight=227&originWidth=200&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4675&status=done&style=none&taskId=ud08efbef-dfdc-4483-905b-2c86c828028&title=&width=147)

坐标系

parentRelative

![a7b7568d46d9dec7.png](https://img-blog.csdnimg.cn/img_convert/4c28b14856d2e6c627ea2395e68eb5db.png#clientId=u078a1b73-f90e-4&crop=0&crop=0&crop=1&crop=1&from=drop&height=390&id=ub6895cc5&margin=[object Object]&name=a7b7568d46d9dec7.png&originHeight=1600&originWidth=1594&originalType=binary&ratio=1&rotation=0&showTitle=false&size=64298&status=done&style=none&taskId=u39eb523e-693a-46a9-96ad-2b5aa1415e9&title=&width=389)
父相对坐标系,左上(0,0),右下(1,1),使用较多。

deltaRelative

![52.png](https://img-blog.csdnimg.cn/img_convert/80ca70af18e66d9e7f67580fed7571f4.png#clientId=u078a1b73-f90e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=393&id=uedfaaaab&margin=[object Object]&name=52.png&originHeight=1600&originWidth=1583&originalType=binary&ratio=1&rotation=0&showTitle=false&size=66092&status=done&style=none&taskId=u980fbb72-22a4-4730-ad05-9b4dbdfc067&title=&width=389)
左下为起点(0,0),右上为终点(1,1)。
场景:单独控制水平或垂直运动

<Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <KeyFrameSet >
            <KeyPosition
                motion:motionTarget="@+id/iv"
                motion:framePosition="50"
                motion:keyPositionType="deltaRelative"
                motion:percentY="0"
                motion:percentX="0.3" />
        </KeyFrameSet>
    </Transition>

目标视图iv会水平移动前半程再向上进行原先的路径到达结束位置。

pathRelative

![path.png](https://img-blog.csdnimg.cn/img_convert/0bd7ed66b68e5ed4f5915df87c4eee1a.png#clientId=u078a1b73-f90e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=319&id=u97f490c6&margin=[object Object]&name=path.png&originHeight=1310&originWidth=1600&originalType=binary&ratio=1&rotation=0&showTitle=false&size=150658&status=done&style=none&taskId=u4a567ae9-67f7-43b2-9754-7008b83bbf0&title=&width=390)
运动路径为X 轴,起点(0,0),终点(1,0);
场景:在动画的一部分期间加速、减速或停止视图,配合viewPager滑动展示动画等;

<Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <KeyFrameSet >
            <KeyPosition
                motion:motionTarget="@+id/iv"
                motion:framePosition="50"
                motion:keyPositionType="pathRelative"
                motion:percentX="0.3" />
        </KeyFrameSet>
    </Transition>

一半的时间运行前30%的动作。

 S型曲线运动:
 <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <KeyFrameSet >
            <KeyPosition
                motion:motionTarget="@+id/iv"
                motion:framePosition="25"
                motion:keyPositionType="pathRelative"
                motion:percentY="0.25"
                motion:percentX="0.25" />
            <KeyPosition
                motion:motionTarget="@+id/iv"
                motion:framePosition="75"
                motion:keyPositionType="pathRelative"
                motion:percentY="-0.25"
                motion:percentX="0.75" />
        </KeyFrameSet>
    </Transition>

官方提供的效果实例

自定属性修改

标签CustomAttribute

  <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/iv"
            android:layout_width="50dp"
            android:layout_height="50dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            android:layout_marginLeft="20dp"
            android:layout_marginStart="20dp"
            android:layout_marginBottom="20dp" >

            <CustomAttribute
                motion:attributeName="backgroundColor"  //自定义属性名称 需设置set/get方法 字体大小颜色透明度等 
                motion:customColorValue="#D81B60" />   // 属性值
        </Constraint>

customColorValue的类型

 <declare-styleable name="CustomAttribute">
        <attr format="string" name="attributeName"/>
        <attr format="string" name="methodName"/>
        <attr format="color" name="customColorValue"/>
        <attr format="color" name="customColorDrawableValue"/>
        <attr format="integer" name="customIntegerValue"/>
        <attr format="float" name="customFloatValue"/>
        <attr format="string" name="customStringValue"/>
        <attr format="dimension" name="customDimension"/>
        <attr format="dimension" name="customPixelDimension"/>
        <attr format="boolean" name="customBoolean"/>
        <attr format="reference" name="customReference"/>
    </declare-styleable>

针对不同控件有特殊使用方法场景,如ImageFilterView实现的各种图片过度;

关于Carousel

是一个MotionHelper类,只能在MontionLayout布局中使用,通常在涉及一些轮播的效果中使用;
使用Carousel的话需要描述三种状态约束:start、previous 和 next,轮播是双向的,初始状态,每次轮播前和后的状态;
对于一个横向的轮播视图:
![carousel-ex3.png](https://img-blog.csdnimg.cn/img_convert/5719024ac3ccd21beda427ed5169d0b8.png#clientId=u247703ad-81a4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=425&id=ua2989578&margin=[object Object]&name=carousel-ex3.png&originHeight=621&originWidth=1085&originalType=binary&ratio=1&rotation=0&showTitle=false&size=65234&status=done&style=none&taskId=u525d4562-2c01-483f-b949-b93dc32bc04&title=&width=743)
还需要加forward和backward两种转换,用forward对应的Transition表示 start - previous 的向左滑动的过渡动画,用 backward 对应的 Transition 表示 start - next 的向右滑动的过渡动画

 <Transition
        motion:constraintSetStart="@id/start"
        motion:constraintSetEnd="@+id/next"
        motion:duration="1000"
        android:id="@+id/forward">
        <OnSwipe
            motion:dragDirection="dragLeft"
            motion:touchAnchorSide="left" />

    </Transition>

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/previous"
        android:id="@+id/backward">
        <OnSwipe
            motion:dragDirection="dragRight"
            motion:touchAnchorSide="right" />

    </Transition>

预览视图也会变成这样:
![image.png](https://img-blog.csdnimg.cn/img_convert/5522c0a82920a341b7b7107130269e1a.png#clientId=u247703ad-81a4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=260&id=ue3f7b523&margin=[object Object]&name=image.png&originHeight=260&originWidth=532&originalType=binary&ratio=1&rotation=0&showTitle=false&size=11774&status=done&style=none&taskId=ub37d4c6a-ed7b-479e-a907-74b5b9515c5&title=&width=532)

如实现网上的这个效果:
![](https://img-blog.csdnimg.cn/img_convert/7d8aab05f699b7bd7add1fdee456b50b.gif#clientId=u247703ad-81a4-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9442c03f&margin=[object Object]&originHeight=712&originWidth=400&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u603c5e18-7f7c-4574-96f3-4dc8e1c91a3&title=)

xml布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout 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"
    app:layoutDescription="@xml/card_motion_scence"
    tools:context=".CardMotionActivity">

    <ImageView
        android:id="@+id/iv0"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:alpha="0"
        android:scaleType="centerCrop"
        android:scaleX="0.5"
        android:scaleY="0.5"
        android:src="@drawable/card1"
        android:translationY="-110dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="@+id/gl1"
        app:layout_constraintRight_toRightOf="@+id/gl2"
        app:layout_constraintTop_toTopOf="parent" />


    <ImageView
        android:id="@+id/iv1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:scaleX="0.6"
        android:scaleY="0.6"
        android:src="@drawable/card1"
        android:translationY="-90dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="@+id/gl1"
        app:layout_constraintRight_toRightOf="@+id/gl2"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:scaleX="0.7"
        android:scaleY="0.7"
        android:src="@drawable/card2"
        android:translationY="-70dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="@+id/gl1"
        app:layout_constraintRight_toRightOf="@+id/gl2"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:scaleX="0.8"
        android:scaleY="0.8"
        android:src="@drawable/card3"
        android:translationY="-50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="@+id/gl1"
        app:layout_constraintRight_toRightOf="@+id/gl2"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv4"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:scaleX="0.9"
        android:scaleY="0.9"
        android:src="@drawable/card4"
        android:translationY="-30dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="@+id/gl1"
        app:layout_constraintRight_toRightOf="@+id/gl2"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv5"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:scaleX="1.0"
        android:scaleY="1.0"
        android:src="@drawable/card5"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="@+id/gl1"
        app:layout_constraintRight_toRightOf="@+id/gl2"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv6"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/card5"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="@+id/gl1"
        app:layout_constraintRight_toRightOf="@+id/gl2"
        app:layout_constraintBottom_toTopOf="parent" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/gl1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.2" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/gl2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.8" />


</androidx.constraintlayout.motion.widget.MotionLayout>

通过设置缩放偏移量达到重叠的效果,
![image.png](https://img-blog.csdnimg.cn/img_convert/1b687c63193ad8d4608f09f86d8c7c0c.png#clientId=u247703ad-81a4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=490&id=ue6d1b16c&margin=[object Object]&name=image.png&originHeight=490&originWidth=305&originalType=binary&ratio=1&rotation=0&showTitle=false&size=23628&status=done&style=none&taskId=u16f6100d-ddfc-4146-b78f-78df393a1db&title=&width=305)
每个ImagView仅仅偏移量和缩放比列不同,在定义描述文件时也通过改变这两个属性设置每个视图的约束即可,

<?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
        android:id="@+id/forward"
        motion:constraintSetEnd="@+id/next"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <OnSwipe
            motion:dragDirection="dragUp"
            motion:touchAnchorSide="left" />

    </Transition>

    <Transition
        android:id="@+id/backward"
        motion:constraintSetEnd="@+id/previous"
        motion:constraintSetStart="@+id/start">
        <OnSwipe
            motion:dragDirection="dragDown"
            motion:touchAnchorSide="right" />
    </Transition>

    <ConstraintSet android:id="@+id/previous">

        <Constraint
            android:id="@+id/iv1"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:alpha="0"
            android:scaleX="0.5"
            android:scaleY="0.5"
            android:translationY="-110dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.6"
            android:scaleY="0.6"
            android:translationY="-90dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv3"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.7"
            android:scaleY="0.7"
            android:translationY="-70dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv4"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.8"
            android:scaleY="0.8"
            android:translationY="-50dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv5"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.9"
            android:scaleY="0.9"
            android:translationY="-30dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv6"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="1.0"
            android:scaleY="1.0"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>


    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/iv0"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:alpha="0"
            android:scaleX="0.5"
            android:scaleY="0.5"
            android:translationY="-110dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv1"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.6"
            android:scaleY="0.6"
            android:translationY="-90dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.7"
            android:scaleY="0.7"
            android:translationY="-70dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv3"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.8"
            android:scaleY="0.8"
            android:translationY="-50dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv4"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.9"
            android:scaleY="0.9"
            android:translationY="-30dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv5"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="1.0"
            android:scaleY="1.0"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv6"
            android:layout_width="0dp"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toTopOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2" />

    </ConstraintSet>

    <ConstraintSet android:id="@+id/next">

        <Constraint
            android:id="@+id/iv0"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.6"
            android:scaleY="0.6"
            android:translationY="-90dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv1"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.7"
            android:scaleY="0.7"
            android:translationY="-70dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.8"
            android:scaleY="0.8"
            android:translationY="-50dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv3"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="0.9"
            android:scaleY="0.9"
            android:translationY="-30dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv4"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:scaleX="1.0"
            android:scaleY="1.0"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@+id/iv5"
            android:layout_width="0dp"
            android:layout_height="0dp"
            motion:layout_constraintBottom_toTopOf="parent"
            motion:layout_constraintDimensionRatio="5:3"
            motion:layout_constraintLeft_toLeftOf="@+id/gl1"
            motion:layout_constraintRight_toRightOf="@+id/gl2" />

    </ConstraintSet>

</MotionScene>

![image.png](https://img-blog.csdnimg.cn/img_convert/be87a330139769aa8e32c1cf6279aa2d.png#clientId=u247703ad-81a4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=453&id=u4dc6e181&margin=[object Object]&name=image.png&originHeight=453&originWidth=332&originalType=binary&ratio=1&rotation=0&showTitle=false&size=15463&status=done&style=none&taskId=uae07ac73-8bdd-4488-b79b-4e3a1145ded&title=&width=332)
在previous约束中把iv6变成start中的iv5位置,iv1变成初始iv0的位置,其余也都是位置互换。

carsoul布局中的设置:


    <androidx.constraintlayout.helper.widget.Carousel
        android:id="@+id/carousel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:carousel_forwardTransition="@+id/forward"    //start - next
        app:carousel_backwardTransition="@+id/backward"
        app:carousel_nextState="@+id/previous"  //next id 
        app:carousel_previousState="@+id/next"
        app:carousel_infinite="true"   //是否是无限轮播
        app:carousel_firstView="@+id/iv1"  //展示第一个数据的视图
        app:constraint_referenced_ids="iv0,iv1,iv2,iv3,iv4,iv5,iv6"
        />

设置Adapter

 public interface Adapter {
        /**
         * Number of items you want to display in the Carousel
         * @return number of items
         */
        int count();

        /**
         * Callback to populate the view for the given index
         *
         * @param view
         * @param index
         */
        void populate(View view, int index);

        /**
         * Callback when we reach a new index
         * @param index
         */
        void onNewItem(int index);
    }

官方文档提供了一个很酷炫的效果。
Carousel官方实例效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值