android DialogFragment を ViewModel と LiveDataで実装
レガシーcallbackスタイルからの脱却
Androidでダイアログのcallbackを受け取るのってめんどくさいですよね。
お作法的にはcallback interfaceを定義してそいつを通じてイベントを受け取る感じで。
class LegacyDialogFragment : DialogFragment() {
interface DialogListener {
fun onOkClick(dialog: LegacyDialogFragment)
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as DialogListener
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireActivity())
.setTitle(title)
.setMessage(message)
.setPositiveButton("OK") { _, _ ->
listener.onOkClick(this)
}
.create()
}
}
・Activityはinterfaceを実装
class LegacyDialogActivity : AppCompatActivity(), LegacyDialogFragment.DialogListener {
companion object {
val TAG = LegacyDialogActivity::class.java.simpleName ?: ""
}
private lateinit var binding: ActivityLegacyDialogBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_legacy_dialog)
binding.button.setOnClickListener {
LegacyDialogFragment.show(
"legacy",
"Are you sure you want to finish this activity?",
supportFragmentManager,
TAG
)
}
}
override fun onOkClick(dialog: LegacyDialogFragment) {
finish()
}
}
これがめんどくさい。
という訳で↓
ViewModelとLiveDataでクリックイベントを通知
今時はViewModeとLiveDataで通知するのがイケてますね。
・build.gradle// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
// 便利なktx
implementation 'androidx.core:core-ktx:1.1.0'
implementation "androidx.fragment:fragment-ktx:1.1.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-beta01"
・DialogFragment
class SimpleDialogFragment : DialogFragment() {
companion object {
private const val BUNDLE_KEY_TITLE = "bundle_key_title"
private const val BUNDLE_KEY_MESSAGE = "bundle_key_message"
private fun newInstance() = SimpleDialogFragment()
private fun newInstance(title: String, message: String): SimpleDialogFragment {
return newInstance().apply {
arguments = bundleOf(
Pair(BUNDLE_KEY_TITLE, title),
Pair(BUNDLE_KEY_MESSAGE, message)
)
}
}
fun show(title: String, message: String, fragmentManager: FragmentManager, tag: String) {
newInstance(title, message).run {
show(fragmentManager, tag)
}
}
}
lateinit var title: String
lateinit var message: String
private val viewModel: DialogViewModel by viewModels({ requireActivity() })
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments!!.run {
title = getString(BUNDLE_KEY_TITLE)!!
message = getString(BUNDLE_KEY_MESSAGE)!!
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireActivity())
.setTitle(title)
.setMessage(message)
.setPositiveButton("OK") { _, _ ->
viewModel.state.value = DialogState.Ok(this@SimpleDialogFragment)
}
.create()
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
viewModel.state.value = DialogState.Cancel(this@SimpleDialogFragment)
}
}
・DialogState
sealed classで通知の状態定義
sealed class DialogState<T : DialogFragment> {
data class Ok<T : DialogFragment>(val dialog: T) : DialogState<T>()
data class Cancel<T : DialogFragment>(val dialog: T) : DialogState<T>()
}
・DialogViewModel
class DialogViewModel: ViewModel() {
val state = MutableLiveData<DialogState<SimpleDialogFragment>>()
}
・DialogShowFragment
class DialogShowFragment : Fragment() {
companion object {
val TAG = DialogShowFragment::class.java.simpleName ?: ""
}
private lateinit var binding: FragmentDialogShowBinding
private val dialogViewModel: DialogViewModel by viewModels({ requireActivity() })
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_dialog_show, container, false)
binding.showDialogButton.setOnClickListener {
SimpleDialogFragment.show("from fragment.", "Are you sure you want to finish this activity?", childFragmentManager, TAG)
}
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
dialogViewModel.state.observe(this) {
when (it) {
is DialogState.Ok -> requireActivity().finish()
is DialogState.Cancel -> {
// do nothing.
}
}
}
}
}
こんな感じでしょうか。
Activityからも同じ感じで使えます。
ソースはこちらにあげてます。
https://github.com/sakony/android_samples
ディスカッション
コメント一覧
まだ、コメントがありません