Hinzufügen von AlertDialog mit Jetpack Compose zu Android-Apps

Jetpack Compose ist ein großartiges neues deklaratives UI-Kit für Android, das die UI-Erstellung in Kotlin ermöglicht und umständliche XML-Layouts ersetzt.

Dieser Artikel enthält ein einfaches Beispiel für die Verwendung von Jetpack Compose in einem Projekt und wie Sie einen Warndialog erstellen, der sich als nützlich erweisen kann, wenn Benutzer aufgefordert werden, wichtige Aktionen zu bestätigen oder abzubrechen.

Voraussetzungen für das Tutorial

Sie können diesem Tutorial folgen, wenn Sie bereits eine XML-Layout-basierte Android-App haben und mit der Integration von Compose-UI-Elementen beginnen möchten oder wenn Sie einfach eine neue App starten und die UI von Anfang an in Compose erstellen möchten.

Für eine optimale Entwicklung in Jetpack Compose benötigen Sie Android Studio Arctic Fox, mit dem Sie die integrierte Vorschau der von Ihnen erstellten Benutzeroberfläche verwenden können. Es bietet auch einen Assistenten zum einfachen Erstellen eines neuen Compose-Projekts.

Erstellen einer neuen Jetpack Compose-App

Um eine neue App zu erstellen, öffnen Sie Android Studio, wählen Sie Datei > Neu > Neues Projekt, und wählen Sie im Assistenten Leere Aktivität zum Verfassen. Dann klick Beenden, und ein neues Jetpack Compose-Projekt wird erstellt.

Erstellen Sie eine neue Jetpack Compose-App, die die Auswahl der bestimmten Aktivität zeigt

Wenn Sie Jetpack Compose noch nicht kennen, empfehle ich Ihnen, diesen hervorragenden Einführungsartikel zu lesen. Es bietet einen großartigen Überblick über verfügbare Komponenten und beschreibt die Prinzipien von Jetpack Compose. Ich werde jedoch auch alles erklären, während wir diesen Artikel durchgehen.

Dieser Beitrag setzt auch voraus, dass Sie vertraut sind mit ViewModel (von Android-Architekturkomponenten) und Bereitstellung des UI-Status von a ViewModel über StateFlow von Kotlin-Koroutinen.

Jetpack Compose zu einem bestehenden Projekt hinzufügen

Wenn Sie über ein vorhandenes Android-Projekt verfügen, müssen Sie einige Konfigurationen hinzufügen, um Jetpack Compose verwenden zu können.

Einrichten des Hauptprojekts

In Ihrem Hauptprojekt build.gradle.kts, stellen Sie sicher, dass Sie das Android Gradle Plugin 7.0.0 und die Kotlin-Version 1.5.31 haben:

buildscript {
    // ...
    dependencies {
        classpath("com.android.tools.build:gradle:7.0.0")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
        // ...
    }
}

Beachten Sie, dass Jetpack Compose eng an eine bestimmte Kotlin-Version gekoppelt ist, da Jetpack Compose sein eigenes Kotlin-Compiler-Plugin verwendet (und dessen API derzeit instabil ist). Sie können Kotlin also nicht auf eine neuere Version aktualisieren, es sei denn, Sie aktualisieren auch Jetpack Compose auf eine kompatible Version.

Einrichten der app Modul

Im build.gradle.kts der tatsächlichen app Modul wo Sie schreiben die Benutzeroberfläche, Sie müssen Änderungen im Inneren vornehmen

android {
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.0.5"
    }
}

Anschließend können Sie die erforderlichen Abhängigkeiten hinzufügen. Beachten Sie, dass compose-theme-adapter hat eine Versionierung unabhängig von anderen Compose-Abhängigkeiten (dies ist nur ein Zufall, dass es in diesem Beispiel auch auf Version 1.0.5 liegt):

dependencies {
  val composeVersion = 1.0.5
  implementation("androidx.compose.ui:ui:$composeVersion")
  implementation("androidx.compose.ui:ui-tooling:$composeVersion")
  implementation("androidx.compose.material:material:$composeVersion")
  implementation("com.google.android.material:compose-theme-adapter:1.0.5")
}

Ihre Funktionalität ist wie folgt:

  • compose.ui:ui bietet die Kernfunktionalität
  • compose.ui:ui-tooling aktiviert Vorschau im Android Studio
  • compose.material bietet materielle Komponenten wie AlertDialog oder TextButton
  • compose-theme-adapter bietet einen Wrapper, um ein vorhandenes Materialdesign für Compose-UI-Elemente wiederzuverwenden (definiert in themes.xml)

