ConstraintSet MotionLayout

看这里学习的https://www.jianshu.com/p/54a6e2568cdd#comment-28749841
class ConstraintSet
class MotionLayout

demo

ConstraintSet

这东西就是一堆view的约束条件的集合
实例化的方法

//Manually 
c = new ConstraintSet(); c.connect(....);
//from a R.layout.* object 
c.clone(context, R.layout.layout1);
//看下这方法可以看到上边的layout1布局,根元素需要是ConstraintLayout
    public void clone(Context context, int constraintLayoutId) {
        this.clone((ConstraintLayout)LayoutInflater.from(context).inflate(constraintLayoutId, (ViewGroup)null));
    }
//from a ConstraintLayout 
c.clone(clayout);

//还有这种弄一个布局的,当然了这个布局不要求根元素是ConstraintLayout
constraintSet.load(this, R.layout.keyframe_two); 

顺道简单看下这个几个方法的源码,可以看到这里的布局或者ConstraintLayout里的所有child都必须设置id


image.png
理解

ConstraintSet 怎么说了,这东西可以理解成我们以前比如线性布局,相对布局的LayoutParams属性的集合。
嗯嗯,我觉得是差不多。
至于上边的clone,load其实都是从一个布局或者说从一个ConstraintLayout 里把所有的约束条件都提取出来而已。而且上边也说了里边的child必须要有个id,为啥了,是因为它要根据id和我们当前的布局对应起来。
最后这个set的使用方法都是这样的
constraintSet.applyTo(constraintLayout1)
也就是说它把自己从其他地方提取的【clone,load方法等】约束条件,应用到一个老的constraintLayout1上边。

constraintSet只能从布局提取吗?

答案是否定的,看下这个类里边的方法,好多个。
和普通的LayoutParams一样,它也可以手动设置一些属性的

比如有个老的按钮,我们现在给它设置新的属性如下
需要注意,新的属性,必须要保证这个view的具体位置,大小。也就是新的属性得保证这个view在某个位置。下边的,如果你不设置宽高,你会发现applyTo以后,按钮就不见了 。

constraintSet.setMargin(R.id.btn_test,ConstraintSet.LEFT,100)
        constraintSet.setMargin(R.id.btn_test,ConstraintSet.TOP,200)
        constraintSet.constrainHeight(R.id.btn_test,200)
        constraintSet.constrainWidth(R.id.btn_test,100)

比如 public void setGoneMargin(int viewId, int anchor, int value)
我们在xml也可以设置这种属性 app:layout_goneMarginLeft="100dp"
这个anchor的值有6种,LEFT,RIGHT,TOP,BOTTOM,START ,END
方法是干啥的它就具体设置啥的,比如下边的设置goneMargin的


image.png

这个就是设置正常margin的


image.png

比如下边这些2个参数的,很多都是一个id,一个值,看名字也就大概知道干啥了,对应的都是xml的属性
image.png
刚开始的疑问

刚开始看到下边的代码,我还以为是替换布局了,还专门给老的布局的按钮弄个点击事件看有没有改变,后来发现自己想多了,看完上边分析,已经知道了,这里只是从这个布局或者说是constraintLayout里把每个child的约束属性读出来存起来而已,并不是替换这个布局。
之后applyTo就是把set里的约束,根据id找到对应的,然后把约束应用到老的布局上而已。


image.png

MotionLayout

public class MotionLayout extends ConstraintLayout implements NestedScrollingParent3
子类而已,所以父类有的它都有,很明显实现了嵌套滚动

implementation 'androidx.constraintlayout:constraintlayout:2.0.1'

2.0以后的库才有这个东西,这玩意需要一个motionscene的文件来实现动画,最低要求版本是14,也就是这个库的minSdkVersion =14

image.png

大家先看下文章开头的帖子,看完基本入门了,完事再去看上边官方文档,看下各个属性的描述,也就理解的八九不离十了,之后就是自己在demo里测试了

使用过程:按照以前的ConstraintLayout写完,然后换成MotionLayout,完事就会报错,提示你少个layoutDescription参数,按照提示就会自动生成一个xml文件了.

image.png

下边贴一个motionscene文件,这个文件是放在res的xml文件夹下边的,使用的时候是在布局文件里添加layoutDescription标签的.下图是一个最简单的.
总结一下,就是写2套ConstraintSet ,一套是起始位置,一套是结束位置,系统自动给我们添加一个过渡动画。当然,Transition里还可以添加一些关键帧之类的属性,后边讲


