This article has been published exclusively by guolin_blog, an official wechat account

ConstraintHelper

ConstraintLayout provides GuideLine assisted layouts in 1.0, groups and barriers in 1.1, and layers in 2.0, allowing developers to customize Helper configurations.

Group (Added in 1.1)

Groups can be used to control the visibility of a set of Views

    <android.support.constraint.Group
              android:id="@+id/group"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:visibility="visible"
              app:constraint_referenced_ids="button4,button9" />
Copy the code

You can directly control the visibility of a group of views (button4,button9) by controlling the group’s hide/show.

The Barrier (Added in 1.1)

Let’s take a look at a scene. Here’s a form with Email and Password left-aligned, and the dotted line in the middle is GuideLine. The specific fields are left-aligned with GuideLine.

Now if you need to do multiple languages, the translation into German becomes the following effect

And that’s where the Barrier comes in.

Improved method

  • Replace the dotted line GuideLine with a Barrier
  • Add ① and ② to the referenced_ids of barriers
  • Specify barrierDirection as right (no more than right)
  • Align the left side of ③ and ④ to the right of the Barrier

In this way, the Email and Password will not exceed the Barrier, and the general code is as follows (deleted, please refer to here for the complete code).

<?xml version="1.0" encoding="utf-8"? >
<android.support.constraint.ConstraintLayout>
    <TextView
        android:id="@+id/tv_email"
        app:layout_constraintBottom_toTopOf="@+id/tv_password"
        app:layout_constraintStart_toStartOf="@+id/tv_password"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="E-mail Addresse" />

    <EditText
        android:id="@+id/et_email"
        android:text="[email protected]"
        app:layout_constraintBaseline_toBaselineOf="@+id/tv_email"
        app:layout_constraintStart_toEndOf="@+id/barrier" />

    <TextView
        android:id="@+id/tv_password"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_email" />

    <EditText
        android:id="@+id/et_password"
        android:inputType="textPassword"
        android:text="2321321"
        app:layout_constraintBaseline_toBaselineOf="@+id/tv_password"
        app:layout_constraintStart_toEndOf="@+id/barrier" />

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        app:barrierDirection="right"
        app:constraint_referenced_ids="tv_email,tv_password" />

</android.support.constraint.ConstraintLayout>
Copy the code

Layer (Added in 2.0)

Layer can be thought of as the boundary of the views it refers to (think of as a ViewGroup containing those views, but Layer is not a ViewGroup and does not add to the view’s hierarchy). In addition, Layer supports the transformation of the view inside.

Consider a scenario where a page has views that need a background, use Layer to reference those views, and then set the background for Layer. If you do not use Layer, you have to add another ViewGroup to cover these views, which will increase the View level and not good for performance.

Take a look at an example (full code) :

The Layer in the figure covers the six buttons in the middle, and the green border is filled with white through the Layer setting background. In addition, animate all the buttons in the Layer together, and the resulting effect is like this

ConstraintLayout2.0 provides a way for developers to customize ConstraintHelper, in addition to several default implementations of ConstraintHelper.

Custom Helper

Why custom?

  • Keep the view’s hierarchy unchanged, unlike a ViewGroup that increases the view’s hierarchy
  • Encapsulate some specific behavior for easy reuse
  • A View can be referenced by multiple helpers, making it easy to create complex effects

How to customize?

  • The Helper holds a reference to the view, so it can get the view (getViews) and manipulate the view
  • Provides onLayout before and after the callback (updatePreLayout/updatePreLayout)
  • The Helper inherits the View, so the Helper itself is also a view

CircularRevealHelper

CircularReveal ViewAnimationUtils provides the createCircularReveal function to createCircularReveal on an image

public static Animator createCircularReveal(View view,
            int centerX,  int centerY, float startRadius, float endRadius) 
Copy the code

The CircularReveal effect can be easily achieved by calculating the center (Cenx) and endRadius (radius)