Erstellen AlertDialog

Jetpack Compose bietet eine domänenspezifische Sprache (DSL) für die Entwicklung von Benutzeroberflächen in Kotlin. Jedes UI-Element wird mit einer Funktion definiert, die mit annotiert ist @Composable, die Argumente annehmen kann oder nicht, aber immer zurückgibt Unit.

Dies bedeutet, dass diese Funktion nur als Nebeneffekt die UI-Komposition ändert und nichts zurückgibt. Konventionell werden diese Funktionen mit einem Großbuchstaben beginnend geschrieben, sodass sie leicht mit Klassen verwechselt werden können.

Schauen wir uns also die Dokumentation für ein Material an AlertDialog Composable (Ich habe die Parameter weggelassen, die gerade nicht benötigt werden):

@Composable
public fun AlertDialog(
    onDismissRequest: () → Unit,
    confirmButton: @Composable () → Unit,
    dismissButton: @Composable (() → Unit)?,
    title: @Composable (() → Unit)?,
    text: @Composable (() → Unit)?,
    // ...
): Unit

Was wir auf den ersten Blick sehen, ist, dass seine Parameter andere sind @Composable Funktionen. Dies ist ein gängiges Muster beim Erstellen einer Benutzeroberfläche in Compose: Übergeben einfacherer Composables als Argumente zum Erstellen komplexerer Benutzeroberflächenelemente.

Der AlertDialog Parameter die uns hier interessieren sind onDismissRequest, confirmButton, dismissButton, title, und text.

Mit onDismissRequest, können wir eine Rückruffunktion übergeben, die ausgeführt werden soll, wenn ein Benutzer außerhalb des Dialogs tippt oder auf die Zurück-Schaltfläche des Geräts tippt (aber nicht, wenn er auf den Dialog klickt Abbrechen Taste).

Andere Parameter sind:

  • confirmButton, das ist ein Composable, das die OK Schaltflächen-Benutzeroberfläche und -Funktionalität
  • dismissButton, das gleiche gilt für die Abbrechen Knopf wie confirmButton
  • title, ein Composable, das das Layout für den Dialogtitel bereitstellt

Und schlussendlich, text ist ein Composable, das das Layout für die Dialognachricht bereitstellt. Beachten Sie, dass, obwohl es benannt ist text, es muss nicht nur aus einer statischen Textnachricht bestehen.

Denn text nimmt ein @Composable als Parameter können Sie auch dort ein komplexeres Layout bereitstellen.

Schreiben einer Composable-Funktion für AlertDialog

Erstellen wir in unserem Projekt eine neue Datei für den Warndialog, den wir erstellen möchten. Nennen wir die Datei SimpleAlertDialog.kt und darin schreiben wir eine zusammensetzbare Funktion namens SimpleAlertDialog().

Innerhalb dieser Funktion erstellen wir die AlertDialog; Wir werden auch die Argumente untersuchen, die wir nacheinander übergeben.

Hinzufügen eines leeren onDismissRequest Ruf zurück

Das erste Argument ist ein leeres Lambda als Rückruf für die Ablehnungsanfrage (wir werden es später ausfüllen):

@Composable
fun SimpleAlertDialog() { 
    AlertDialog(
        onDismissRequest = { },
    )
}

Hinzufügen einer Bestätigungsschaltfläche

Geben wir für die Schaltfläche Bestätigen ein TextButton mit dem Text „OK“ und einem leeren Rückruf. Werfen wir einen Blick auf einen Auszug aus dem TextButton Dokumentation (im Codebeispiel unten), um zu sehen, was ein TextButton tatsächlich braucht (ich habe wieder die Parameter weggelassen, die nicht verwendet werden):

@Composable
public fun TextButton(
    onClick: () → Unit,
    // ...
    content: @Composable RowScope.() → Unit
): Unit

Das sieht einfach aus: a TextButton braucht ein onClick Hörer und a content Composable wie seine Benutzeroberfläche.

Sie können jedoch nicht einfach eine rohe Zeichenfolge an die übergeben TextButton um es anzuzeigen; die Zeichenfolge muss sich in a umwickeln Text zusammensetzbar.

Jetzt möchten wir, dass die Schaltfläche das Wort “OK” anzeigt. Also, die content Argument für das UI-Layout der Schaltfläche “Bestätigen” sieht wie folgt aus:

{ Text(text = "OK") }

Seit der content Lambda ist das letzte Argument der TextButton, gemäß der Kotlin-Konvention, können wir es aus den Klammern ziehen.