image.png

回头再看,下边有图先整体分析下结构,跳到代码后边
~下边代码比较老,有些attribute 换名字了,懒得改了~

<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <Transition
        android:id="@+id/my_transition"
        app:constraintSetEnd="@+id/ending_set"
        app:constraintSetStart="@+id/starting_set"
        app:duration="4000">
        <KeyFrameSet android:id="@+id/frameSet1">
            <KeyPosition

                app:curveFit="arc"

                app:drawPath="path"
                app:framePosition="30"
                app:percentX="0.85"
                app:target="@+id/btn_test"

                app:type="deltaRelative" />
            <KeyPosition

                app:framePosition="60"

                app:percentX="1"

                app:target="@+id/btn_test"

                app:type="deltaRelative" />
            <KeyCycle

                android:rotation="50"
                app:framePosition="50"

                app:target="@+id/btn_test"

                app:wavePeriod="1"

                app:waveShape="square" />
        </KeyFrameSet>
        <OnClick
            app:mode="transitionToEnd"
            app:target="@+id/btn_test" />
        <OnSwipe app:dragDirection="dragDown"
            app:touchAnchorId="@+id/btn_test"
            app:touchAnchorSide="left"/>
    </Transition>
    
    <ConstraintSet android:id="@+id/starting_set">
        <Constraint
            android:id="@+id/btn_test"
            android:layout_width="160dp"
            android:layout_height="60dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/ending_set">
        <Constraint //需要注意,这里要完整的宽高和约束属性额
            android:id="@+id/btn_test"
            android:layout_width="160dp"
            android:layout_height="60dp"
            android:layout_marginTop="100dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>

</MotionScene>
image.png

MotionScene下有3种节点,StateSet试了下没反应,不管它了先


image.png
ConstraintSet

一般这玩意有两个对应上边的Transition标签
每个ConstraintSet里边就是N个Constraint标签了,用来处理想要进行动画的view的位置和大小的。这里的id和我们布局里的id一样的,你想哪个动,就在这里处理他们的位置。

Constraint

Constraint里就是对控件的约束条件,位置大小之类的,
如果要动态改变某个属性,可以用CustomAttribute标签,里边写上名字和值即可

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:layout_marginStart="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="BackgroundColor"
                motion:customColorValue="#D81B60" />
        </Constraint>
    </ConstraintSet>

比如下边这个,如果Constraint里没写alpha,那么默认是不透明的,只有手指开始滑动,下边的CustomAttribute属性才能生效,

            <CustomAttribute
                motion:attributeName="Alpha"
                motion:customFloatValue="0.3"/>

value有下边几种

