Framments e Kotlin DSL

Il componente Navigazione fornisce una lingua specifica del dominio basata su Kotlin, oppure DSL, che si basa sulla tecnologia type-safe di Kotlin costruttori di Google. Questa API ti consente di comporre in modo dichiarativo il grafico nel codice Kotlin, anziché che all'interno di una risorsa XML. Questo può essere utile se vuoi creare la navigazione dinamica. Ad esempio, l'app potrebbe scaricare e memorizzare nella cache configurazione di navigazione da un servizio web esterno e poi usarla per creare dinamicamente un grafico di navigazione nel pannello Funzione onCreate().

Dipendenze

Per utilizzare Kotlin DSL con Fragments, aggiungi la seguente dipendenza al file File build.gradle:

Groovy

dependencies {
    def nav_version = "2.8.4"

    api "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

Kotlin

dependencies {
    val nav_version = "2.8.4"

    api("androidx.navigation:navigation-fragment-ktx:$nav_version")
}

Creazione di un grafico

Ecco un esempio di base basato sulla Girasole Google Cloud. Per questo ad esempio, abbiamo due destinazioni: home e plant_detail. home è presente quando l'utente avvia l'app per la prima volta. Questa destinazione mostra un elenco di piante del giardino dell'utente. Quando l'utente seleziona uno di piante, l'app raggiunge la destinazione plant_detail.

La figura 1 mostra queste destinazioni insieme agli argomenti richiesti dalla plant_detail destinazione e un'azione, to_plant_detail, utilizzata dall'app per navigare da home a plant_detail.

L'app Girasole ha due destinazioni e un'azione che
            li collegano.
Figura 1. L'app Girasole ha due destinazioni: home e plant_detail, insieme a un'azione che li connette.

Hosting di un grafico di navigazione DSL Kotlin

Prima di poter creare il grafico di navigazione della tua app, devi disporre di un luogo in cui grafico. Questo esempio utilizza frammenti, quindi ospita il grafo in una NavHostFragment all'interno di un FragmentContainerView:

<!-- activity_garden.xml -->
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true" />

</FrameLayout>

Tieni presente che in questo esempio l'attributo app:navGraph non è impostato. Il grafico non è definita come risorsa res/navigation, quindi deve essere impostata come parte del onCreate() processo nell'attività.

In XML, un'azione collega un ID destinazione a uno o più argomenti. Tuttavia, quando utilizzi la navigazione DSL, una route può contenere argomenti come parte di lungo il percorso. Ciò significa che non esiste un concetto di azioni quando si utilizza la DSL.

Il passaggio successivo è definire le route da utilizzare per definire grafico.

Crea percorsi per il tuo grafico

I grafici di navigazione basati su XML vengono analizzati come parte del processo di compilazione di Android. Viene creata una costante numerica per ogni id definito nel grafico. Questi ID statici generati in fase di build non sono disponibili durante la creazione del grafico di navigazione in fase di runtime, in modo che il sistema utilizza serializzabili tipi anziché ID. Ogni percorso è rappresentato da un tipo unico.

Quando si gestiscono argomenti, questi vengono incorporati nel percorso del testo. In questo modo puoi impostare la sicurezza del tipo per gli argomenti di navigazione.

@Serializable data object Home
@Serializable data class Plant(val id: String)

Una volta definiti i percorsi, puoi creare il grafico di navigazione.

val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
    startDestination = Home
) {
    fragment<HomeFragment, Home> {
        label = resources.getString(R.string.home_title)
    }
    fragment<PlantDetailFragment, PlantDetail> {
        label = resources.getString(R.string.plant_detail_title)
    }
}

In questo esempio, vengono definite due destinazioni di frammenti utilizzando il metodo fragment() Funzione di builder DSL. Questa funzione richiede due argomenti di Google.

Innanzitutto, un corso Fragment. che fornisce la UI per questa destinazione. L'impostazione ha lo stesso effetto Impostazione dell'attributo android:name sulle destinazioni dei frammenti definite utilizzando XML.

Il secondo è il percorso. Deve essere un tipo serializzabile che si estende da Any. it deve contenere gli argomenti di navigazione che verranno utilizzati da questa destinazione, e i relativi tipi.

La funzione accetta anche un lambda facoltativo per la configurazione aggiuntiva, come come etichetta di destinazione, oltre a funzioni di builder incorporate per argomenti e link diretti.

