Search code examples
androidandroid-constraintlayoutandroid-motionlayout

MotionLayout with 3 Views and Barrier


I want create some view with 3 cards:

  • Top card with detail informaton of some object
  • left and right cards contains only main informations.

If i click on left or right card, then it comes on place of top card (the cards switch their places)

But if i click on right card, then the animation see very strange

First after some animations it looks as expected

For the animation i have created motion scene

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

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

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/right_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/left_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="left_card,right_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

    </ConstraintSet>

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

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/center_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="left_card,center_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/left_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

    </ConstraintSet>

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

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/center_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="right_card,center_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/right_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

    </ConstraintSet>

    <ConstraintSet
        android:id="@+id/swipeFromLeftToBottom"
        app:deriveConstraintsFrom="@id/center_on_left">

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/right_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/left_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="left_card,right_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

    </ConstraintSet>

    <ConstraintSet
        android:id="@+id/swipeFromRightToBottom"
        app:deriveConstraintsFrom="@id/center_on_right">

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/right_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/left_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="left_card,right_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

    </ConstraintSet>

    <ConstraintSet
        android:id="@+id/swipeFromBottomToLeft"
        app:deriveConstraintsFrom="@id/center_on_bottom">

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/center_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="left_card,center_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/left_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

    </ConstraintSet>

    <ConstraintSet
        android:id="@+id/swipeFromRightToLeft"
        app:deriveConstraintsFrom="@id/center_on_right">

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/center_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="left_card,center_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/left_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

    </ConstraintSet>

    <ConstraintSet
        android:id="@+id/swipeFromBottomToRight"
        app:deriveConstraintsFrom="@id/center_on_bottom">

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/center_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="right_card,center_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/right_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

    </ConstraintSet>

    <ConstraintSet
        android:id="@+id/swipeFromLeftToRight"
        app:deriveConstraintsFrom="@id/center_on_left">

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/barrier" />

            <Transform
                android:scaleX="1"
                android:scaleY="1" />

        </Constraint>

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toStartOf="@id/center_card"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

        <Constraint
            android:id="@id/barrier"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="right_card,center_card" />

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

            <Layout
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_margin="@dimen/layout_margin"
                app:layout_constraintBottom_toTopOf="@id/barrier"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.2"
                app:layout_constraintStart_toEndOf="@id/right_card"
                app:layout_constraintTop_toTopOf="parent" />

            <Transform
                android:scaleX="0.90"
                android:scaleY="0.90" />

        </Constraint>

    </ConstraintSet>

</MotionScene>

and motion layout

<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/motion_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/scene_swipe"
    app:motionDebug="SHOW_ALL">

    <FrameLayout
        android:id="@+id/left_card"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="@drawable/cave"
        android:backgroundTint="@color/primaryDarkGreen"
        app:layout_constraintBottom_toTopOf="@id/barrier"
        android:scaleX="0.90"
        android:scaleY="0.90">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginEnd="40dp"
            android:layout_marginBottom="40dp"
            android:text="Left card" />

    </FrameLayout>

    <FrameLayout
        android:id="@+id/right_card"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="@drawable/cave"
        android:backgroundTint="@color/primaryDarkSlateBlue"
        app:layout_constraintBottom_toTopOf="@id/barrier"
        android:scaleX="0.90"
        android:scaleY="0.90">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginEnd="40dp"
            android:layout_marginBottom="40dp"
            android:text="Right card" />

    </FrameLayout>

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="bottom"
        app:constraint_referenced_ids="left_card,right_card" />

    <FrameLayout
        android:id="@+id/center_card"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@drawable/cave"
        android:backgroundTint="@color/primaryPink"
        app:layout_constraintTop_toBottomOf="@id/barrier">

        <TextView
            android:id="@+id/likeButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginEnd="40dp"
            android:layout_marginBottom="40dp"
            android:text="Top card" />

    </FrameLayout>

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

in my fragment i set the animated based on the last animation

private int curState = R.id.swipeFromLeftToBottom;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.swipe_card, container, false);
    ...

    leftCard.setOnClickListener(view -> swipe(1));
    rightCard.setOnClickListener(view -> swipe(2));
    centerCard.setOnClickListener(view -> swipe(3));

    motionLayout.transitionToState(R.id.swipeFromLeftToBottom);

    motionLayout.setTransitionListener(new TransitionAdapter() {
        @Override
        public void onTransitionCompleted(MotionLayout motionLayout, int swipeId) {
            curState = swipeId;
        }
    });

    return rootView;
}

private void swipe(int card) {
    // swipeFromLeftToBottom    Left Right Bottom
    // swipeFromRightToBottom   Left Right Bottom
    // swipeFromBottomToLeft    Bottom Left Right
    // swipeFromRightToLeft     Bottom Left Right
    // swipeFromBottomToRight   Right Bottom Left
    // swipeFromLeftToRight     Right Bottom Left

    // ignore swipe if big layout clicked
    if ((curState == R.id.swipeFromLeftToBottom || curState == R.id.swipeFromRightToBottom) && card == 3 ||
            (curState == R.id.swipeFromBottomToLeft || curState == R.id.swipeFromRightToLeft) && card == 2 ||
            (curState == R.id.swipeFromBottomToRight || curState == R.id.swipeFromLeftToRight) && card == 1) {
        return;
    }

    int motion =
            curState == R.id.swipeFromLeftToBottom || curState == R.id.swipeFromRightToBottom ?
                    card == 1 ? R.id.swipeFromBottomToRight : R.id.swipeFromBottomToLeft :

                    curState == R.id.swipeFromBottomToLeft || curState == R.id.swipeFromRightToLeft ?
                            card == 3 ? R.id.swipeFromLeftToBottom : R.id.swipeFromLeftToRight :

                            card == 2 ? R.id.swipeFromRightToLeft : R.id.swipeFromRightToBottom;

    motionLayout.transitionToState(motion);
}

Where do i mistake?

Is it because of Barrier, that is not rendered by first animation?

How can i solve it?

Please, help.


Solution

  • Set of

    app:layout_constraintHeight_percent="0.8"
    

    to all views that are by transition on place of big layout has solved the problem