Nachdem Sie die obigen Schritte abgeschlossen haben, wurde die Schaltfläche Bestätigen zu unserem hinzugefügt AlertDialog sieht aus wie das:

@Composable
fun SimpleAlertDialog() {
    AlertDialog(
        onDismissRequest = { },
        confirmButton = {
            TextButton(onClick = {})
            { Text(text = "OK") }
        },
    )
}

Schaltfläche zum Schließen hinzufügen

Wir können nun in ähnlicher Weise die dismissButton das sagt “Abbrechen”:

@Composable
fun SimpleAlertDialog() {
    AlertDialog(
        onDismissRequest = { },
        confirmButton = {
            TextButton(onClick = {})
            { Text(text = "OK") }
        },
        dismissButton = {
            TextButton(onClick = {})
            { Text(text = "Cancel") }
        }
    )
}

Einen Titel und eine Nachricht hinzufügen

Lassen Sie uns auch a hinzufügen title und text das wird unsere Nachricht so einfach wie möglich machen Text Elemente. Der Titel lautet „Bitte bestätigen“ und die Nachricht lautet „Soll ich mit der angeforderten Aktion fortfahren?“:

@Composable
fun SimpleAlertDialog() {
    AlertDialog(
        onDismissRequest = { },
        confirmButton = {
            TextButton(onClick = {})
            { Text(text = "OK") }
        },
        dismissButton = {
            TextButton(onClick = {})
            { Text(text = "Cancel") }
        },
        title = { Text(text = "Please confirm") },
        text = { Text(text = "Should I continue with the requested action?") }
    )
}

Hinzufügen AlertDialog zum Layout

Unser Dialog bietet noch keine Funktionalität, aber versuchen wir zu sehen, wie er auf dem Bildschirm aussieht. Dazu müssen wir es unserem Layout hinzufügen. Dies geschieht auf zwei verschiedene Arten.

Erstellen eines neuen Jetpack Compose-Projekts über den Assistenten

Wenn Sie mit dem Projektassistenten ein neues Compose-Projekt erstellt haben, MainActivity.onCreate() Methode finden Sie einen Aufruf an setContent{}. Hier landen all Ihre Composables für den Bildschirm.

Um das hinzuzufügen SimpleAlertDialog kombinierbar zu Ihrem MainActivity lege es einfach in den MyApplicationTheme (Der Themename wird anders sein, wenn Sie Ihrer Anwendung einen anderen Namen geben als MyApplication).

Ihr Code sollte wie folgt aussehen:

class MainActivity : ComponentActivity() {

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

        setContent {
            MyApplicationTheme {
                SimpleAlertDialog()
            }
        }
    }
}

Verwenden eines vorhandenen XML-Layout-basierten Projekts

Wenn Sie ein bestehendes Projekt mit einem XML-basierten Layout haben, müssen Sie ein ComposeView zu Ihrem XML-Layout:

<...>
    
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</...>

Jetzt in deinem Activity, Sie können darauf zugreifen compose_view, zum Beispiel durch Ansichtsbindung, und es wird a setContent{} Methode, mit der Sie alle Ihre Composables einstellen können.

Beachten Sie, dass Sie sie einschließen müssen, damit die Composables Ihr vorhandenes Material-App-Design verwenden können MdcTheme (der Themen-Wrapper der Material Design-Komponenten).

Also, in deinem Activity, Sie werden so etwas haben:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Inflate your existing layout as usual, e.g. using view binding
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Access the added composeView through view binding and set the content
        binding.composeView.setContent {
            // Wrap all the composables in your app's XML material theme
            MdcTheme {
                SimpleAlertDialog()
            }
        }
    }
}

Testen der App mit SampleAlertDialog

Lassen Sie uns das Projekt durchführen und sehen, was wir bisher erreicht haben!

Das Dialogfeld sieht wie erwartet aus, mit Titel, Nachricht und zwei Schaltflächen.

App mit SampleAlertDialog, Dialog zum Bestätigen oder Abbrechen anzeigen

Allerdings… die Warnung kann nicht abgewiesen werden! Es spielt keine Rolle, wenn Sie die drücken Abbrechen oder OK -Taste, tippen Sie auf den Bildschirm außerhalb des Dialogs oder drücken Sie die Zurück-Taste des Geräts; es geht nicht weg.

Dies ist eine große Änderung gegenüber dem alten XML-basierten Layoutsystem. Dort haben sich die UI-Komponenten „auf sich selbst gestellt“ und ein AlertDialog wird automatisch ausgeblendet, sobald Sie auf eine der Schaltflächen getippt haben (oder eine andere Aktion ausführen, um sie zu schließen).

