DialogFragmentのカスタムレイアウトでEditTextの値を取得しようとして苦戦したのが、Data Bindingを使うといい感じに解決できそうだったのでメモ兼ねて記事を残しておきます。
もっと根本的に良い解決方法があれば遠慮なくコメントください。
#今回使用するカスタムレイアウト
<?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>
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
を使います。
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)
を参考にしてください。
まずレイアウトファイルを次のように書き換えます。
<?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で利用できるようになりました。
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_text
がeditText
になっていますね。
画面回転などによるActivityの更新などを考慮しない場合、次のように書くこともできます。
<?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>
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