attributeName   The name of the attribute. Case sensitive. ( MyAttr will look for method setMyAttr(...)
customColorValue    The value is a color looking setMyAttr(int )
customIntegerValue      The value is an integer looking setMyAttr(int )
customFloatValue    The value is a float looking setMyAttr(float )
customStringValue   The value is a String looking setMyAttr(String )
customDimension The value is a dimension looking setMyAttr(float )
customBoolean   The value is true or false looking setMyAttr(boolean )

Constraint支持的属性

android:id Id of the View
[ConstraintLayout attributes] Any attribute that is part of ContraintLayout layout is allowed
[Standard View attributes] A collection of view attributes supported by the system (see below)
transitionEasing define an easing curve to be used when animating from this point (e.g. curve(1.0,0,0,1.0)) or key words {standard | accelerate | decelerate | linear }
pathMotionArc the path will move in arc (quarter eclipses) or key words {startVertical | startHorizontal | none }
transitionPathRotate (float) rotate object relative to path taken
drawPath draw the path the layout will animate animate
progress call method setProgress(float) on this view (used to talk to nested ConstraintLayouts etc.)
<CustomAttribute> call a set"name" method via reflection
<Layout> Attributes for the ConstraintLayout e.g. layout_constraintTop_toTopOf
<PropertySet> currently only visibility, alpha, motionProgress,layout_constraintTag.
<Transform> All the view transform API such as android:rotation.
<Motion> Motion Layout control commands such as transitionEasing and pathMotionArc

上述就是约束的基础属性了,写在这里的有个问题,它只能设置一个初始值,一个结束值,然后中间由系统自动处理。
比如开头demo里的viewpager,有5个页面,进度从0到100,如果我们只打算在最后一个页面出来的时候处理些东西,那么进度应该是在80到100之间的,这个上边的做不到了,就需要Transition里KeyFrameSet来处理了,比如,下边的透明度在进度从0到20的时候就变化完成了,进度20之后的就完全是不透明了

        <KeyFrameSet>
            <KeyAttribute
                android:alpha="0"
                app:framePosition="0"
                app:motionTarget="@+id/btn_pre" />
            <KeyAttribute
                android:alpha="1"
                app:framePosition="20"
                app:motionTarget="@id/btn_pre" />
Transition

下边2个属性,一个是动画起始的时候的约束set,一个是结束的时候的约束set,指向上边介绍的ConstraintSet
查看demo,发现constraintSetStart 或者constraintSetEnd 也可以指向一个布局文件。

constraintSetStart ConstraintSet to be used as the start constraints or a layout file to get the constraint from
constraintSetEnd ConstraintSet to be used as the end constraints or a layout file to get the constraint from

OnSwipe (optional)

处理触摸事件的

Attributes Description
touchAnchorId Have the drag act as if it is moving the "touchAnchorSide" of this object
touchRegionId Limits the region that the touch can be start in to the bounds of this view (even if the view is invisible)
touchAnchorSide The side of the object to move with {top,left,right,bottom}
maxVelocity limit the maximum velocity (in progress/sec) of the animation will on touch up. Default 4
dragDirection which side to swipe from {dragUp,dragDown,dragLeft,dragRight}
maxAcceleration how quickly the animation will accelerate (progress/sec/sec) and decelerate on touch up. Default 1.2
dragScale scale factor to adjust the swipe by. (e.g. 0.5 would require you to move 2x as much)
moveWhenScrollAtTop If the swipe is scrolling and View (such as RecyclerView or NestedScrollView) do scroll and transition happen at the same time
autoComplete swipe automatically animates to start or end. Default is true. Warning: turning this off and using time cycles can result in continuous animations.

dragDirection 就是手指滑动的方向了,左右,上下 触发动画事件,拖动到一半松手,动画自动就还原了。
touchAnchorId 这个东西好像是那个在进行动画的view的id。

OnClick (optional)
targetId What button triggers Transition.
clickAction Direction for buttons to move the animation. mode: transitionToEnd, toggle, transitionToStart, jumpToEnd, jumpToStart

这个就2个属性,target 指向一个view的id,指定点击哪个view触发事件
第二个就是mode了,有5种 transitionToEnd, toggle, transitionToStart, jumpToEnd, jumpToStart
就是前边Transition里的constraintSetStart和constraintSetEnd 决定从start到end还是end到start
jumpToEnd, jumpToStart好像就是直接结束动画了,以前测试版会异常,正式版试了下好像不挂,就是直接从start切换为end状态或者反过来.

KeyFrameSet关键帧的集合

前边一堆,我们也看到我们的动画只有开头和结束两种状态,中间其实就是线性的对x和y的位置进行过渡的。
比如view开始在左上角,结束在右下角,那么正常动画就是斜线了。
如果我们需要view拐个弯啥的,咋办了。KeyFrameSet就是干这个用的,可以插入一些中间帧。


image.png

研究下简单的两种

KeyPosition

这个是修改关键帧的位置的

attribute descrption
motionTarget Id of the View or a regular expression to match layout_ConstraintTag
framePosition The point along the interpolation 0 = start 100 = end
transitionEasing define an easing curve to be used when animating from this point (e.g. curve(1.0,0,0,1.0)) or key words {standard | accelerate | decelerate | linear }
pathMotionArc The path will move in arc (quarter eclipses) key words {startVertical | startHorizontal | flip | none }
keyPositionType how this keyframe's deviation for linear path is calculated {deltaRelative | pathRelative|parentRelative}
percentX (float) percent distance from start to end along X axis (deltaRelative) or along the path in pathRelative
percentY (float) Percent distance from start to end along Y axis (deltaRelative) or perpendicular to path in pathRelative
percentWidth (float) Percent of change in the width. Note if the width does not change this has no effect.This overrides sizePercent.
percentHeight (float) Percent of change in the width. Note if the width does not change this has no effect.This overrides sizePercent.
curveFit path is traced
drawPath Draw the path of the objects layout takes useful for debugging
sizePercent If the view changes size this controls how growth of the size. (for fixed size objects use KeyAttributes scaleX/X)
curveFit selects a path based on straight lines or a path based on a monotonic spline {linear|spline}

motionTarget :指定是哪个view,因为动画过程可能有N个在发生变化,总得说下这个是给谁的
framePosition 这个就是动画运行的百分比,0到100
keyPositionType:deltaRelative | pathRelative|parentRelative 下边说
percentX 指定在framePostion这个时间点的时候x应该在啥位置,这个是个百分比,0到1之间的值,具体是谁的百分比要看keyPositionType,delta话这里就是target的start和end的x坐标的差值;path的话就是值起点到终点的路径的百分比。parent顾名思义,指的就是MotionLayout这个容器的横轴的百分比
percentY一个道理
sizePercent 这个和上边x,y其实一样,不过这里是对大小改变的view来说的。
https://medium.com/google-developers/defining-motion-paths-in-motionlayout-6095b874d37
上边的帖子有讲坐标轴,percentX,percentY是有正负一说的,而且所对应的值也是path这个坐标系统对应的值,帖子里的parentRelative的图片感觉y轴画反了,大家自己实验下吧。

  1. pathRelative
    正负咋区分?
    简单,从start 到end就是x轴的正向,y轴就是start为圆点,顺时针旋转90度,ok.


    image.png
  2. deltaRelative
    也是从起点到终点,不过坐标轴是和屏幕一样,是横竖的,不像parentRelative坐标轴可能是歪的,
    至于正负,从起点往终点的方向就是正的,反之就是负的


    image.png
  3. parentRelative
    这个就更简单了,坐标轴是parent,左上角是圆点,和我们平时的坐标系统一样,下图好像y轴反了?


    image.png

KeyAttribute

attribute descrption
motionTarget Id of the View or a regular expression to match layout_ConstraintTag
framePosition The point along the interpolation 0 = start 100 = end
curveFit selects a path based on straight lines or a path based on a monotonic spline {linear|spline}
transitionEasing Define an easing curve to be used when animating from this point (e.g. curve(1.0,0,0,1.0)) or key words {standard , accelerate , decelerate , linear }
transitionPathRotate (float) rotate object relative to path taken
drawPath draw the path the layout will animate animate
motionProgress call method setProgress(float) on this view (used to talk to nested ConstraintLayouts etc.)
[standard view attributes] A collection of post layout view attributes see below
<CustomAttribute> call a set"name" method via reflection

CustomAttribute

attribute descrption
attributeName The name of the attribute. Case sensitive. ( MyAttr will look for method setMyAttr(...)
customColorValue The value is a color looking setMyAttr(int )
customIntegerValue The value is an integer looking setMyAttr(int )
customFloatValue The value is a float looking setMyAttr(float )
customStringValue The value is a String looking setMyAttr(String )
customDimension The value is a dimension looking setMyAttr(float )
customBoolean The value is true or false looking setMyAttr(boolean )

attributeName这个需要注意一下,这里的名字不是xml里的,而是控件对应的setXXX方法对应的XXX。
举例,
设置背景,在xml里是android:background="#222",想测试下动态修改背景图片,愣是没找到咋弄,CustomAttribute 里好像没有设置背景图片的.

<CustomAttribute app:attributeName="backgroundColor"   app:customColorValue="@color/colorPrimaryDark"/>
<CustomAttribute app:attributeName="backgroundColor"  app:customColorValue="#2196F3"/>

这个是修改在关键帧的地方控件的属性

            <KeyAttribute
                android:scaleX="2"
                android:scaleY="2"
                android:rotation="-45"
                motion:framePosition="50"
                motion:target="@id/button" />

KeyCycle

image.png

app:wavePeriod 这个就是波形的次数,比如正弦从0到360是一次,你要执行2次,这里就写个2
app:framePosition 和上边一样,指定一个时间点,在这个附近应用Cycle
android:rotation="50" 下边列子是一个角度旋转的动画,这里指定50,就是最大值了。
android:translationY="50dp" 表示y轴的最大偏移量就是50dp
实际的动画受到波形的影响。比如波形是正弦,那么正弦的值从0到1到0再到-1最后又到0
那么实际的角度 就是不停的变化了,就是那个50度乘以这个正弦值。
其他的波形逻辑是一样的,不过那些波形的值的变化没研究,不太清楚。
举例如下

            <KeyCycle
                app:target="@+id/btn_test"
                android:rotation="50"
                app:framePosition="50"
                app:wavePeriod="1"
                app:waveShape="sin" />

下边是支持的标准属性,用这些属性,加上波形,就可以让控件忽明忽暗,一会变大一会缩小,旋转了。看自己需求了。。。


image.png

KeyTrigger

attribute description
motionTarget Id of the View or a regular expression to match layout_ConstraintTag
framePosition The point along the interpolation 0 = start 100 = end
onCross (method name) on crossing this position call this methods on the target
onPositiveCross (method name) on forward crossing of the framePosition call this methods on the target
onNegativeCross/td> (method name) backward crossing of the framePosition call this methods on the target
triggerSlack (float) do not call trigger again if the framePosition has not moved this fraction away from the trigger point
triggerId (id) call the TransitionListener with this trigger id
motion_postLayoutCollision Define motion pre or post layout. Post layout is more expensive but captures KeyAttributes or KeyCycle motions.
motion_triggerOnCollision (id) Trigger if the motionTarget collides with the other motionTarget

举个列子,在进度为50的时候会执行onCross里的方法performClick,谁的点击事件?自然是mationTarget指定的,

<KeyTrigger
                app:framePosition="50"
                app:motionTarget="@+id/view_pin"
                app:onCross="performClick"
                app:onNegativeCross="performLongClick"
                app:triggerSlack="1" />

可以理解为触发器,就是在某一位置framePostion的时候会回调容器MotionLayout的TransitionListener里的onTransitionTrigger 的方法,参数triggerId就是上边KeyTrigger里定义的,没有定义的话就是-1,progress就是当前的进度从0到1,positive咋说了,比如我们上边写的触发机制是position 50,如果比这个晚的比如返回的progress是0.51那么就是true,如果是0.49这样的就是false

public void addTransitionListener(MotionLayout.TransitionListener listener) 
public abstract void onTransitionTrigger (MotionLayout motionLayout, 
                int triggerId, 
                boolean positive, 
                float progress)

试了下,只有第一次start到end,和从end返回start 会触发trigger回调,其他时候就没有了,onCross方法也只有第一次有,后边就没了.不太清楚是姿势不对还是本来就这样,待研究.

补充

demo里看到下边的代码,初始不知道干啥的

    <com.google.androidstudio.motionlayoutexample.helpers.ExampleFlyinBounceHelper
        android:id="@+id/helper"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="imageView9"/>

然后看了下它的类,发现里边就是对某个view进行了一个平移动画

public class ExampleFlyinBounceHelper extends ConstraintHelper {
    protected ConstraintLayout mContainer;
//构造方法省略。
    @Override
    public void updatePreLayout(ConstraintLayout container) {
        if (mContainer!=container) {
            View[] views = getViews(container);
            for (int i = 0; i < mCount; i++) {
                View view = views[i];
                ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", - 2000, 0).setDuration(1000);
                animator.setInterpolator(new BounceInterpolator());
                animator.start();
            }
        }
        mContainer = container;
    }
}

然后简单研究下这个类
可以看到它主要是读取了一个属性constraint_referenced_ids
我们在ConstraintLayout里见过这个,就是关联id,多个的话用逗号隔开。
如下

app:constraint_referenced_ids="imageButton2,imageView9"

源码简化部分,以及可能用到的方法,变量等

public abstract class ConstraintHelper extends View {
    protected int[] mIds = new int[32];//保存关联的id,当然了这个数组比较大
    protected int mCount;//关联的id个数
    public int[] getReferencedIds() {//这个是真实的id个数的数组
        return Arrays.copyOf(this.mIds, this.mCount);
    }
TypedArray a = this.getContext().obtainStyledAttributes(attrs, styleable.ConstraintLayout_Layout);
if (attr == styleable.ConstraintLayout_Layout_constraint_referenced_ids) {
                    this.mReferenceIds = a.getString(attr);
                    this.setIds(this.mReferenceIds);
                }

//关联的这些id对应的view数组
protected View[] getViews(ConstraintLayout layout){
}

//然后下边几个方法会不停的调用
public void updatePreLayout(ConstraintLayout container){
//这个应该是预加载,在measure和layout之前。
}
//首次加载view,我们知道measure和layout会执行2次,这个也是
    public void updatePostLayout(ConstraintLayout container) {
    }

    public void updatePostMeasure(ConstraintLayout container) {
    }

  1. 布局初始状态加载完成,当我们移动布局,
    会多次执行
    updatePreLayout
    updatePostMeasure
    当移动结束的时候会执行
    updatePostLayout
  2. 调用motionLayout.transitionToStart() 自动移动的话,
    执行顺序如下
    updatePostMeasure
    updatePreLayout
    updatePostMeasure
    updatePostLayout

就如demo里写的,这个helper类,主要就是用来对一些view做特殊处理。它本身默认宽高都是0的。

MotionLayout的常用方法

设置当前的进度。

public void setProgress(float pos)

移动到开始或结束位置

    public void transitionToStart() {
        this.animateTo(0.0F);
    }

    public void transitionToEnd() {
        this.animateTo(1.0F);
    }

继续学习

Defining motion paths in MotionLayout

KeyPosition

framePosition:这个就是关键帧的位置?咋说了,总的帧是100,我们知道有个interpolator的东西,以线性的interpolator来举例。从起点A【50,100】移动到终点B【150,100】,需要100秒,在中心点【100,100】的地方,帧是50,耗时一半也就是50秒, 现在我们弄个关键帧,framePosition=50,完事修改这个帧的位置为C【60,100】那么结果就是从起点A【50,100】移动到C【60,100】耗时50秒,后边C到终点B【150,100】耗时50秒。
下边有效果图,看那虚线密度不一样也能大概看出来。
主要学习下
percentX,percentY【大小从0到1f】,
keyPositionType[3种:parentRelative,pathRelative,deltaRelative] 之间的关系

            <KeyPosition
                app:keyPositionType="pathRelative"
                app:percentY="-0.25"
                app:percentX="0.5"
                app:framePosition="50"
                app:transitionEasing="accelerate"
                app:motionTarget="@id/btn_test"/>

下边几种起始点和结束点都一样,起始点在中心,结束点在左上角有个margin而已。分别是A和B
C就是我们的keyPostiion

  1. parentRelative
    实际结果来看,坐标原点还是左上角,x轴往右是正的,y轴往下是正的。所以下图感觉画的不太合适


    image.png

    percentX 就是parent水平方向的百分比
    percentY:parent的垂直方向的百分比
    代码

            <KeyPosition
                app:keyPositionType="parentRelative"
                app:percentX="0.75"
                app:percentY="0.25"
                app:framePosition="50"
                app:transitionEasing="accelerate"
                app:motionTarget="@id/btn_test"/>

效果图如下


image.png
  1. deltaRelative
    percentX,percentY 对应的百分比,是参照起点和终点之间的x,y轴距离的,正负也是按照从起点到重点来的,
    如果B在A的右边,那么percentX为正就是右边的
    参考这个


    image.png

代码

            <KeyPosition
                app:keyPositionType="deltaRelative"
                app:percentX="0.75"
                app:percentY="0.25"
                app:framePosition="50"
                app:transitionEasing="accelerate"
                app:motionTarget="@id/btn_test"/>
image.png
  1. pathRelative
    就是说x轴就是起点start连接到终点end这段距离,至于y轴就是和这条路径垂直的,至于方向x轴顺时针90度那个是y轴的方向
    代码如下
            <KeyPosition
                app:keyPositionType="pathRelative"
                app:percentX="0.5"
                app:percentY="0.5"
                app:framePosition="50"
                app:transitionEasing="accelerate"
                app:motionTarget="@id/btn_test"/>
image.png

pathMotionArc

大家知道,没有中间点的话,start到end之间就是线性移动的,如果想要弧形移动咋办,非常简单
You will simply need to add the motion:pathMotionArc attribute to the starting Constraint, to switch from the default linear motion to an arc motion

    <ConstraintSet
        android:id="@+id/start" >
        <Constraint
            app:pathMotionArc="startVertical"
            android:id="@id/btn_test"

这个属性有4种值:设置的话一般就startVertical或startHorizontal,要不就不写
none:默认的,就是线条
startVertical:圆弧起点往下或者往上,根据终点绝点
startHorizontal: 圆弧从起点往左或者往右,根据终点决定,
flip:这个结果和startHorizontal一样,其实这个需要参照物的,可没有
看下上边链接里写的flipping the current arc direction ,反转当前圆弧的方向。


image.png

上边的是没有中间点的情况,如果有中间点咋办?
中间点可以控制自己之后的圆弧方向,或者还原成直线
如下代码,一样的属性,value也是4种可选,
不过这里的flip就有实际意义了,如果之前的是startVertical,那么这里flip就相当于startHorizontal,同理之前是startHorizontal,这里就相当于startVertical
如果是none,那么这后边的就成直线了。

        <KeyFrameSet>
            <KeyPosition
                app:pathMotionArc="flip"
                app:framePosition="50"

transitionEasing

这个影响插值器的,默认的是线性的,这里可以修改,系统已经写好了几个可以用的

        <Constraint
            app:pathMotionArc="startHorizontal"
            app:transitionEasing="decelerate"

可用的standard, accelerate, decelerate
还有这种cubic(float, float , float, float) 就是个一阶贝塞尔曲线,所以这里是2个点的坐标,百分比,从0到1的
当然可以大于1,不过大于1你会发现,时间本来是2秒,结果1秒可能就到终点了。
比如

 app:transitionEasing="cubic(0.0, 0.0, 0.2, 1)"

KeyAttribute

也是处理中间状态的,不过这里是处理一些基本属性的,比如缩放,旋转,平移,透明度等。

<KeyFrameSet>
    <KeyAttribute
        android:scaleX="2"
        android:scaleY="2"
        android:rotation="-45"
        motion:framePosition="50"
        motion:target="@id/button" />
</KeyFrameSet>

Supported Attributes
The attributes you can use out of the box are view attributes:
android:visibility, android:alpha, android:elevation, android:rotation, android:rotationX, android:rotationY, android:scaleX, android:scaleY, android:translationX, android:translationY, android:translationZ
这些属性和sdk版本挂钩的,如下2个,21才支持,所以21以下我们设置是无用的
android:elevation was introduced in SDK 21
android:translationZ was introduced in SDK 21

Custom Attributes

修改控件属性的
可以放在Constraint里,也可以放在KeyAttribute里
看几个例子

        <Constraint
            android:id="@id/btn_test"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="80dp"
            android:layout_marginRight="30dp"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <CustomAttribute app:attributeName="background"
                app:customColorDrawableValue="@color/book_content_bg_color"/>
        </Constraint>


            <KeyAttribute
                android:scaleX="2"
                android:scaleY="2"
                android:rotation="-45"
                app:framePosition="50"
                app:motionTarget="@id/btn_test">
                <CustomAttribute   省略 />
            </KeyAttribute>

attributeName:这个是控件本身xml里支持的,名字就是xml里的,首字母大小写都可以,名字不对不会生效,自己试下就知道了。

剩下的是个value,有好几种,就是区分是颜色,文字,小数整数等的,看名字也就大概知道了
这里说下背景色的

            <CustomAttribute
                app:attributeName="background"
                app:customColorDrawableValue="@color/colorAccent" />

            <CustomAttribute
                app:attributeName="BackgroundColor"
                app:customColorValue="@color/color_blue" />

比如修改文字

            <CustomAttribute
                app:attributeName="text"
                app:customStringValue="开始"/>

touchAnchorId

需要说一下,这个id是必须的,要不手指触摸屏幕滑动的时候直接就挂了,说找不到view之类的错误

        <OnSwipe app:dragDirection="dragDown"
            app:touchAnchorId="@+id/btn_test"
            app:touchAnchorSide="left"/>

使用经验

  1. onSwipe
    这个标签作用就是手指滑动就可以执行动画了,啥参数也没有,默认是手指上下滑动进行移动。
<onSwipe />

touchAnchorId:这个可以不写,它默认应该有一个吧。就是ConstraintSet里的某个Constraint。 如果写了,必须写Constraint对应的id,比如你有2个Constraint,写任何一个的id都可以。写谁就参照谁来移动。 如果你写了一个非Constraint的id,比如MotionLayout里的某个不变化的控件id,那么移动的会后你会发现没有过程,start到end之间是瞬间切换的。

  1. app:pathMotionArc="startVertical"
    前边介绍过这个,两个点直接本来是直线,用这个可以变成弧线。startHorizontal一个道理
    不过实际使用中发现个问题,我的view变化是x轴的【y轴变化也一样,换句话说,不能是横线或竖线变化】,我也想看看x轴的,它咋弄,结果是它挂了。哈哈。
    反正start和end的x,或者y坐标不能一样,至少有点偏差才能用上边的属性
    java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
        at androidx.constraintlayout.motion.utils.ArcCurveFit.getPos(ArcCurveFit.java:51)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容