MotionLayout的代码控制与案例

使用代码修改效果

       MotionLayout 通过xml 写着很方便,但有时我们需要动态的改变转场效果,就需要通过代码来实现了。比如初始状态是xml 描述的转场效果,当我们点击按钮时,变成另外一种转场效果,类似如下效果图:

       初始转场动画是一个简单的位移动画。
点击状态1按钮,转场动画变成除了平移转场动画外,还有rotationY 从1到180 和alpha 从1到0.1的过度转场动画。点击状态2按钮,平移转场结束的位置变了。

       核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
btnState1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ConstraintSet constraintSet = motionLayout.getConstraintSet(R.id.end);
ConstraintSet.Constraint constraint = constraintSet.getConstraint(R.id.box);
constraint.layout.topToTop = Constraints.LayoutParams.PARENT_ID;
constraint.layout.bottomToBottom = Constraints.LayoutParams.PARENT_ID;
constraint.layout.endToEnd = Constraints.LayoutParams.PARENT_ID;
//transform 可以改变旋转缩放等属性
constraint.transform.rotationY = 180f;
constraint.transform.rotationX = 0f;
constraint.transform.scaleY = 2f;
constraint.transform.scaleX = 2f;
constraint.propertySet.alpha = 0.1f;
}
});
btnState2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ConstraintSet constraintSet = motionLayout.getConstraintSet(R.id.end);
ConstraintSet.Constraint constraint = constraintSet.getConstraint(R.id.box);
constraint.layout.topToTop = Constraints.LayoutParams.PARENT_ID;
constraint.layout.endToEnd = Constraints.LayoutParams.PARENT_ID;
//去除约束将View 放到右上角
constraint.layout.bottomToBottom = Constraints.LayoutParams.UNSET;
constraint.transform.rotationX = 180f;
constraint.transform.rotationY = 0f;
}
});

       通过代码动态改变转场大致有以下三步

  1. 通过MotionLayout的getConstraintSet获取要修改的的约束集ConstrainSet;
  2. 通过ConstrainSet的getConstraint的方法获取对应Contraint;
  3. 拿到Contraint之后,利用提供的方法,就可以改成自己想要的效果了。

和ViewPager组合使用

       让动画随着ViewPager的滑动而运动。效果如下:

       MotionLayout 有一个setProgress(float pos) 方法,当滑动ViewPager时,改变MotionLayout的执行进度。

       布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<LinearLayout 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"
android:orientation="vertical">
<!--MotionLayout 作为根布局,才能使用as的预览动画,所以采用include方式-->
<include
android:id="@+id/motionLayout"
layout="@layout/viewpager_header" />


<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />


<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</LinearLayout>

       viewpager_header.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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="200dp"
android:background="@drawable/sky"
app:layoutDescription="@xml/viewpager_header_scene"
app:showPaths="true">

<ImageView
android:id="@+id/doraemon"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/doraemon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />

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

        viewpager_header_scene.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<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"
motion:pathMotionArc="flip">
</Transition>

<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/doraemon"
android:layout_width="60dp"
android:layout_height="60dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent" />
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/doraemon"
android:layout_width="60dp"
android:layout_height="60dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>

       核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
motionLayout.setProgress((position + positionOffset) / (adapter.getCount() - 1));
}

@Override
public void onPageSelected(int position) {

}

@Override
public void onPageScrollStateChanged(int state) {

}
});

        添加viewPager的page改变监听器,在onPageScrolled 中根据位置和偏移量计算当前活动的进度,修改MotionLayout的progress的值,从而实现MotionLayout和ViewPager的联动。如果其它控件想实现联动也是类似方式。

       代码详见ConstraintLayoutTest

参考资料:
knight康康 MotionLayout 使用说明书(进阶+实战)

Fork me on GitHub