Help us understand the problem. What is going on with this article?

MotionLayoutを使って無限Pagerを実装する

この記事は、はてなエンジニア Advent Calendar 2019 の 23 日目の記事です。

MotionLayout でこのような無限 Pager を実装してみたというちょっとしたお遊びです。

MotionLayout で Pager を作る考え方

  • 表示させているもの + 前後に表示されず隠れている View を用意する

  • スワイプさせて次の View が表示されたタイミングで、表示をそのままに Motion を最初の状態に戻す

といった感じで Pager を実現させています。

実装

MotionScene の定義

ConstraintSet を最初の状態、右にスワイプした時、左にスワイプした時の 3 パターン用意します。
最初の状態を起点に Transition を右にスワイプした時、左にスワイプした時で 2 つ用意します。  

<?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">

    <ConstraintSet android:id="@+id/base_state">
        <Constraint android:id="@id/centerView">
            <Layout
                android:layout_width="320dp"
                android:layout_height="320dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />

        </Constraint>

        <Constraint android:id="@id/leftView">
            <Layout
                android:layout_width="320dp"
                android:layout_height="320dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </Constraint>

        <Constraint android:id="@id/rightView">
            <Layout
                android:layout_width="320dp"
                android:layout_height="320dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/move_left_to_right">
        <!-- 省略 -->
        <!-- 左へスワイプした時のViewの状態を定義する -->
    </ConstraintSet>

    <ConstraintSet android:id="@+id/move_right_to_left">
        <!-- 省略 -->
        <!-- 右へスワイプした時のViewの状態を定義する -->
    </ConstraintSet>

    <Transition
        motion:constraintSetEnd="@id/move_left_to_right"
        motion:constraintSetStart="@id/base_state">
        <OnSwipe
            motion:dragDirection="dragRight"
            motion:onTouchUp="autoCompleteToStart"
            motion:touchAnchorId="@id/centerView"
            motion:touchAnchorSide="right" />
    </Transition>

    <Transition
        motion:constraintSetEnd="@id/move_right_to_left"
        motion:constraintSetStart="@id/base_state">
        <OnSwipe
            motion:dragDirection="dragLeft"
            motion:onTouchUp="autoCompleteToStart"
            motion:touchAnchorId="@id/centerView"
            motion:touchAnchorSide="left" />
    </Transition>

</MotionScene>

アイテムの表示処理の実装

アニメーションが完了したら(スワイプで完全に移動した時)、リストの位置をずらしてアニメーションを最初に戻す (motionLayout?.progress = 0F) だけです。

motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
    override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
        when (currentId) {
            R.id.move_left_to_right -> {
                if (currentPosition > 0) {
                    currentPosition--
                } else {
                    currentPosition = itemList.lastIndex
                }
                motionLayout?.progress = 0F
                updateView()
            }
            R.id.move_right_to_left -> {
                if (currentPosition < itemList.lastIndex) {
                    currentPosition++
                } else {
                    currentPosition = 0
                }
                motionLayout?.progress = 0F
                updateView()
            }
        }
    }
})

MotionLayout を使っているため KeyFrameSet を調整することで様々なアニメーションを実現できます。

全コードはこちらに公開しています
https://github.com/NUmeroAndDev/MotionLayoutPager-android

MotionLayout で無限 Pager を実装してみました。
プロダクトに当てはめるには考慮すべきことや表現できないパターンがありそうですが、ぬるぬるアニメーションさせるなら MotionLayout は便利だなと思いました。
来年には MotionEditor が使えるようになりそうなので、MotionLayout をもっと使っていきたいですね :dancer:

明日のはてなエンジニア Advent Calendar 2019 は @hitode909 さんです!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away