LoginSignup
1
0

More than 3 years have passed since last update.

【Android】MotionLayoutでMaterialDesignのTextFieldもどきを実装する

Last updated at Posted at 2019-05-02

はじめに

昨年のGoogleI/O 2018でConstraintLayout2.0が紹介されましたが、その中でMotionLayoutという新たなAndroidのアニメーションの仕組みが公開されました。
今回は、そのMotionLayoutを使用して、MaterialDesignのTextFieldっぽいものを実装してみたので、それについて実装方法などを書き連ねたいと思います。
※またMotionLayoutはまだalpha版なので、今回は、constraint-layout:2.0.0-alpha4での実装を紹介しますが、新たなバージョンがリリースされた場合、実装が変わる可能性もあるので、ご了承ください。

作ったもの

こちらのような項目がリストのように表示されていて、画面遷移させることなく閲覧モードから入力モードにすることができます。それぞれのモード間の遷移時のアニメーションでMotionLayoutを使って実装しています。

挙動としては、
1. 右のテキスト部分をクリックした場合、入力モードになる
2. フォーカスが外れたら、閲覧モードに戻る
(フォーカスは別のテキストFieldをクリックするか、下のボタンを押下すると外れる)

ソースコードはこちらに上げています。参考にされたい方はこちらをご覧ください。
https://github.com/youmitsu/MotionLayoutMaterialTextField

MotionLayoutの概要

MotionLayoutについては、多くの方が仕組みについての記事を公開しているので、ここでは省略します。

基本的なステップとしては、以下となります。

  1. アニメーションさせる前のレイアウトをMotionLayoutを親として実装
  2. Sceneのxmlを作成
  3. 1のattributeに2のSceneファイルを指定

実装方法

1. ConstraintLayout2.0のインストール

まずは、ConstraintLayout2.0をインストールします。
AndroidXと、SupportLibraryの2つのArtifactsがあるので、自身のプロジェクトに応じて、使い分けましょう。

リリースノートはこちら
https://androidstudio.googleblog.com/2019/04/constraintlayout-200-alpha-4.html

AndroidXの場合

dependencies {
    ...
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha4'
}

SupportLibraryの場合

dependencies {
    ...
    implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha4'
}

2. 初期状態のviewを作成

まずは、こちらのクリックされる前のレイアウトを作っていきます。

xmlは以下になります。MotionLayoutはConstraintLayoutの子クラスなので、基本的にはConstraintLayoutでレイアウトを作るのと同じ要領で実装します。

※サンプルリポジトリでは、Databindingを使って入力データをバインディングしているので、<layout><data>タグが入っていますが、無くても動きます。

layout_custom_motion_edittext.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="data" type="jp.co.youmitsu.myapplication.CustomMotionEditTextLayout"/>
    </data>

    <androidx.constraintlayout.motion.widget.MotionLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/motion_layout"
    >

        <TextView
                android:id="@+id/nickname_title"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:gravity="start|center_vertical"
                android:layout_marginStart="10dp"
                android:textColor="#808080"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toStartOf="@id/nickname_value"
                android:text="@{data.title}"
        />

        <TextView
                android:id="@+id/nickname_value"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:gravity="end|center_vertical"
                android:textSize="15sp"
                android:layout_marginEnd="10dp"
                android:ellipsize="end"
                android:singleLine="true"
                android:maxEms="15"
                android:text="@{data.value}"
                android:clickable="true"
                android:focusable="true"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@id/nickname_title"/>

        <androidx.appcompat.widget.AppCompatEditText
                android:id="@+id/edit_text"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:alpha="0"
                android:visibility="invisible"
                android:textSize="15sp"
                android:inputType="text"
                android:text="@{data.value}"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"/>

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

3. アニメーション前後のConstraintの状態とTransitionを表すMotionSceneファイルを作成

次に、タップしてアニメーションし始める時と終わった時のレイアウトのConstraintSetとTransitionをMotionSceneという形で定義します。
MotionSceneファイルはxmlで記述するので、res/xml配下に新たなファイルを作成します。

xmlファイルは以下です。
@id/startがアニメーションする前のConstraintSet。@id/endがアニメーションした後のConstraintSetを表しています。
今回の場合、start時のConstraintと上記2で定義したMotionLayout内の要素のConstraintは一致することになります。

@id/startのConstraintSetでのレイアウト:

@id/endのConstraintSetでのレイアウト:

layout_custom_motion_edittext_scene.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
        xmlns:motion="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android">

    <Transition
            motion:constraintSetStart="@id/start"
            motion:constraintSetEnd="@id/end"
            motion:duration="200">

        <OnClick motion:targetId="@id/nickname_value"
                 motion:clickAction="transitionToStart|transitionToEnd"/>

    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
                android:id="@+id/nickname_title"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:textSize="@dimen/text_size_normal"
                android:layout_marginStart="5dp"
                motion:layout_constraintTop_toTopOf="parent"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintEnd_toStartOf="@id/nickname_value">
            <CustomAttribute
                    motion:attributeName="textSize"
                    motion:customFloatValue="15"
            />
            <CustomAttribute
                    motion:attributeName="textColor"
                    motion:customColorValue="#808080"
            />
        </Constraint>

        <Constraint
                android:id="@+id/nickname_value"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:textSize="15sp"
                android:layout_marginEnd="10dp"
                motion:layout_constraintTop_toTopOf="parent"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintStart_toEndOf="@id/nickname_title"/>

        <Constraint
                android:id="@+id/edit_text"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:alpha="0"
                android:visibility="invisible"
                android:textSize="15sp"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent"
                motion:layout_constraintBottom_toBottomOf="parent"/>
    </ConstraintSet>

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

        <Constraint
                android:id="@id/nickname_title"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="5dp"
                motion:layout_constraintBottom_toTopOf="@id/edit_text"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintEnd_toEndOf="parent">
            <CustomAttribute
                    motion:attributeName="textSize"
                    motion:customFloatValue="12"
            />
            <CustomAttribute
                    motion:attributeName="textColor"
                    motion:customColorValue="@color/colorAccent"
            />
        </Constraint>

        <Constraint
                android:id="@+id/nickname_value"
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:textSize="15sp"
                android:alpha="0"
                android:visibility="invisible"
                motion:layout_constraintTop_toTopOf="parent"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent"/>

        <Constraint
                android:id="@id/edit_text"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:alpha="1"
                android:visibility="visible"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent"
                motion:layout_constraintBottom_toBottomOf="parent"/>
    </ConstraintSet>

</MotionScene>

4. MotionLayoutのattributeにlayoutDescriptionとして3のSceneファイルを設定

アニメーションする対象のview(MotionLayout)とMotionSceneファイルを作成できたら、それぞれを結びつけるために、MotionLayoutのlayoutDescription属性を設定します。こうすることで、画像のようなアニメーションが実現できます。

layout_custom_motion_edittext.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="data" type="jp.co.youmitsu.myapplication.CustomMotionEditTextLayout"/>
    </data>

    <androidx.constraintlayout.motion.widget.MotionLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/motion_layout"
            app:layoutDescription="@xml/layout_custom_motion_edittext_scene"  // これを追加
    >
    ...
    </androidx.constraintlayout.motion.widget.MotionLayout>
</layout>

まとめ

今回は、MotionLayoutを使って、MaterialDesignのTextFieldの拡張っぽいものを実装してみました。アニメーションに関するコードをほとんどxmlだけで完結させることができるので、とても使いやすそうだなという印象です。
これからも色々機能追加があるみたいなので、楽しみです。
最後まで読んでいただきありがとうございました。

参考にさせていただいたもの

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0