class CircularRevealHelper @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintHelper(context, attrs, defStyleAttr) {

    override fun updatePostLayout(container: ConstraintLayout) {
        super.updatePostLayout(container)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val views = getViews(container)
            for (view in views) {
                val anim = ViewAnimationUtils.createCircularReveal(view, view.width / 2,
                        view.height / 2.0f,
                        Math.hypot((view.height / 2).toDouble(), (view.width / 2).toDouble()).toFloat())
                anim.duration = 3000
                anim.start()
            }
        }
    }
}
Copy the code

UpdatePostLayout is going to be called right after onLayout, so just animate it here.

CircularRevealHelper can be used directly in XML, and constraint_referenced_ids of CircularRevealHelper specifies that the view needs to be animated.

<?xml version="1.0" encoding="utf-8"? >
<android.support.constraint.ConstraintLayout 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">


    <ImageView
        android:id="@+id/img_mario"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/mario" />

    <cn.feng.constraintLayout2.helps.CircularRevealHelper
        android:id="@+id/helper"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="img_mario"
        tools:ignore="MissingConstraints" />

</android.support.constraint.ConstraintLayout>
Copy the code

CircularReveal can be specified directly in XML if you want to do view CircularReveal, so that a good reuse.

FlyinHelper

Let’s take a look at the Flyin effect, where views Flyin from around to their respective positions.

The key to this animation is to figure out which direction each view should fly in.

The position of the red border can be found with the help of the Layer introduced above (of course, it can also be calculated without the help of Layer, which is slightly complicated), so as to calculate the position of the middle point of the red box part, and then compare with the middle point of each view in the figure (the position of each white point in the figure), so as to get which direction each view should fly in.

The code for calculating the initial position of each view is shown below, which should be easy to understand with the help of the graphics above.

    for (view in views) {

            val viewCenterX = (view.left + view.right) / 2
            val viewCenterY = (view.top + view.bottom) / 2


            val startTranslationX = if (viewCenterX < centerPoint.x) - 2000.f else 2000f
            val startTranslationY = if (viewCenterY < centerPoint.y) - 2000.f else 2000f


            view.translationX = (1 - animatedFraction) * startTranslationX
            view.translationY = (1 - animatedFraction) * startTranslationY
        }
Copy the code

See here for the full code for FlyinHelper

ComposeMultipleHelper

Each view can accept not only one ConstraintHelper, but also multiple ConstraintHelper simultaneously.

The four ImageViews on the left and the FloatingActionButton on the bottom right have a Flyin effect, while the four imageViews on the left are also 3D rotated around the Y-axis. Top Seekbar background in doing CircularReveal effect. With CircularRevealHelper and FlyInHelper written earlier, it’s easy to do this.

Code reference here

Flow (VirtualLayout)

Flow is VirtualLayout, and Flow helps to quickly layout elements within constraint_referenced_ids like Chain. Flow_wrapMode specifies the arrangement. There are three modes

  • Wrap None: Simply chain the elements within constraint_referenced_IDS, even if there is not enough space

  • Wrap chain: One or more chains are formed based on space size and element size

  • Wrap aligned: Wrap chain is similar but aligned

Here’s how to implement the calculator layout:

