Custom Dialogs with XML Layouts in Kotlin: Android Development Guide

In Android development, dialogs are a crucial UI element for displaying important information to the user, prompting them to make a decision, or requesting input. While Jetpack Compose is the modern way to build UIs, many legacy and existing projects still utilize XML layouts. Creating custom dialogs using XML layouts in Kotlin allows developers to maintain a consistent design, reuse layouts, and leverage existing resources.

What is a Custom Dialog?

A custom dialog is a dialog window with a UI designed and implemented by the developer, as opposed to using default or pre-built dialog options. This enables full control over the dialog’s appearance, functionality, and behavior, offering a tailored user experience.

Why Use Custom Dialogs with XML Layouts?

  • Design Flexibility: Complete control over the dialog’s look and feel.
  • Layout Reusability: Ability to reuse existing XML layouts.
  • Compatibility: Ensures compatibility with older Android projects and codebase practices.

How to Create Custom Dialogs with XML Layouts in Kotlin

Here’s a step-by-step guide to creating custom dialogs with XML layouts in Kotlin:

Step 1: Create the XML Layout File

First, design the layout for your custom dialog in an XML file. For example, custom_dialog_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/dialog_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Custom Dialog Title"
        android:textSize="18sp"
        android:textStyle="bold"
        android:gravity="center"
        android:paddingBottom="8dp"/>

    <TextView
        android:id="@+id/dialog_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is a custom dialog with a message."
        android:textSize="16sp"
        android:gravity="center"
        android:paddingBottom="16dp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">

        <Button
            android:id="@+id/dialog_positive_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OK"
            android:layout_marginEnd="8dp"/>

        <Button
            android:id="@+id/dialog_negative_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Cancel"/>

    </LinearLayout>

</LinearLayout>

In this XML layout, we define:

  • A LinearLayout as the root layout.
  • A TextView for the dialog title.
  • A TextView for the dialog message.
  • Two Button elements for positive and negative actions.

Step 2: Create a Custom Dialog Class in Kotlin

Create a Kotlin class that extends Dialog to handle the custom dialog logic.


import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.view.Window
import android.view.WindowManager

class CustomDialog(context: Context,
                   private val title: String,
                   private val message: String) : Dialog(context) {

    private lateinit var dialogTitle: TextView
    private lateinit var dialogMessage: TextView
    private lateinit var positiveButton: Button
    private lateinit var negativeButton: Button

    private var positiveButtonClickListener: (() -> Unit)? = null
    private var negativeButtonClickListener: (() -> Unit)? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        requestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(R.layout.custom_dialog_layout)

        // Set the dialog to take up the full screen width
        window?.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT)

        dialogTitle = findViewById(R.id.dialog_title)
        dialogMessage = findViewById(R.id.dialog_message)
        positiveButton = findViewById(R.id.dialog_positive_button)
        negativeButton = findViewById(R.id.dialog_negative_button)

        dialogTitle.text = title
        dialogMessage.text = message

        positiveButton.setOnClickListener {
            positiveButtonClickListener?.invoke()
            dismiss()
        }

        negativeButton.setOnClickListener {
            negativeButtonClickListener?.invoke()
            dismiss()
        }
    }

    fun setPositiveButtonClickListener(listener: () -> Unit) {
        positiveButtonClickListener = listener
    }

    fun setNegativeButtonClickListener(listener: () -> Unit) {
        negativeButtonClickListener = listener
    }
}

Explanation of the Kotlin code:

  • We create a class CustomDialog extending Dialog.
  • The constructor takes a Context, a title, and a message for the dialog.
  • onCreate is overridden to set the layout, find views, and set the title and message.
  • Click listeners are set up for the positive and negative buttons.
  • Functions setPositiveButtonClickListener and setNegativeButtonClickListener are provided to handle the button click actions externally.

Step 3: Display the Custom Dialog in an Activity or Fragment

To show the custom dialog, instantiate the CustomDialog class in your Activity or Fragment and call the show() method.


import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

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

        val showDialogButton: Button = findViewById(R.id.showDialogButton)

        showDialogButton.setOnClickListener {
            val customDialog = CustomDialog(this, "Alert", "Do you want to continue?")

            customDialog.setPositiveButtonClickListener {
                Toast.makeText(this, "Continuing...", Toast.LENGTH_SHORT).show()
            }

            customDialog.setNegativeButtonClickListener {
                Toast.makeText(this, "Cancelled.", Toast.LENGTH_SHORT).show()
            }

            customDialog.show()
        }
    }
}

Explanation of the Activity code:

  • A button click listener triggers the creation and display of the CustomDialog.
  • The CustomDialog is instantiated with a title and a message.
  • The click listeners for the positive and negative buttons are set up using the provided functions, showing Toast messages based on the action taken.
  • Finally, customDialog.show() displays the dialog.

Additional Tips for Custom Dialogs

  • Theming: Ensure that your dialog matches the app’s overall theme by setting the dialog theme in the constructor.
  • Accessibility: Implement accessibility features for users with disabilities.
  • Input Validation: Validate user inputs if your dialog contains input fields.

Conclusion

Creating custom dialogs using XML layouts in Kotlin provides a flexible and reusable approach to displaying UI elements and interacting with users. By leveraging XML layouts and Kotlin, developers can maintain consistent design patterns, offer tailored user experiences, and ensure compatibility with older projects. This approach gives developers the power to display critical information and gather user input efficiently.