Infine, puoi navigare da home a plant_detail utilizzando NavController.navigate(): chiamate:

private fun navigateToPlant(plantId: String) {
   findNavController().navigate(route = PlantDetail(id = plantId))
}

In PlantDetailFragment, puoi ottenere gli argomenti di navigazione ottenendo attuale NavBackStackEntry e le chiamate toRoute per ottenere l'istanza della route.

val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id

Se PlantDetailFragment utilizza un ViewModel, ottieni l'istanza di route utilizzando SavedStateHandle.toRoute

val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id

Il resto della guida descrive gli elementi comuni dei grafici di navigazione, le destinazioni e come utilizzarle durante la creazione del grafico.

Destinazioni

La DSL Kotlin fornisce supporto integrato per tre tipi di destinazione: Destinazioni Fragment, Activity e NavGraph, ciascuna delle quali ha le proprie funzione di estensione in linea disponibile per creare e configurare destinazione.

Destinazioni dei frammenti

La fragment() La funzione DSL può essere parametrizzata con la classe del frammento per l'interfaccia utente e la tipo di route utilizzato per identificare in modo univoco questa destinazione, seguito da una lambda in cui puoi fornire un'ulteriore configurazione come descritto nella Navigazione con il grafico Kotlin DSL.

fragment<MyFragment, MyRoute> {
   label = getString(R.string.fragment_title)
   // custom argument types, deepLinks
}

Destinazione dell'attività

La activity() La funzione DSL accetta un parametro di tipo per la route ma non è parametrizzata per qualsiasi classe di attività di implementazione. Puoi invece impostare un valore facoltativo per il campo activityClass un lambda finale. Questa flessibilità ti consente di definire una destinazione per le attività un'attività che dovrebbe essere avviata utilizzando una intent, in cui un'esplicita una lezione di attività non avrebbe senso. Come per le destinazioni dei frammenti, puoi anche configurare un'etichetta, argomenti personalizzati e link diretti.

activity<MyRoute> {
   label = getString(R.string.activity_title)
   // custom argument types, deepLinks...

   activityClass = MyActivity::class
}

La navigation() La funzione DSL può essere utilizzata per creare una navigazione nidificata grafico. Questa funzione prende un tipo per la route da assegnare a questo grafico. Richiede anche due argomenti: il percorso della destinazione di partenza del grafico e una lambda per e configurare il grafico. Gli elementi validi includono altre destinazioni, un argomento personalizzato di testo, link diretti e un'etichetta descrittiva per il destinazione. Questa etichetta può essere utile per associare il grafico di navigazione ai componenti dell'interfaccia utente utilizzando NavigationUI

@Serializable data object HomeGraph
@Serializable data object Home

navigation<HomeGraph>(startDestination = Home) {
   // label, other destinations, deep links
}

Supporto di destinazioni personalizzate

Se utilizzi un nuovo tipo di destinazione che non supporta direttamente il Kotlin DSL, puoi aggiungere queste destinazioni a il tuo DSL Kotlin utilizzando addDestination():

// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
    route = Graph.CustomDestination.route
}
addDestination(customDestination)

In alternativa, puoi anche utilizzare l'operatore unario più per aggiungere una nuova destinazione creata direttamente nel grafico:

// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
    route = Graph.CustomDestination.route
}

Fornitura di argomenti di destinazione

Gli argomenti della destinazione possono essere definiti come parte della classe della route. Questi possono essere esattamente come lo faresti per qualsiasi classe Kotlin. Gli argomenti obbligatori sono i tipi non nulli e gli argomenti facoltativi sono definiti con e i relativi valori.

Il meccanismo sottostante per rappresentare le route e i relativi argomenti è di tipo stringa basato su cloud. L'uso di stringhe per creare un modello di route consente di archiviare lo stato di navigazione ripristinato dal disco durante la configurazione modifiche e processo avviato dal sistema e morte. Per questo motivo, ogni argomento di navigazione deve essere serializzabile, cioè deve avere che converte la rappresentazione in memoria del valore dell'argomento in un String.

La serie di serializzazione di Kotlin plug-in genera automaticamente metodi di serializzazione per quando L'annotazione @Serializable è stata aggiunta a un oggetto.

@Serializable
data class MyRoute(
  val id: String,
  val myList: List<Int>,
  val optionalArg: String? = null
)