<?xml version="1.0" encoding="utf-8"? >
<android.support.constraint.ConstraintLayout 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"
    tools:context=".activity.MainActivity">


    <android.support.constraint.helper.Flow
        android:id="@+id/flow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFC107"
        android:padding="20dp"
        app:constraint_referenced_ids="tv_num_7,tv_num_8,tv_num_9,tv_num_4,tv_num_5,tv_num_6,tv_num_1,tv_num_2,tv_num_3,tv_num_0,tv_operator_div,tv_dot,tv_ope rator_times"
        app:flow_horizontalGap="10dp"
        app:flow_maxElementsWrap="3"
        app:flow_verticalGap="10dp"
        app:flow_wrapMode="aligned"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/tv_num_7"
        style="@style/text_view_style"
        android:text="Seven" />

    <TextView
        android:id="@+id/tv_num_8"
        style="@style/text_view_style"
        android:text="8" />

    <TextView
        android:id="@+id/tv_num_9"
        style="@style/text_view_style"
        android:text="9" />


    <TextView
        android:id="@+id/tv_num_4"
        style="@style/text_view_style"
        android:text="4" />

    <TextView
        android:id="@+id/tv_num_5"
        style="@style/text_view_style"
        android:text="5" />

    <TextView
        android:id="@+id/tv_num_6"
        style="@style/text_view_style"
        android:text="6" />


    <TextView
        android:id="@+id/tv_num_1"
        style="@style/text_view_style"
        android:text="1" />

    <TextView
        android:id="@+id/tv_num_2"
        style="@style/text_view_style"
        android:text="2" />

    <TextView
        android:id="@+id/tv_num_3"
        style="@style/text_view_style"
        android:text="3" />

    <TextView
        android:id="@+id/tv_num_0"
        style="@style/text_view_style"
        android:text="0" />

    <TextView
        android:id="@+id/tv_operator_div"
        style="@style/text_view_style"
        android:text="/"
        tools:layout_editor_absoluteX="156dp"
        tools:layout_editor_absoluteY="501dp" />

    <TextView
        android:id="@+id/tv_operator_times"
        style="@style/text_view_style"
        android:text="*" />

    <TextView
        android:id="@+id/tv_dot"
        style="@style/text_view_style"
        android:text="."
        tools:layout_editor_absoluteX="278dp"
        tools:layout_editor_absoluteY="501dp" />

    <TextView
        android:id="@+id/KE"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#00BCD4"
        android:gravity="center"
        android:text="Compute"
        android:textColor="@android:color/white"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="@+id/tv_operator_times"
        app:layout_constraintEnd_toEndOf="@+id/tv_dot"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="@+id/tv_operator_div"
        app:layout_constraintTop_toTopOf="@+id/tv_operator_times" />

    <TextView
        android:id="@+id/KR"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#03A9F4"
        android:gravity="right|center_vertical"
        android:paddingEnd="16dp"
        android:text="0"
        android:textColor="@android:color/white"
        android:textSize="58sp"
        app:layout_constraintBottom_toTopOf="@+id/flow"
        app:layout_constraintEnd_toEndOf="@+id/flow"
        app:layout_constraintStart_toStartOf="@+id/flow"
        app:layout_constraintTop_toTopOf="parent" />


</android.support.constraint.ConstraintLayout>
Copy the code

Flow_wrapMode is aligned. Textviews with ID KE can be aligned with views in the flow, textViews with ID KR can be aligned with the flow. Flow is also ConstraintHelper, so Flow is also a View, and you can set background, padding, etc. So what are the advantages of this layout? This layout view is all in one level, do not use ViewGroup, reduce the level.

Streaming APIs

This was required before 1.1

    val set = ConstraintSet()
        set.clone(constraintLayout)
        set.setTranslationZ(R.id.image, 32f)
        set.setMargin(R.id.image, ConstraintSet.START, 43)
        set.applyTo(constraintLayout)
Copy the code

ConstraintProperties 2.0 provides the ability to modify properties using streaming apis

 val properties = ConstraintProperties(findViewById(R.id.image))
        properties.translationZ(32f)
                .margin(ConstraintSet.START, 43)
                .apply()
Copy the code

MotionLayout

For MotionLayout, see a series of articles by Interpret Layout developer Nicolas Roard,

Introduction to MotionLayout (part I)

Introduction to MotionLayout (part II)

Introduction to MotionLayout (part III)

Defining motion paths in MotionLayout


Complete code refer to Github, like star oh


The resources

ConstraintLayout Deep Dive (Android Dev Summit ’18)

ConstraintLayout 2.0 by Nicolas Roard and John Hoford, Google EN

What’s New in ConstraintLayout (Google I/O’19)