14. Apr 2021Android

Android development tips: Constraint Layout helpers II. part

I brought you the second part of an article about Constraint Layout Helpers, which are type of ViewGroup, which allows you to create complex layouts. Full power of Constraint Layouts comes from his helpers, so i've prepared the article about them.

Peter ŠulyAndroid developer

In the first part of the introduction to Constraint Layout Helpers, I introduced Group, Guidline, Layer and Barrier. If you haven't seen them yet, don't forget to visit the first part!

Placeholder

Placeholder is helper which can help to position another views (that's where it's name comes from - it's "holding place" for view). When you call setContentId() method on placeholder object somewhere in your code, placeholder objects become the content view. If content view exists on the screen already, it will be treated as gone on its original location after setContentId() call.

E.g. when some of the upper circles is clicked, placeholder.setContentId() is called, placeholder becomes the clicked view (clicked view moves to placeholder's position, inherits placeholder's constraints).

 

val onClickListener = View.OnClickListener { view ->
    TransitionManager.beginDelayedTransition(content) //For animation effect
    placeholder.setContentId(view.id) //Set view to placeholder
}

image1.setOnClickListener(onClickListener)
image2.setOnClickListener(onClickListener)
image3.setOnClickListener(onClickListener)
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    android:id="@+id/content"
    android:paddingTop="16dp">

    <ImageView
        android:id="@+id/image1"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/background_badge_green"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/image2"/>

    <ImageView
        android:id="@+id/image2"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/background_badge_red"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toEndOf="@id/image1"
        app:layout_constraintEnd_toStartOf="@id/image3"/>

    <ImageView
        android:id="@+id/image3"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/background_badge_black"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toEndOf="@id/image2"
        app:layout_constraintEnd_toEndOf="parent"/>

    <androidx.constraintlayout.widget.Placeholder
        android:id="@+id/placeholder"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginBottom="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Don't miss my #goodroidtips I. or II. ,articles about useful tips and tricks for Android developers.

Flow

Similar to Group, Flow takes views ids into constraint_referenced_ids attribute and automatically creates Chain behavior between them. Chain behavior (way how views overflow the Flow) is determined by wrapMode attribute of Flow.

//Default behavior of Flow, just creates chain between referenced views
//If views do not fit into Flow, they will overflow the Flow
app:flow_wrapMode="none"     

OR

//If views do not fit into Flow, they jump on another line and take place evenly 
app:flow_wrapMode="chain"

OR

//If views do not fit into Flow, they jump on another line and align to create rows and columns 
app:flow_wrapMode="aligned"

As we said, Flow automatically creates chain between it's elements. Chain style can be configured by flow_horizontalStyle or flow_verticalStyle attribute based on Flow's orientation. Behavior of chain style is basically the same as for chains.

For horizontal Flow

app:flow_horizontalStyle="spread" //Default

OR

app:flow_horizontalStyle="packed"

OR

app:flow_horizontalStyle="spread_inside"


 

First and last chain in the Flow can have different style attributes  as others e.g. for the first chain we define flow_firstHorizontalStyle="spread_inside" for the last chain we define flow_lastHorizontalStyle="packed". All the others chains in the middle took default flow_horizontalStyle="spread" because we did not defined anything.

<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <androidx.constraintlayout.helper.widget.Flow
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:flow_wrapMode="chain"
        app:flow_maxElementsWrap="2"
        app:flow_firstHorizontalStyle="spread_inside"
        app:flow_lastHorizontalStyle="packed"
        app:constraint_referenced_ids="circle1, ..., circle9"/>

    //Include 9 circles
    <include
        android:id="@+id/circle1"
        layout="@layout/circle"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

Flow can be customized by many other attributes e.g.

//Orientation of Flow, can be vertical or horizontal
android:orientation="vertical"

//Maximum number of elements in one row or column
app:flow_maxElementsWrap="3"

//Vertical or horizontal gap between referenced views
app:flow_horizontalGap="20dp"
app:flow_verticalGap="20dp"

Bonus

Circular positioning

Circular positioning attributes allows you to constraint view to another view at specified angle and distance. Important attributes when we want to circularly position views are

app:layout_constraintCircle="@id/baseImage" //Constrained base view
app:layout_constraintCircleRadius="90dp"    //Radius of the circle
app:layout_constraintCircleAngle="45"       //Degree of the circle on which we display the view

E.g. positioning image1, image2 and image 3 around baseImage in 0, 45 and 90 degree.

<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <ImageView
        android:id="@+id/image1"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:src="@drawable/background_badge_green"
        app:layout_constraintCircle="@id/baseImage"
        app:layout_constraintCircleRadius="90dp"
        app:layout_constraintCircleAngle="0"/>

    <ImageView
        android:id="@+id/image2"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:src="@drawable/background_badge_green"
        app:layout_constraintCircle="@id/baseImage"
        app:layout_constraintCircleRadius="90dp"
        app:layout_constraintCircleAngle="45"/>

    <ImageView
        android:id="@+id/image3"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:src="@drawable/background_badge_green"
        app:layout_constraintCircle="@id/baseImage"
        app:layout_constraintCircleRadius="90dp"
        app:layout_constraintCircleAngle="90"/>

    <ImageView
        android:id="@+id/baseImage"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginBottom="16dp"
        android:layout_marginStart="16dp"
        android:src="@drawable/background_badge_black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Resources: https://developer.android.com/reference

Peter ŠulyAndroid developer