LoginSignup
12
11

More than 5 years have passed since last update.

DialogFragmentでData Bindingを使う

Last updated at Posted at 2018-11-12

DialogFragmentのカスタムレイアウトでEditTextの値を取得しようとして苦戦したのが、Data Bindingを使うといい感じに解決できそうだったのでメモ兼ねて記事を残しておきます。
もっと根本的に良い解決方法があれば遠慮なくコメントください。

今回使用するカスタムレイアウト

fragment_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content">
    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Dialog"
            android:textSize="20sp" />
    <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/edit_text"
            android:hint="入力" />
</LinearLayout>

こんな感じ
今回は例として、OKを押すと入力した内容がToastとして表示されるダイアログを考えていきます。

問題のコード

EditTextの取得にはKotlin Android Extensionsを利用しています。

MyDialogFragment.kt
class MyDialogFragment : DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity)
        activity?.let {
            val view = it.layoutInflater.inflate(R.layout.fragment_dialog, null)
            builder.setView(view)
                .setPositiveButton("OK") { _, _->
                    val text = edit_text.text.toString() // ←これだと落ちる
                    Toast.makeText(activity, text, Toast.LENGTH_SHORT).show()
                }
        }
        return builder.create()
    }
}

一見普通に動きそうですが、edit_textの取得で落ちます。

java.lang.IllegalStateException: edit_text must not be null

本来nullが入らないはずのedit_textがnullなようです。

とりあえずの解決策

View.findViewByIdを使います。

MyDialogFragment.kt
class MyDialogFragment : DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity)
        activity?.let {
            val view = it.layoutInflater.inflate(R.layout.fragment_dialog, null)
            builder.setView(view)
                .setPositiveButton("OK") { _, _->
                    val editText: EditText = view.findViewById(R.id.edit_text) // ←ここでedit_textを取得
                    val text = editText.text.toString()
                    Toast.makeText(activity, text, Toast.LENGTH_SHORT).show()
                }
        }
        return builder.create()
    }
}

一応落ちずに動くのですが、Kotlinを使っているのにも関わらずfindViewByIdを駆逐できていないのがなんとも気持ち悪いですよね。
View経由でKotlin Android Extensionsを使えばfindViewByIdする必要がないようです。

val text = view.edit_text.text.toString()

Data Bindingを使った解決策

Data Bindingについてよく知らない人は、
公式のガイド(https://developer.android.com/topic/libraries/data-binding/?hl=ja)

こちらの記事(https://qiita.com/Omoti/items/a83910a990e64f4dbdf1)
を参考にしてください。

まずレイアウトファイルを次のように書き換えます。

fragment_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content">
        <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Dialog"
                android:textSize="20sp" />
        <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/edit_text"
                android:hint="入力" />
    </LinearLayout>
</layout>

レイアウト全体をlayoutタグで囲うことにより、このレイアウトファイルをData Bindingで利用できるようになりました。

MyDialogFragment.kt
class MyDialogFragment :DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity)
        val binding = DataBindingUtil.inflate<FragmentDialogBinding>(LayoutInflater.from(activity), R.layout.fragment_dialog, null, false)
        val view = binding.root
        builder.setView(view)
            .setPositiveButton("OK") { _, _ ->
                Toast.makeText(activity, binding.editText.text, Toast.LENGTH_SHORT).show()
            }
        return builder.create()
    }
}

FragmentDialogBinding はプロジェクトをビルドしたときにData Bindingによって自動生成されるクラスです。クラス名はレイアウトファイルのファイル名をアッパーキャメルケースにしたもの+Bindingになります。
Data Binding経由でViewを取得するときには、入力されたidをローワーキャメルケースで指定して取得します。この場合、edit_texteditTextになっていますね。

画面回転などによるActivityの更新などを考慮しない場合、次のように書くこともできます。

fragment_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable name="text" type="String" />
    </data>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content">
        <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Dialog"
                android:textSize="20sp" />
        <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@={text}"
                android:hint="入力" />
    </LinearLayout>
</layout>
MyDialogFragment.kt
class MyDialogFragment :DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity)
        val binding = DataBindingUtil.inflate<FragmentDialogBinding>(LayoutInflater.from(activity), R.layout.fragment_dialog, null, false)
        val view = binding.root
        builder.setView(view)
            .setPositiveButton("OK") { _, _ ->
                Toast.makeText(activity, binding.text, Toast.LENGTH_SHORT).show()
            }
        return builder.create()
    }
}

EditTextにidを設定していないことに気がついたかと思います。dataタグでレイアウトファイル上にtext変数を定義し、入力した値をandroid:text="@={text}"とすることによって直接textに入れています。Fragment側ではbinding.textとすることでtext変数を取得することができます。この方法だと画面回転などでActivityが更新されたときに入力中の値は消えてしまいますが、それを考慮する必要がない場合はこちらのほうがよりシンプルです。

参考

https://developer.android.com/topic/libraries/data-binding/?hl=ja
https://stackoverflow.com/questions/34967868/how-to-use-data-binding-in-dialog

12
11
1

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
12
11