preface

In the previous article, CollapsingToolbarLayout was used in collaboration with AppBarLayout to collapse the title bar.

By using MotionLayout with AppBarLayout, in addition to realizing the folding effect of the title bar, it also supports the smooth movement of the sub-view in the title bar, so that the whole interaction is more comfortable.

implementation

First analyze the effect:

  • When the appbar is completely hidden, the two views remain in the upper left corner
  • The background image will continue to move up as the scroll, and gradually transparent
  • The todos, cards, levels and meeting entries below become more transparent

MotionLayout The scroll associated with AppBarLayout

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) } }Copy the code

This code is a reference to the official demo. This code is used to listen to the sliding distance of the AppBarLayout, then convert the sliding distance to the scale, and then set the value to the Progress property of the MotionLayout. This property is used to control the progress of the child view.

One more caveat here is that in order to listen for the slide distance of AppBarLayout, ensure that the parent of the CollapsibleToolbar is AppBarLayout

Configure MotionScene: the animation effect of the child view

<? The 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" motion:motionInterpolator="linear"> <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@+id/background" motion:touchAnchorSide="bottom" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/background" android:layout_width="match_parent" Android: layout_height = "match_parent" android: alpha = "1.0" android: scaleX = "1.1" android: scaleY = "1.1" motion:layout_constraintBottom_toBottomOf="parent" /> <Constraint android:id="@+id/ivHead" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="50dp" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@+id/tvUserName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" motion:layout_constraintLeft_toLeftOf="parent" motion:layout_constraintRight_toRightOf="parent" motion:layout_constraintTop_toBottomOf="@+id/ivHead" /> <Constraint android:id="@+id/llContent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" Android :alpha="1.0" Motion :layout_constraintTop_toBottomOf="@+id/tvUserName" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/background" android:layout_width="match_parent" android:layout_height="match_parent" android:alpha="0" android:translationX="0dp" android:translationY="100dp" motion:layout_constraintBottom_toBottomOf="parent" /> <Constraint android:id="@id/ivHead" Android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: scaleX = "0.6" android: scaleY = "0.6" android:translationY="8dp" motion:layout_constraintBottom_toBottomOf="@+id/background" motion:layout_constraintStart_toStartOf="@+id/background" /> <Constraint android:id="@id/tvUserName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="7dp" motion:layout_constraintBottom_toBottomOf="@+id/background" motion:layout_constraintLeft_toRightOf="@+id/ivHead" /> <Constraint android:id="@+id/llContent" android:layout_width="match_parent" android:layout_height="wrap_content" Android :alpha="0.0" Motion :layout_constraintTop_toBottomOf="@+id/tvUserName" /> </ConstraintSet> </MotionScene>Copy the code

According to the effect analysis above, the animation is mainly aimed at the child view

  • Background: 1. Changes in transparency 2. Changes in y axis displacement
  • Head: 1. Zoom change; 2. Xy axis displacement change
  • 1. Change of xy axis displacement
  • Total user information: 1. Changes in transparency

The content of MotionScene mainly includes these four points. Of course, some small details need to be studied in depth, such as: how to ensure that after the AppBar is hidden, the avatar is displayed vertically in the center, and the relative position of the name and avatar is set

conclusion

At this point, the entire effect is complete. Some students may wonder, it seems that not a word of code has been written, how do these effects come from? The core is still in the logic of MotionLayout and AppBarLayout, which already handles animation, scrolling, and everything else.

expand

  1. Use the MotionLayout property first. The official provided a good example: MotionLayout-Demo
  2. In the previous article, we mentioned that app:layout_scrollFlags has a snap configuration, which is useful here:app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"As you can see, AppBarLayout automatically scrolls until it is fully hidden or expanded when you let go halfway through the scroll
  3. The AppBarLayout is not completely folded, but retains some distance. How to do this? Lies mainly inandroid:minHeight="50dp"After the minimum height is set, the configuration height will not be folded
  4. For avatar scaling, android:scaleX=”0.6″ and Android :scaleY=”0.6″ only change the content size of the view, the size of the view itself remains, note here

Finally, as usual, attach the demo address: gitee-demo