Creating reusable AlertDialogs with Custom Views in Anko

One of the features Anko is most notable for is simplifying the process of creating layouts programmatically. Anko also offers some variations of some views such as the AlertDialog. The indeterminateProgressDialog() displays an AlertDialog with a progress dialog while a selector() displays an AlertDialog with a list of text items. Even with all these, sometimes we still need to create dialogs with custom views, and we might need to use those dialogs in multiple screens in our app.

There will be a lot of code duplication if we keep replicating the same Anko DSL whenever we need it. In this article, we’ll first explore creating an AlertDialog with a default layout with Anko. Next, we will learn how to create a reusable AlertDialog with a Custom View, still with Anko. We’ll extract all the related code to a separate file and keep our views clean.

AlertDialog with default layout

implementation "org.jetbrains.anko:anko-commons:$anko_version"

Here is a typical dialog with a title resource, a message resource, a positive and a negative button which dismisses the dialog when clicked. We can replace the titleResource key with title and that will take in a string. But, as good Android citizens, and also because of internationalization, we want to have all our strings in the strings.xml file.

alert {
titleResource = R.string.title
messageResource = R.string.message
positiveButton(R.string.ok, { dialog ->
dialog.dismiss()
})
negativeButton(R.string.cancel, { dialog ->
dialog.dismiss()
})
show()
}

Reusable AlertDialog with a Custom View

Sample dialog

In order to create the sample dialog above, we first need to include these additional imports in our app level build.gradle file.

implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
implementation "org.jetbrains.anko:anko-sdk25:$anko_version"

Next, we create a new class that takes in an AnkoContext. We use an AnkoContext because it uses a weak reference under the hood. Then, we flesh out the views and call the dialog’s show() method.

class FeedbackDialog(ui: AnkoContext<View>) {

lateinit var dialog: DialogInterface
lateinit var feedbackText: TextInputEditText
lateinit var cancelButton: TextView
lateinit var okButton: TextView

init {
with(ui) {
dialog = alert {
customView {
verticalLayout {
padding = dip(16)

textView(R.string.some_title) { ... }

textView(R.string.some_message) { ... }

textInputLayout {
hint = context.getString(R.string.hint)
feedbackText = textInputEditText {
textSize = 16f
}
}

// More DSL goes here
}
}
}
.show()
}
}
}

We have now created our dialog with a custom view. We have to be able to call this from any View/Activity/Fragment. We’ll need to reference whatever view we are in from AnkoContext’s create() method. In the code below, we pass in the ContentView from an Activity. In a Fragment, we can use the View returned from the onCreateView() method.

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}

private fun displayFeedbackDialog(view: View) {
val feedbackDialogUi by lazy {
contentView
?.let {
FeedbackDialog
(AnkoContext.create(ctx, it))
}
}

feedbackDialogUi?.okButton?.setOnClickListener {
val text = feedbackDialogUi.feedbackText.text.toString()
toast(getString(R.string.confirmation, text))

feedbackDialogUi.dialog.dismiss()
}

feedbackDialogUi?.cancelButton?.setOnClickListener {
feedbackDialogUi.dialog.dismiss()
}
}
}

Conclusion

All the code in this article can be found on Github. Please leave a comment if you have any feedback.

Android Dev, Marathoner