fragment<MyFragment, MyRoute>

Fornitura di tipi personalizzati

Per i tipi di argomenti personalizzati, devi fornire una classe NavType personalizzata. Questo ti consente di controllare esattamente il modo in cui il tipo viene analizzato da una route o un link diretto.

Ad esempio, una route utilizzata per definire una schermata di ricerca potrebbe contenere una classe rappresenta i parametri di ricerca:

@Serializable
data class SearchRoute(val parameters: SearchParameters)

@Serializable
data class SearchParameters(
  val searchQuery: String,
  val filters: List<String>
)

Un NavType personalizzato potrebbe essere scritto come:

val SearchParametersType = object : NavType<SearchParameters>(
  isNullableAllowed = false
) {
  override fun put(bundle: Bundle, key: String, value: SearchParameters) {
    bundle.putParcelable(key, value)
  }
  override fun get(bundle: Bundle, key: String): SearchParameters {
    return bundle.getParcelable(key) as SearchParameters
  }

  override fun serializeAsValue(value: SearchParameters): String {
    // Serialized values must always be Uri encoded
    return Uri.encode(Json.encodeToString(value))
  }

  override fun parseValue(value: String): SearchParameters {
    // Navigation takes care of decoding the string
    // before passing it to parseValue()
    return Json.decodeFromString<SearchParameters>(value)
  }
}

Questa opzione può quindi essere utilizzata nella tua DSL Kotlin come qualsiasi altro tipo:

fragment<SearchFragment, SearchRoute> {
    label = getString(R.string.plant_search_title)
    typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
}

Quando raggiungi la destinazione, crea un'istanza del percorso:

val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))

Il parametro può essere ottenuto dalla route nella destinazione:

val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters

Link diretti

I link diretti possono essere aggiunti a qualsiasi destinazione, proprio come con un file XML grafico di navigazione. Tutte le procedure descritte nella sezione Creazione di un link diretto per una destinazione si applicano al processo di creazione di un link diretto utilizzando il Kotlin DSL.

Quando crei un link diretto implicito Tuttavia, non hai una risorsa di navigazione XML che possa essere analizzata <deepLink> elementi. Pertanto, non puoi fare affidamento sul posizionamento di un <nav-graph> nel tuo file AndroidManifest.xml e al suo posto deve aggiungere l'intent filtri manualmente alle tue attività. L'intento che fornisci deve corrispondere al percorso di base, all'azione e al tipo MIME link diretti dell'app.

I link diretti vengono aggiunti a una destinazione richiamando la funzione deepLink all'interno il lambda della destinazione. Accetta la route come tipo con parametri e un parametro basePath per il percorso di base dell'URL utilizzato per il link diretto.

Puoi anche aggiungere un'azione e un tipo MIME utilizzando deepLinkBuilder lambda finale.

L'esempio seguente crea un URI del link diretto per la destinazione Home.

@Serializable data object Home

fragment<HomeFragment, Home>{
  deepLink<Home>(basePath = "www.example.com/home"){
    // Optionally, specify the action and/or mime type that this destination
    // supports
    action = "android.intent.action.MY_ACTION"
    mimeType = "image/*"
  }
}

Formato URI

Il formato dell'URI del link diretto viene generato automaticamente dai campi del percorso utilizzando le seguenti regole:

  • I parametri obbligatori vengono aggiunti come parametri di percorso (esempio: /{id})
  • I parametri con un valore predefinito (parametri facoltativi) vengono aggiunti come query parametri (esempio: ?name={name})
  • Le raccolte vengono aggiunte come parametri di query (ad esempio: ?items={value1}&items={value2}).
  • L'ordine dei parametri corrisponde all'ordine dei campi nella route

Ad esempio, il seguente tipo di route:

@Serializable data class PlantDetail(
  val id: String,
  val name: String,
  val colors: List<String>,
  val latinName: String? = null,
)

ha un formato URI generato:

basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}

Non esiste un limite al numero di link diretti che puoi aggiungere. Ogni volta che chiami deepLink() viene aggiunto un nuovo link diretto a un elenco gestito per quella destinazione.

Limitazioni

Il plug-in Safe Args incompatibile con il Kotlin DSL, in quanto il plug-in cerca i file di risorse XML generano le classi Directions e Arguments.