In this ocsaly blog, we will learn how to present the user with a pop-up dialog window. We can then put all that we know into the first phase of our first multi-ocsaly blog app, Note to self. We will then learn about more Android and Kotlin features in this ocsaly blog and the four following ocsaly blogs (up to Ocsaly blog 18, Localization), and then use our newly acquired knowledge to enhance the Note to self app. In each ocsaly blog, we will also build a selection of smaller apps that are separate from this main app. So, what does Ocsaly blog 14, Android Dialog Windows, hold in store for you? The following topics will be covered in this ocsaly blog:
• Implement a simple app with a pop-up dialog box
• Learn how to use DialogFr gment to begin the Note to self app
• Start the Note to self app and learn how to add string resources in our
projects instead of hardcoding text in our layouts
• Implement more complex dialog boxes to capture input from the user
So, let’s get started. Dialog windows Often in our apps, we will want to show the user some information, or perhaps ask for confirmation of an action in a pop-up window. This is known as a dialog window. If you quickly scan the palette in Android Studio, you might be surprised to see no mention of dialog windows whatsoever. Dialog windows in Android are more advanced than a simple widget or even a whole layout. They are classes that can also have layouts and other UI elements of their own.
Creating the dialog demo project
We previously mentioned that the best way to create a dialog window in Android is with the DialogFragment class. However, there is another way to create dialogs in Android that is arguably a little bit simpler. The problem with this simpler Dialog class is that it is not very well supported in the Activity lifecycle. It is even possible that using Dialog could accidentally crash the app. If you were writing an app with one fixed orientation layout that only needed one
simple pop-up dialog, it could be argued that the simpler Dialog class should be used. But, as we are aiming to build modern, professional apps with advanced features, we will benefit from ignoring this class.Create a new project in Androi Studio using the Empty Activity project template and call it Dialog Demo. The completed code for this project is in the Ocsaly blog14/ Dialog Demo folder of the download bundle. Coding a DialogFragment class Create a new class in Android Studio by right-clicking on the folder with the name of your package (the one that has the MainActivity.kt file). Select New | Kotlin File/ class, name it MyDialog, and choose Class in the drop-down selector. Left-click on OK to create the class. The first thing you need to do is to change the class declaration to inherit from DialogFragment. Also, let’s add all the imports we will need in this class. When you have done so, your new class will look like this:
import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
class MyDialog : DialogFragment() {
}
Now, let’s add code to this class a bit at a time and explain what is happening at
each step.
As with so many classes in the Android API, DialogFragment provides us with
functions that we can override to interact with the different events that will occur
with the class.
Add the following highlighted code that overrides the onCreateDialog function.
Study it carefully, and then we will examine what is happening:
class MyDialog : DialogFragment() {
override
fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Use the Builder class because this dialog
// has a simple UI.
// We will use the more flexible onCreateView function
// instead of onCreateDialog in the next project
val builder = AlertDialog.Builder(this.activity!!)
// More code here soon
}
}
In the code that we just added, we first add the overridden onCreateDialog
function, which will be called by Android when we later show the dialog with code
from the MainActivity class.
Then, inside the onCreateDialog function, we get our hands on an instance of a new
class. We declare and initialize an object of the AlertDialog.Builder type that needs
a reference to the MainActivity class to be passed into its constructor. This is why we
use activity!! as the argument; and we are asserting that the instance is not null (!!).
The activity property is part of the Fragment class (and, therefore,
DialogFragment too) and it is a reference to the Activity class instance that will
create the DialogFragment instance. In this case, this is our MainActivity class.
Let’s take a look at what we can do with builder now that we have declared and
initialized it.
Using chaining to configure the DialogFragment class
Now we can use our builder object to do the rest of the work. There is something
slightly odd in the next three blocks of code. If you look ahead and quickly scan
them, you will notice that there are three uses of the dot operator, but only one usage
is actually placed next to the builder object. This shows that these three apparent
blocks of code are, in fact, just one line to the compiler.
We have seen what is going on here before, but in a less pronounced situation.
When we create a Toast message and add a .show() call on to the end of it, we
are chaining. That is, we are calling more than one function, in sequence, on the
same object. This is equivalent to writing multiple lines of code; it is just clearer and
shorter this way.
Add this code, which utilizes chaining, right after the previous code that we added
in onCreateDialog, examine it, and then we will discuss it:
// Dialog will have "Make a selection" as the title
builder.setMessage("Make a selection")
// An OK button that does nothing
.setPositiveButton("OK", { dialog, id ->
// Nothing happening here
})
// A "Cancel" button that does nothing
.setNegativeButton("Cancel", { dialog, id ->
// Nothing happening here either
})
Each of the three parts of code that we added can be explained as follows:
1. In the first of the three blocks that uses chaining, we call builder.
setMessage, which sets the main message that the user will see in the dialog
box. Also, note that it is fine to have comments in between parts of the
chained function calls, as these are ignored entirely by the compiler.
2. Then, we add a button to our dialog with the setPositiveButton function
and the first argument sets the text on it to OK. The second argument is a
lambda that implements DialogInterface.OnClickListener that handles
clicks on the button. Notice that we are not going to add any code to the
onClick function, but we could, just as we did in the previous chapter. We
just want to see this simple dialog and we will take things a step further in
the next project.
3. Next, we call yet another function on the same builder object. This time,
it’s the setNegativeButton function. Again, the two arguments set Cancel
as the text for the button and a lambda is used to set up listening for clicks.
Again, for the purposes of this demo, we are not taking any action in the
overridden onClick function.
Next, we will code the return statement to complete the function and remove the
error. Add the return statement to the end (but keep it inside the final curly brace)
of the onCreateDialog function:
// Create the object and return it
return builder.create()
}// End of onCreateDialog
This last line of code has the effect of returning to MainActivity (which will call
onCreateDialog in the first place) our new, fully configured, dialog window. We
will see and add this calling code quite soon.
Now that we have our MyDialog class that inherits from FragmentDialog, all we
have to do is to declare an instance of MyDialog, instantiate it, and call its overridden
onCreateDialog function.
Using the DialogFragment class
Before we turn to the code, let’s add a button to our layout, by observing the
following steps:
1. Switch to the activity_main.xml tab, and then switch to the Design tab.
2. Drag a Button widget onto the layout and make sure its id attribute is set to
button.
3. Click the Infer Constraints button to constrain the button exactly where
you place it, but the position isn’t important; how we will use it to create an
instance of our MyDialog class is the key lesson.
1. Switch to the activity_main.xml tab, and then switch to the Design tab.
2. Drag a Button widget onto the layout and make sure its id attribute is set to
button.
3. Click the Infer Constraints button to constrain the button exactly where
you place it, but the position isn’t important; how we will use it to create an
instance of our MyDialog class is the key lesson.
Now switch to the MainActivity.kt tab and we will handle a click on this new
button by using a lambda as we did in Ocsaly blog 13, Bringing Android Widgets to Life
during the Widget exploration app. We do it this way as we only have one button in
the layout, and it seems sensible and more compact than doing the alternative (that
is, implementing the OnClickListener interface and then overriding onClick for the
entire MainActivity class as we did in Ocsaly blog 12, Connecting Our Kotlin to the UI and
Nullability).
Add the following code to the onCreate function of MainActivity after the call to
setContentView:
val button = findViewById<Button>(R.id.button)
// We could have removed the previous line of code by
// adding the ...synthetic.main.activity_main.* import
// as an alternative
button.setOnClickListener {
val myDialog = MyDialog()
myDialog.show(supportFragmentManager, "123")
// This calls onCreateDialog
// Don't worry about the strange looking 123
// We will find out about this in chapter 18
}
Notice that the only thing that happens in the code is that the setOnClickListener
lambda overrides onClick. This means that when the button is pressed, a new
instance of MyDialog is created and calls its show function, which will show our
dialog window just as we configured it in the MyDialog class.
The show function needs a reference to FragmentManager, which we get from the
supportFragmentManager property. This is the class that tracks and controls all
fragment instances for an Activity instance. We also pass in an ID (“123”).
More details on FragmentManager will be revealed when we look more deeply at
fragments, starting in Ocsaly blog 24, Design Patterns, Multiple Layouts, and Fragments.
Now we can run the app and admire our new dialog window that appears when we
click the button in the layout. Notice that clicking either of the buttons in the dialog
window will close it; this is the default behavior. The following screenshot shows our
dialog window in action on the tablet emulator:
Comments are closed.