Während Jetpack Compose Ihnen große Macht verleiht, geht mit großer Macht auch große Verantwortung einher; Sie haben die vollständige Kontrolle über Ihre Benutzeroberfläche, sind aber auch für deren Verhalten vollständig verantwortlich.

Dialog anzeigen und schließen von a ViewModel

So steuern Sie das Anzeigen und Schließen der AlertDialog, wir werden es an a anhängen ViewModel. Vorausgesetzt, Sie verwenden bereits ViewModels Wenn dies nicht der Fall ist, können Sie in Ihrer App die folgende Logik problemlos an die von Ihnen verwendete Präsentationsschichtarchitektur anpassen.

Erstellen MainViewModel ein-/ausblenden SimpleAlertDialog

Fügen Sie zunächst die folgende Abhängigkeit zu Ihrem . hinzu build.gradle.kts:

implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0")

Wir können jetzt a . erstellen MainViewModel das den UI-Status für die MainActivity. Durch Hinzufügen von a showDialog -Eigenschaft können Sie den sichtbaren/unsichtbaren Zustand des Dialogs als Kotlin-Coroutine ausgeben StateFlow mit a Boolean.

EIN true Wert bedeutet, dass der Dialog angezeigt werden soll; false bedeutet, dass es ausgeblendet werden sollte.

Dies showDialog Zustand kann sich durch die folgenden Callbacks ändern:

  • onOpenDialogClicked(), das den Dialog bei Bedarf anzeigt
  • onDialogConfirm(), die aufgerufen wird, wenn ein Benutzer drückt OK im Dialog
  • onDialogDismiss(), die aufgerufen wird, wenn ein Benutzer drückt Abbrechen im Dialog

Sehen wir uns diese in Aktion an:

class MainViewModel : ViewModel() {
    // Initial value is false so the dialog is hidden
    private val _showDialog = MutableStateFlow(false)    
    val showDialog: StateFlow<Boolean> = _showDialog.asStateFlow()

    fun onOpenDialogClicked() {
      _showDialog.value = true
    }

    fun onDialogConfirm() {
        _showDialog.value = false
        // Continue with executing the confirmed action
    }

    fun onDialogDismiss() {
        _showDialog.value = false
    }

    // The rest of your screen's logic...
}

Hinzufügen von Status und Rückrufen zu SimpleAlertDialog

Jetzt müssen wir unseren Dialog ein wenig modifizieren. Gehen wir zurück zum SimpleAlertDialog.kt Datei.

Da müssen wir einige Änderungen vornehmen. Zuerst fügen wir einen Parameter für die . hinzu show Staat zu den SimpleAlertDialog() zusammensetzbare Funktion.

Dann können wir innerhalb der Funktion das Ganze umschließen AlertDialog in einem großen if (show) Anweisung, damit sie nur angezeigt wird, wenn die ViewModel sagt es zu.

Wir müssen auch die hinzufügen onConfirm und onDismiss Rückrufe als Parameter an SimpleAlertDialog() damit der Dialog zurück kommunizieren kann ViewModel wenn der Benutzer den Dialog geschlossen oder bestätigt hat.

Stellen Sie schließlich die onConfirm Rückruf als Klick-Listener für die OK-Schaltfläche und die onDismiss Callback als Click-Listener für den Cancel-Button und als Callback für den onDismissRequest (ein Tippen außerhalb des Dialogs/ein Drücken der Zurück-Taste des Geräts).

Insgesamt sieht es so aus:

@Composable
fun SimpleAlertDialog(
    show: Boolean,
    onDismiss: () -> Unit,
    onConfirm: () -> Unit
) {
    if (show) {
        AlertDialog(
            onDismissRequest = onDismiss,
            confirmButton = {
                TextButton(onClick = onConfirm)
                { Text(text = "OK") }
            },
            dismissButton = {
                TextButton(onClick = onDismiss)
                { Text(text = "Cancel") }
            },
            title = { Text(text = "Please confirm") },
            text = { Text(text = "Should I continue with the requested action?") }
        )
    }
}

Anhängen SimpleAlertDialog zu MainViewModel

Jetzt können wir die anhängen SimpleAlertDialog zu MainViewModel in unserem MainActivity So können sie in beide Richtungen miteinander kommunizieren.

Dafür brauchen wir drei Dinge. Zuerst die MainActivity braucht einen Hinweis auf die MainViewModel Instanz (unter Verwendung der by viewModels() delegieren).

Zweitens, innerhalb der setContent Umfang, wir müssen eine lokale erstellen showDialogState variabel also die SimpleAlertDialog kann das beobachten showDialog Zustand aus dem viewModel.

