beerBear
beerBear

Reputation: 979

Horizontally Align Two TextViews in MotionLayout

I am trying to build a motion scene where two textviews are transitioned in a collapsing toolbar style from an expanded state to a collapsed state on dragging up.

The two textviews are positioned with some margins to the left and right side of the screen respectively and should be horizontally aligned to each other.

The first textview on the left side has a margin from the back button arrow on its left and needs to be left aligned in parent.

The second textview on the right side has a margin to its right and between the end of parent.

The two textviews needs to have a transition where the textsize smoothly translates.

How can I achieve the same?

enter image description here

Motionlayout:

<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="match_parent"
    app:layoutDescription="@xml/motion_scene">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:clipToPadding="false"
        android:paddingTop="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/space"/>

    <View
        android:id="@+id/space"
        android:layout_width="0dp"
        android:layout_height="110dp"
        android:background="@color/white"
        android:fitsSystemWindows="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:foreground="?attr/selectableItemBackground"
        android:padding="20dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="80dp"
        android:layout_marginEnd="10dp"
        android:elevation="0dp"
        android:textAlignment="viewStart"
        android:textColor="@color/text_black"
        android:text="text view 1"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="@id/space"
        app:layout_constraintEnd_toStartOf="@id/text_view_2"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/text_view_2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="20dp"
        android:elevation="0dp"
        android:textAlignment="viewEnd"
        android:textColor="@color/text_black"
        android:text="text view 2"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="@id/space"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/text_view_1" />

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

Motionscene:

<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <Transition
        app:constraintSetEnd="@id/state_collapsed"
        app:constraintSetStart="@id/state_expanded">

        <OnSwipe
            app:dragDirection="dragUp"
            app:touchAnchorId="@id/recyclerview"
            app:touchAnchorSide="top" />

        <KeyFrameSet>
            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/text_view_1">
                <CustomAttribute
                    app:attributeName="textSize"
                    app:customFloatValue="20" />
            </KeyAttribute>

            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/text_view_2">
                <CustomAttribute
                    app:attributeName="textSize"
                    app:customFloatValue="20" />
            </KeyAttribute>
        </KeyFrameSet>

    </Transition>

 <ConstraintSet android:id="@+id/state_collapsed">

        <Constraint android:id="@id/back">

            <CustomAttribute
                app:attributeName="elevation"
                app:customDimension="6dp" />

        </Constraint>

        <Constraint
            android:id="@id/space"
            android:layout_height="?attr/actionBarSize"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <CustomAttribute
                app:attributeName="elevation"
                app:customDimension="6dp" />

        </Constraint>

        <Constraint
            android:id="@id/text_view_1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="80dp"
            android:textAlignment="viewStart"
            app:layout_constraintBottom_toBottomOf="@id/back"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@id/back">

            <CustomAttribute
                app:attributeName="textSize"
                app:customFloatValue="16" />

            <CustomAttribute
                app:attributeName="elevation"
                app:customDimension="6dp" />

        </Constraint>

        <Constraint
            android:id="@id/text_view_2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="20dp"
            android:textAlignment="viewEnd"
            app:layout_constraintBottom_toBottomOf="@id/text_view_1"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@id/text_view_1">

            <CustomAttribute
                app:attributeName="textSize"
                app:customFloatValue="16" />

            <CustomAttribute
                app:attributeName="elevation"
                app:customDimension="6dp" />

        </Constraint>

    </ConstraintSet>

    <ConstraintSet android:id="@+id/state_expanded">

        <Constraint android:id="@id/back">

            <CustomAttribute
                app:attributeName="elevation"
                app:customDimension="0dp" />

        </Constraint>

        <Constraint
            android:id="@id/space"
            android:layout_height="110dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <CustomAttribute
                app:attributeName="elevation"
                app:customDimension="0dp" />

        </Constraint>

        <Constraint
            android:id="@id/text_view_1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="80dp"
            app:layout_constraintBottom_toBottomOf="@id/space"
            app:layout_constraintEnd_toStartOf="@id/text_view_1"
            app:layout_constraintStart_toStartOf="parent">

            <CustomAttribute
                app:attributeName="textSize"
                app:customFloatValue="24" />

            <CustomAttribute
                app:attributeName="elevation"
                app:customDimension="0dp" />

        </Constraint>

    </ConstraintSet>

</MotionScene>

Upvotes: 0

Views: 2181

Answers (2)

macros013
macros013

Reputation: 769

I had a similar goal in my project and I used android:scaleX and android:scaleY attributes in Constraints, because changing textSize attr using CustomAttribute, didn't work smoothly. When you use android:scaleX and android:scaleY the view is scaled with a pivot in its center. So you may face alignment issues. You have to set android:transformPivotX="..." and android:transformPivotY="..." to change the default behavior. As a result, I had something like this in my case:

TextView I want to animate:

<TextView
                android:id="@+id/title_tv"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:text="Text"
                android:transformPivotX="0sp"
                android:transformPivotY="24dp"/>

Motion Scene:

<?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/collapsed"
        motion:constraintSetStart="@id/expanded">

        <OnSwipe
            motion:dragDirection="dragUp"
            motion:touchAnchorId="@+id/viewpager"
            motion:touchAnchorSide="top" />

    </Transition>

    <ConstraintSet android:id="@+id/expanded">
        <Constraint
            android:id="@id/toolbar_image_iv"
            android:layout_height="200dp"
            ...>
            <CustomAttribute
                motion:attributeName="imageAlpha"
                motion:customIntegerValue="255" />
        </Constraint>
        <Constraint
            android:id="@id/title_tv"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:scaleX="1.0"
            android:scaleY="1.0"
            ...>
        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/collapsed">
        <Constraint
            android:id="@id/toolbar_image_iv"
            android:layout_height="?attr/actionBarSize"
            ...>
            <CustomAttribute
                motion:attributeName="imageAlpha"
                motion:customIntegerValue="0" />
        </Constraint>
        <Constraint
            android:id="@id/title_tv"
            android:layout_width="wrap_content"
            android:layout_height="?attr/actionBarSize"
            android:scaleX="0.667"
            android:scaleY="0.667"
            ...>
        </Constraint>

    </ConstraintSet>

</MotionScene>

Upvotes: 2

Naveen Kommuri
Naveen Kommuri

Reputation: 406

Try with below code and add dummy elements to recycler view for expected behavior.

Motion Layout:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.motion.widget.MotionLayout
            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:showPaths="true"
            android:id="@+id/motionLayout"
            app:layoutDescription="@xml/scene01">


        <TextView
                android:textColor="@android:color/black"
                android:text="TextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/textView1"/>
        <ImageView
                android:layout_width="40dp"
                android:layout_height="40dp" app:srcCompat="@mipmap/ic_launcher"
                android:id="@+id/imageView"
                app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="24dp"
                android:layout_marginTop="24dp" app:layout_constraintTop_toTopOf="parent"/>
        <TextView
                android:text="TextView"
                android:textColor="@android:color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/textView2"/>
        <View android:layout_width="0dp"
              android:layout_height="1dp"
              android:id="@+id/view"
              android:layout_marginTop="20dp"
              app:layout_constraintTop_toBottomOf="@id/textView1"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintEnd_toEndOf="parent"/>

        <androidx.recyclerview.widget.RecyclerView android:layout_width="0dp"
                                                   android:id="@+id/recyclerview"
                                                   app:layout_constraintTop_toBottomOf="@id/view"
                                                   app:layout_constraintStart_toStartOf="parent"
                                                   app:layout_constraintEnd_toEndOf="parent"
                                                   app:layout_constraintBottom_toBottomOf="parent"
                                                   android:layout_height="0dp"/>
    </androidx.constraintlayout.motion.widget.MotionLayout>

MotionScene:

 <?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:constraintSetStart="@+id/start"
            motion:constraintSetEnd="@+id/end">
        <OnSwipe
                motion:touchAnchorId="@+id/recyclerview"
                motion:touchAnchorSide="top"
                motion:dragDirection="dragUp"/>
    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint android:id="@+id/textView1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    motion:layout_constraintStart_toEndOf="@+id/imageView"
                    android:layout_marginStart="20dp" android:layout_marginTop="16dp"
                    motion:layout_constraintTop_toBottomOf="@+id/imageView">
            <CustomAttribute motion:attributeName="textSize" motion:customFloatValue="25"/>
        </Constraint>
        <Constraint
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/textView2"
                motion:layout_constraintTop_toTopOf="@+id/textView1"
                motion:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="20dp">
            <CustomAttribute motion:attributeName="textSize" motion:customFloatValue="25"/>
        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint android:id="@+id/textView1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    motion:layout_constraintStart_toEndOf="@+id/imageView"
                    android:layout_marginStart="20dp"
                    motion:layout_constraintTop_toTopOf="@+id/imageView">
            <CustomAttribute motion:attributeName="textSize" motion:customFloatValue="15"/>
        </Constraint>
        <Constraint
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/textView2"
                motion:layout_constraintTop_toTopOf="@+id/textView1"
                motion:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="20dp">
            <CustomAttribute motion:attributeName="textSize" motion:customFloatValue="15"/>
        </Constraint>
    </ConstraintSet>

</MotionScene>

Upvotes: 1

Related Questions