Androidアプリ開発 初級編 (データバインディング)
前回に引き続きUdacityで紹介されているAboutMeアプリ開発を通じてデータバインディングの基本的な使い方について学習します。
■事前準備
Android StudioからNew ProjectでEmpty Activity ・ Kotlin を選択してプロジェクトを作成してください。
アプリモジュールのbuild.gradleで以下のコードを追加します。
android{ ..... dataBinding { enabled = true } ..... }
こちらはデータバインディングを実装するのに必要なものになります。
■サンプルアプリの動作
■データクラスの作成
アプリ内で使用するデータクラスを作成します。
データクラスは後ほど、データバインディングに使用することになります。
data class MyName(var name: String = "", var nickname: String = "")
■レイアウトファイルを作成する
下記のレイアウトファイルを編集します。
res -> layout -> activity_main.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" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="myName" type="com.tomostudy.aboutme.MyName" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" tools:context=".MainActivity"> <TextView android:id="@+id/name_text" style="@style/NameStyle" android:text="@={myName.name}" /> <EditText android:id="@+id/nickname_edit" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/what_is_your_name" android:inputType="textPersonName" /> <Button android:id="@+id/button" style="@style/Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@font/roboto" android:text="@string/done" /> <TextView android:id="@+id/nickname_text" style="@style/NameStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="invisible" android:text="@={myName.nickname}" /> <ImageView android:id="@+id/star_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/yellow_star" app:srcCompat="@android:drawable/btn_star_big_on" /> <ScrollView android:id="@+id/bio_scroll" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/bio_text" style="@style/NameStyle" android:paddingBottom="25dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:lineSpacingMultiplier="1.2" android:text="@string/bio" /> </ScrollView> </LinearLayout> </layout>
一つ一つ要素を見ていきます
データバインディングを使用するにはまず<layout>から開始して、要素にはレイアウト内で使用するname spaceを定義します。
<layout 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"> </layout>
<data>には前章で作成したデータクラスを定義するこで、レイアウト内でデータクラスのパラメーターをbindできるようになります。
<variable>が実際のデータクラスを定義する部分になり、nameはレイアウト内で使用するidのようなもの、typeはデータクラスをpackage名から指定します。
package名は環境に合わせて変更してください。
<data> <variable name="myName" type="com.tomostudy.aboutme.MyName" /> </data>
<LinearLayout>はこのレイアウトの本体になります。今回は子要素をverticalで並べます。
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" tools:context=".MainActivity"> </LinearLayout>
<TextView> 名前を表示するWigetです。styleを作成することで同じように表示したいTextViewに設定することで共通化が出来るようになります。
textにはmyName.nameとすることで<data>で定義したデータクラスのパラメータをbindするように指定しています。
<TextView android:id="@+id/name_text" style="@style/NameStyle" android:text="@={myName.name}" />
<EditText>はテキストを入力できるWigetです。hintには入力欄に表示するテキストを指定でき、inputTypeにはnumberPasswordなど入力内容に応じたtypeを指定できます。今回はニックネームの入力のためtextPersonNameを選択します。inputType詳細については公式のリファレンスを確認してください。
<EditText android:id="@+id/nickname_edit" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/what_is_your_name" android:inputType="textPersonName" />
<Button>はその名の通りボタンです。こちらもstyleを使用していますが、android標準で定義されているボタンのstyleを指定しています。textにはボタンに表示するテキストを指定します。fontFamily=”@font/roboto”ではfontを指定することが出来るものですが特に指定が必要ない場合は無くても大丈夫です。
<Button android:id="@+id/button" style="@style/Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@font/roboto" android:text="@string/done" />
nicknameの<TextView>も同様にデータバインディングを行います。ここではvisibilityにinvisibleを設定して非表示にしていますが、表示領域は確保されるようになります。goneを指定すると非表示+表示領域が確保されません。
<TextView android:id="@+id/nickname_text" style="@style/NameStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="invisible" android:text="@={myName.nickname}" />
<ImageView>は画像を表示するWigetです。srcCompatに表示する画像のdrawableリソースを指定します。contentDescriptionはandroidのユーザー補助機能をサポートするためのものです。私も使用したことがないものでしたが、スクリーンリーダーを使用すると要素の説明をする際に読み上げてくれたりするそうです。詳しくは公式のガイドを参照してください。
<ImageView android:id="@+id/star_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/yellow_star" app:srcCompat="@android:drawable/btn_star_big_on" />
<ScrollView>はスクロールさせたいようなUIを作成するときに使います。領域をスクロールすることで子要素がスクロールされて表示されます。今回はTextViewに長い文章を設定することで動作を確認出来るようにしましょう。
<ScrollView android:id="@+id/bio_scroll" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/bio_text" style="@style/NameStyle" android:paddingBottom="25dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:lineSpacingMultiplier="1.2" android:text="@string/bio" /> </ScrollView>
■データ処理を実装
MainActivity.ktを以下のように実装します。
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var myName: MyName override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) myName = MyName(resources.getString(R.string.my_name)) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.myName = myName binding.button.setOnClickListener { addNickname(it) } } private fun addNickname(view: View) { binding.apply { nicknameText.text = nicknameEdit.text nicknameEdit.visibility = View.GONE view.visibility = View.GONE nicknameText.visibility = View.VISIBLE } // Hide the keyboard. val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(view.windowToken, 0) } }
こちらも一つずつ処理内容を見ていきましょう。
binding変数はActivityMainBindingを宣言しています。
ActivityMainBindingはデータバインディングをサポートしてくれるライブラリです。ActivityMainBindingは自動で作成され、layout名 + Bindingがクラス名になります。
private lateinit var binding: ActivityMainBinding
onCreate内でまずデータクラスを初期化し、DataBindingUtilクラスを使用してsetContentViewすることで、ActivityMainBindingの参照を取得することが出来ます。取得したインスタンスにはデータクラスを設定します。
ActivityMainBindingにより、findViewbyIdを呼び出す必要がなくなります。
最後にbindingインスタンスのbuttonに押下処理を設定していきます。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) myName = MyName(resources.getString(R.string.my_name)) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.myName = myName binding.button.setOnClickListener { addNickname(it) } }
addNickNameには引数でボタンのViewが渡されます。
binding.apply{}ブロック内にデータの表示更新処理を実装していきます。applyブロック内ではthisの参照先がbindingになっていることに注意しましょう。
最後にInputMethodManagerを呼び出して、入力時に表示されるキーボードを非表示にする処理を実装しています。
private fun addNickname(view: View) { binding.apply { nicknameText.text = nicknameEdit.text nicknameEdit.visibility = View.GONE view.visibility = View.GONE nicknameText.visibility = View.VISIBLE } // Hide the keyboard. val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(view.windowToken, 0) }
これで一通りの処理が完了です。
それでは動作確認して試してみましょう。
■まとめ
・<layout>、<data>を使ってデータバインディングをレイアウトファイルに定義する
・Bindingオブジェクトが自動生成され、DataBindingUtilを使って参照を取得する。面倒なfindViewByIdは必要なくなる。
今回はこんなイメージで覚えていれば良いと思います。ですがまだまだデータバインディングは便利に使うことができますので、後々紹介していきたいと思います。
それではまた。