Wir können dies mit der Delegat-Syntax tun (mit dem by Stichwort). Der Delegierte verwendet dann collectAsState() die einwickeln showDialog in einen speziellen Compose-Wrapper, State.

State wird in Compose verwendet, um Änderungen an dem darin gesammelten Wert zu beobachten. Immer wenn sich dieser Wert ändert, wird die Ansicht neu zusammengesetzt (d. h. alle UI-Elemente prüfen, ob sich ihr Zustand geändert hat und müssen gegebenenfalls neu gezeichnet werden).

Dies showDialogState Variable kann nun als Argument an die übergeben werden show Parameter der SimpleAlertDialog. Ändert sich sein Wert, wird der Dialog entsprechend eingeblendet bzw. ausgeblendet.

Allerdings ist unser SimpleAlertDialog braucht noch zwei Argumente: die onDismiss und onConfirm Rückrufe. Hier werden wir die Hinweise einfach an die entsprechenden weitergeben viewModel Methoden: viewModel::onDialogDismiss und viewModel::onDialogConfirm.

Nachdem Sie die obigen Schritte abgeschlossen haben, wird unser MainActivity sieht aus wie das:

class MainActivity : ComponentActivity() {

    // Reference to our MainViewModel instance using the delegate
    private val viewModel: MainViewModel by viewModels()

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

        // Opens the dialog immediately when the Activity is created
        // Of course in a real app you might want to change it
        viewModel.onOpenDialogClicked()

        setContent {
            // Delegate to observe the showDialog state in viewModel
            val showDialogState: Boolean by viewModel.showDialog.collectAsState()

            MyApplicationComposeTheme {
                SimpleAlertDialog(
                    show = showDialogState,
                    onDismiss = viewModel::onDialogDismiss,
                    onConfirm = viewModel::onDialogConfirm
                )
            }
        }
    }
}

Beachten Sie, dass wir anrufen viewModel.onOpenDialogClicked() in onCreate() Hier; In einer echten App sollten wir sie als Reaktion auf eine Benutzeraktion aufrufen, z. B. das Drücken einer Schaltfläche auf dem Bildschirm.

Testen des Ein- und Ausblendens der SimpleAlertDialog in der App

Lassen Sie uns unsere App erneut ausführen. Jetzt sehen wir, dass wir den Dialog einfach schließen können, indem wir die Taste drücken OK oder Abbrechen Tasten, tippen Sie auf eine beliebige Stelle auf dem Bildschirm außerhalb des Dialogs oder drücken Sie die Zurück-Taste des Geräts.

Wir haben auch einen Bestätigungsrückruf im ViewModel die die gewünschte Aktion fortsetzen kann.

Zusammenfassung

Jetzt haben Sie erfahren, wie sich die Philosophie von Jetpack Compose von der alten XML-Layout-basierten UI-Entwicklung unterscheidet. Compose gibt Ihnen mehr Kontrolle und Integration der UI-Logik in Ihr ViewModels erfordert aber auch, dass Sie alle UI-Verhalten selbst definieren (auch diejenigen, die Sie für selbstverständlich halten, wie das Schließen eines Dialogs).

Mit der ganzen Logik im Inneren des ViewModel bedeutet, dass Sie einfach Unit-Tests dafür schreiben und bei Bedarf in Zukunft ändern können.

Dadurch, dass Compose-UI-Elemente als Kotlin-Funktionen implementiert sind, haben Sie im Vergleich zu XML-Layouts viel weniger lesbaren UI-Code. Sie haben auch direkten Zugriff auf die IDE-Unterstützung beim Schreiben von Code wie Codevervollständigung, One-Button-Dokumentation, Kompilierzeitprüfungen oder Typsicherheit.

Die Möglichkeit, komplexere UI-Elemente aus einfacheren zu erstellen, indem zusammensetzbare Funktionen als Argumente an andere Funktionen übergeben werden, erhöht die Wiederverwendung von Code und die Modularität Ihrer UI.

Es kann auch leicht angepasst werden, zum Beispiel in der SimpleAlertDialog, können Sie einen Parameter hinzufügen, um ein benutzerdefiniertes Layout zu übergeben, um Text anstelle der Bestätigungsnachricht zu bearbeiten RenameDialog.

The source: https://nguyendiep.com
Category: news

Thanks for Reading

Enjoyed this post? Share it with your networks.

Get more stuff

Subscribe to our mailing list and get interesting stuff and updates to your email inbox.

Thank you for subscribing.

Something went wrong.

Leave a Feedback!