【Android】MotionLayout을 만져보세요

MotionLayout이란?



View의 애니메이션을 관리할 수 있는 ViewGroup입니다.

ConstraintLayout의 서브 클래스에서 ConstraintLayout을 사용하는 레이아웃이라면 MotionLayout으로 완전히 바꿀 수 있습니다.

※MotionLayout은 API 레벨 14와의 하위 호환성이 있습니다.

소개



gradle에 종속성을 추가합니다.
MotionLayout은 ConstraintLayoutのライブラリの2.0.0から導入 되었기 때문에 ConstraintLayout의 버전을 올려야합니다.
※2020/3/5의 시점에서의 최신 버전은 2.0.0-beta4였습니다.

build.gradle(app)
// AndroidXを導入している場合はこちら
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'

// それ以外はこちら
implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta4'

MotionLayout에서 이용하는 애니메이션 파일※1을 넣기 위한 XML 폴더※2를 작성합니다.

※1 엄밀하게는 애니메이션 개시시와 종료시의 View 상태를 기술한 파일입니다.
※2 AndroidStudio의 메뉴에서, File > New > Folder > XML Resource Folder로 작성.

만져보세요



정교한 레이아웃을 만드는 시간이 없으므로 기본 activity_main.xml에 MotionLayout을 적응해보십시오.

activity_main.xml
<!-- ConstraintLayoutからMotionLayoutに置き換えました。 -->
<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:context=".MainActivity"
    >

    <!-- アニメーションさせたいViewにidをつけます。 -->
    <TextView
        android:id="@+id/main_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

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

그런 다음 res/xml/ 에 애니메이션 파일을 만듭니다.

res/xml/motion_scene_main.xml
<!-- MotionScene※3タグで大枠を作ります。xmlns:motion="~"を忘れずに。 -->
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto"
    >

    <!-- 開始、終了アニメーションの指定と実行時間を記述します。 -->
    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000"
        >
        <!-- トリガーとなるアクションとViewのidを紐付けます。 -->
        <OnClick motion:targetId="@id/main_text_view"/>
    </Transition>

    <!-- アニメーション開始時のViewの状態を記述します。 -->
    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            />
    </ConstraintSet>

    <!-- アニメーション終了時のViewの状態を記述します。 -->
    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            />
    </ConstraintSet>

</MotionScene>

※3 xmlns:motion="~"를 포함하면 MotionScene이라는 명명이 아니라 어떤 이름을 붙여도 인식해 줍니다.

마지막으로 MotionLayout과 애니메이션 파일을 연결합니다.

activity_main.xml
<androidx.constraintlayout.motion.widget.MotionLayout

     省略 

    app:layoutDescription="@xml/motion_scene_main"
    >

기기에서 확인하려고합니다.
텍스트를 탭하면 제대로 애니메이션하고 있네요.


MotionLayout은 View 자체를 애니메이션화할 뿐만 아니라 View 매개변수를 동적으로 변경할 수 있습니다.
애니메이션 파일에 약간의 손을 추가해 봅니다.

res/xml/motion_scene_main.xml
<?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/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000"
        >
        <OnClick motion:targetId="@id/main_text_view" />
    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            >
            <!-- アニメーション開始時のテキストの色を指定します。 -->
            <CustomAttribute
                motion:attributeName="textColor"
                motion:customColorValue="#ff0000"
                />
        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            >
            <!-- アニメーション終了時のテキストの色を指定します。 -->
            <CustomAttribute
                motion:attributeName="textColor"
                motion:customColorValue="#0000ff"
                />
        </Constraint>
    </ConstraintSet>

</MotionScene>


CustomAttribute 라는 태그는, attributeName 에 지정한 View 의 파라미터를 애니메이션에 맞추어 변경해 줍니다.
색상 변경 외에도 애니메이션에 맞게 뷰를 변형하거나 이미지를 크로스 페이드할 수 있습니다.
CustomAttribute를 복수 지정하는 것도 물론 가능합니다.

여기에서는 특히 만지지 않으므로 신경이 쓰이는 분은 조사해 보세요.

추가 1



위에서는 애니메이션 파일에 트리거가 되는 View를 연결하고 있었습니다만, 실제의 프로젝트에서는, 코드로 지정하는 편이 일반적이라고 생각합니다.

res/xml/motion_scene_main.xml
<!-- ここは削除して構いません。 -->
<OnClick motion:targetId="@id/main_text_view" />

MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val motionLayout = findViewById<MotionLayout>(R.id.main_motion_layout)
        // トリガーとなるViewのクリック時に、指定したid※4のアニメーションを開始します。
        findViewById<TextView>(R.id.main_text_view).setOnClickListener {
            // 現在のアニメーションの状態を判定して、開始するアニメーションを決めます。
            if (motionLayout.currentState == R.id.start) {
                motionLayout.transitionToEnd()
            } else {
                motionLayout.transitionToStart()
            }
        }
    }
}

※4 애니메이션 파일로 정의한 ConstraintSet의 id입니다.

추가 2



요구 사항에 따라 애니메이션 진행 상황을 감지하고 싶을 수도 있습니다.
그럴 때는 MotionLayout.TransitionListener 를 설정하면 콜백을 얻을 수 있습니다.

MainActivity.kt
motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
    override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) {
        // トリガーした時に呼ばれます。
    }

    override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {
        // アニメーション開始時に呼ばれます。
    }

    override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) {
        // アニメーション中に呼ばれます。
    }

    override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
        // アニメーション終了時に呼ばれます。
    }
})

감상



MotionLayout의 매력은 거의 기존의 구현에 영향을 미치지 않고, 선형 보완의 매끄러운 애니메이션으로 간편하게 풍부한 표현을 할 수 있는 곳에 있다고 생각했습니다. 또한 학습 비용이 비교적 낮은 것도 ◎.

참고

좋은 웹페이지 즐겨찾기