We have looked at dialogs before where a dialog box pops up and floats over the UI (that is, partially obscuring the current UI) in a transparent manner prompting the user for input needed by the program -- e.g., date, time. We use dialogs quite a lot in the MyRuns set of labs. Android supports DialogFragment, which is a fragment that displays a dialog window, floating on top of its activity's window. In what follows we show how DialogFragment can be used to construct and customize dialogs. You can use this knowledge for your MyRuns lab2 and future labs.
DialogFragment tutorial from the Android developers site.
In this lecture, we will first show how the DialogFragment can be extended and customized. Android supports three ways to use dialogs:
Use the Dialog class and its numerous extensions; such as AlertDialog, DatePickerDialog. There are a large number of canned dialog boxes that you can use depending on the sort of user interaction you need. For your own custom dialogs, you could extend the Dialog base class, as do DatePickerDialog and TimePickerDialog.
You can apply the dialog theme directly by the activity giving the feel of a dialog box.
Toast.
In this class, we will be focusing on DialogFragment. Your
implementation should override onCreateDialog(Bundle)
to create a custom Dialog object, with its
own content. As shown in the code below, once your Dialog object
(called "ret" in this example) is set up to satisfy your needs, it will
be returned from the onCreateDialog() callback for the dialog box to be
shown to the user.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
lateinit var ret: Dialog
...
...
return ret
}
To create the Dialog object, we use AlertDialog.Builder, a
builder class used to construct a custom dialog. In the code below, requireActivity()
returns the hosting
activity of the DialogFragment. If the activity is null, the system
throws an exception.
val builder = AlertDialog.Builder(requireActivity())
Once done you can then set up the various parts of the dialog as
discussed above -- title, assign buttons programmatically, text to be
displayed, and importantly, event listeners to handle user
interaction such as text input, touching buttons, making a selection.
In the simple code example below, we inflate the layout of the dialog
in a way similar to inflating a fragment. The UI of the dialog window
is setup in the fragment_my_dialog.xml
.
We created an "ok" button using setPositiveButton
and a "cancel" button using setNegativeButton
.
The parameter "this" in these methods is a
DialogInterface.OnClickListener object, which handles the button click
events. The reason we can use "this" here is because we implement the
listener as a part of our custom dialog fragment (e.g., class MyDialog : DialogFragment(),
DialogInterface.OnClickListener{...}
). Lastly, we call builder.create()
to create the custom
Dialog.
val view: View = requireActivity().layoutInflater.inflate(R.layout.fragment_my_dialog, null)
builder.setView(view)
builder.setTitle("my title")
builder.setPositiveButton("ok", this)
builder.setNegativeButton("cancel", this)
ret = builder.create()
The complete code for the onCreateDialog callback is shown below.
Note that if you have multiple different dialog designs, you can use an
if statement to navigate among different designs. For the sake of
simplicity, we only create one. You will be creating more in MyRuns.
Here the idea is to use an integer to indicate the need for rendering
different dialog designs. When we start the DialogFragment in an
Activity, we pass that integer as argument using a Bundle. In the code
below, we retrieve the Bundle in the argument using val bundle = arguments
.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
lateinit var ret: Dialog
val bundle = arguments
val dialogId = bundle!!.getInt(DIALOG_KEY)
if (dialogId == TEST_DIALOG) {
val builder = AlertDialog.Builder(requireActivity())
val view: View = requireActivity().layoutInflater.inflate(R.layout.fragment_my_dialog, null)
builder.setView(view)
builder.setTitle("my title")
builder.setPositiveButton("ok", this)
builder.setNegativeButton("cancel", this)
ret = builder.create()
}
return ret
}
The code snippet below, we create a custom DialogFragment object,
setup arguments, and show the dialog box by calling show()
on the DialogFragment
object.
val myDialog = MyDialog()
val bundle = Bundle()
bundle.putInt(MyDialog.DIALOG_KEY, MyDialog.TEST_DIALOG)
myDialog.arguments = bundle
myDialog.show(supportFragmentManager, "my dialog")