Zur Navigationskomponente migrieren

Die Navigationskomponente ist ein die komplexe Navigation, Übergangsanimationen, Deeplinks und das Argument mit der Kompilierungszeit, das zwischen den Bildschirmen in Ihrer App übergeben wird.

Dieses Dokument dient als allgemeiner Leitfaden für die Migration einer vorhandenen Anwendung zu Navigationskomponente nutzen.

Auf übergeordneter Ebene umfasst die Migration folgende Schritte:

  1. Bildschirmspezifische UI-Logik aus Aktivitäten verschieben: Verschieben Sie die Benutzeroberfläche Ihrer App. Logik aus Aktivitäten heraus, sodass jede Aktivität nur die Logik globale Navigations-UI-Komponenten wie z. B. Toolbar erstellen, während Sie die Implementierung jedes Bildschirms in ein Fragment oder ein benutzerdefiniertes Ziel.

  2. Integration der Navigationskomponente: Erstellen Sie für jede Aktivität eine Navigationsdiagramm, das ein oder mehrere von diesem verwalteten Fragment enthält Aktivitäten. Ersetzen Sie Fragmenttransaktionen durch Vorgänge der Navigationskomponente.

  3. Aktivitätsziele hinzufügen: startActivity() Anrufe ersetzen durch Aktionen mithilfe von Aktivitätszielen.

  4. Aktivitäten kombinieren – Kombinieren Sie Navigationsdiagramme in Fällen, in denen mehrere Aktivitäten ein gemeinsames Layout haben.

Voraussetzungen

In diesem Leitfaden wird davon ausgegangen, dass Sie Ihre Anwendung bereits für die Verwendung von AndroidX-Bibliotheken. Falls noch nicht geschehen, Ihr Projekt migrieren, um AndroidX vorher zu verwenden wird fortgesetzt.

Bildschirmspezifische Benutzeroberflächenlogik aus Aktivitäten verschieben

Aktivitäten sind Komponenten auf Systemebene, die eine grafische Interaktion ermöglichen. zwischen deiner App und Android. Aktivitäten werden im Manifest deiner App registriert damit Android weiß, welche Aktivitäten für den Start verfügbar sind. Die Aktivität kann Ihre App auch auf Android-Änderungen reagieren, z. B. auf die Benutzeroberfläche der App in den Vordergrund eintritt oder diesen verlässt, sich dreht usw. Die Aktivitäten können auch als Ort dienen, Bildschirmfreigaben.

Im Kontext Ihrer App sollten Aktivitäten als Host für die Navigation dienen. und die Logik und das Wissen für den Wechsel zwischen Bildschirmen enthalten, Daten weitergeben usw. Es ist jedoch besser, die Details Ihrer Benutzeroberfläche zu verwalten. in einen kleineren, wiederverwendbaren Teil Ihrer Benutzeroberfläche einfügen. Die empfohlene Implementierung Muster sind Fragmente. Weitere Informationen finden Sie unter Einzelne Aktivität: Warum, wann und wie erfahren Sie mehr über die Vorteile von Fragmenten. Die Navigation unterstützt Fragmente über die Abhängigkeit navigation-fragment. Navigation unterstützt auch benutzerdefinierten Zieltypen.

Wenn Ihre Anwendung keine Fragmente verwendet, müssen Sie als Erstes migrieren für jeden Bildschirm Ihrer App ein Fragment. Sie entfernen die Aktivität nicht unter Punkt zu kommen. Stattdessen erstellen Sie ein Fragment, das den Bildschirm repräsentiert und den Ihre UI-Logik nach Verantwortung differenzieren.

Jetzt neu: Fragmente

Beginnen wir mit einem Beispiel, um den Prozess der Einführung von Fragmenten zu veranschaulichen. einer Anwendung, die aus zwei Bildschirmen besteht: einem Bildschirm mit einer Produktliste und einem mit den Produktdetails. Durch Klicken auf ein Produkt auf dem Listenbildschirm zu einem Bildschirm mit Details, um mehr über das Produkt zu erfahren.

In diesem Beispiel sind die Bildschirme „Liste“ und „Details“ derzeit separate Aktivitäten.

Neues Layout für das Hosten der Benutzeroberfläche erstellen

Um ein Fragment einzuführen, erstellen Sie zunächst eine neue Layoutdatei für die Aktivität, zum Hosten des Fragments. Dadurch wird das aktuelle Layout der Inhaltsansicht der Aktivität ersetzt.

Für eine einfache Ansicht können Sie ein FrameLayout verwenden, wie im Folgenden gezeigt. Beispiel product_list_host:

<FrameLayout
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/main_content"
   android:layout_height="match_parent"
   android:layout_width="match_parent" />

Das Attribut id bezieht sich auf den Inhaltsabschnitt, in dem wir später Fragment.

Ändern Sie als Nächstes in der Funktion onCreate() Ihrer Aktivität die Layoutdatei-Referenz. , um auf diese neue Layoutdatei zu verweisen:

Kotlin

class ProductListActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Replace setContentView(R.layout.product_list) with the line below
        setContentView(R.layout.product_list_host)
        ...
    }
}

Java

public class ProductListActivity extends AppCompatActivity {
    ...
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        // Replace setContentView(R.layout.product_list); with the line below
        setContentView(R.layout.product_list_host);
        ...
    }
}

Das vorhandene Layout (in diesem Beispiel product_list) wird als Stammansicht verwendet. für das Fragment, das Sie erstellen möchten.

Fragment erstellen

Erstellen Sie ein neues Fragment, um die Benutzeroberfläche für Ihren Bildschirm zu verwalten. Es empfiehlt sich, mit dem Hostnamen Ihrer Aktivität übereinstimmen. Im folgenden Snippet wird ProductListFragment, z. B.:

Kotlin

class ProductListFragment : Fragment() {
    // Leave empty for now.
}

Java

public class ProductListFragment extends Fragment {
    // Leave empty for now.
}

Aktivitätslogik in ein Fragment verschieben

Nach der Fragmentdefinition besteht der nächste Schritt darin, die UI-Logik für aus der Aktivität in dieses neue Fragment übertragen. Wenn Sie aus einem eine aktivitätsbasierte Architektur verwenden, in der onCreate()-Funktion Ihrer Aktivität.

Hier ist ein Beispiel für einen aktivitätsbasierten Bildschirm mit UI-Logik, den wir verschieben müssen:

Kotlin

class ProductListActivity : AppCompatActivity() {

    // Views and/or ViewDataBinding references, Adapters...
    private lateinit var productAdapter: ProductAdapter
    private lateinit var binding: ProductListActivityBinding

    ...

    // ViewModels, System Services, other Dependencies...
    private val viewModel: ProductListViewModel by viewModels()

    ...

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

        // View initialization logic
        DataBindingUtil.setContentView(this, R.layout.product_list_activity)

        // Post view initialization logic
        // Connect adapters
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener {...}

        // Subscribe to state
        viewModel.products.observe(this, Observer { myProducts ->
            ...
        })

        // ...and so on
    }
   ...
}

Java

public class ProductListActivity extends AppCompatActivity {

    // Views and/or ViewDataBinding references, adapters...
    private ProductAdapter productAdapter;
    private ProductListActivityBinding binding;

    ...

    // ViewModels, system services, other dependencies...
    private ProductListViewModel viewModel;

    ...

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // View initialization logic
        DataBindingUtil.setContentView(this, R.layout.product_list_activity);

        // Post view initialization logic
        // Connect adapters
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);

        // Initialize ViewModels and other dependencies
        ProductListViewModel viewModel = new ViewModelProvider(this).get(ProductListViewModel.java);

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener(v -> { ... });

        // Subscribe to state
        viewModel.getProducts().observe(this, myProducts ->
            ...
       );

       // ...and so on
   }

Ihre Aktivitäten können auch steuern, wann und wie Nutzende zur nächsten Bildschirm aus, wie im folgenden Beispiel gezeigt:

Kotlin

    // Provided to ProductAdapter in ProductListActivity snippet.
    private val productClickCallback = ProductClickCallback { product ->
        show(product)
    }

    fun show(product: Product) {
        val intent = Intent(this, ProductActivity::class.java)
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id)
        startActivity(intent)
    }

Java

// Provided to ProductAdapter in ProductListActivity snippet.
private ProductClickCallback productClickCallback = this::show;

private void show(Product product) {
    Intent intent = new Intent(this, ProductActivity.class);
    intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId());
    startActivity(intent);
}

Innerhalb Ihres Fragments verteilen Sie diese Arbeit zwischen onCreateView() und onViewCreated(), wobei nur die Navigationslogik in der Aktivität verbleibt:

Kotlin

class ProductListFragment : Fragment() {

    private lateinit var binding: ProductListFragmentBinding
    private val viewModel: ProductListViewModel by viewModels()

     // View initialization logic
    override fun onCreateView(inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(
                inflater,
                R.layout.product_list,
                container,
                false
        )
        return binding.root
    }

    // Post view initialization logic
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // Connect adapters
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener {...}

        // Subscribe to state
        viewModel.products.observe(this, Observer { myProducts ->
            ...
        })

        // ...and so on
    }

    // Provided to ProductAdapter
    private val productClickCallback = ProductClickCallback { product ->
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            (requireActivity() as ProductListActivity).show(product)
        }
    }
    ...
}

Java

public class ProductListFragment extends Fragment {

    private ProductAdapter productAdapter;
    private ProductListFragmentBinding binding;

    // View initialization logic
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
            @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(
                inflater,
                R.layout.product_list_fragment,
                container,
                false);
        return binding.getRoot();
    }

    // Post view initialization logic
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {

        // Connect adapters
        binding.productsList.setAdapter(productAdapter);

        // Initialize ViewModels and other dependencies
        ProductListViewModel viewModel = new ViewModelProvider(this)
                .get(ProductListViewModel.class);

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener(...)

        // Subscribe to state
        viewModel.getProducts().observe(this, myProducts -> {
            ...
       });

       // ...and so on

    // Provided to ProductAdapter
    private ProductClickCallback productClickCallback = new ProductClickCallback() {
        @Override
        public void onClick(Product product) {
            if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
                ((ProductListActivity) requireActivity()).show(product);
            }
        }
    };
    ...
}

Beachten Sie, dass in ProductListFragment kein Aufruf an setContentView() um das Layout zu erweitern und zu verbinden. In einem Fragment initialisiert onCreateView() das Objekt Stammansicht. onCreateView() nimmt eine Instanz eines LayoutInflater, mit dem Sie die Stammansicht basierend auf einer Layout-Ressourcendatei in die Höhe treiben. In diesem Beispiel wird der Parameter Vorhandenes product_list-Layout, das von der Aktivität verwendet wurde, da nichts das Layout selbst ändern muss.

Wenn du UI-Logik in den onStart(), onResume(), onPause() oder onStop(), die nicht mit der Navigation zusammenhängen, können Sie verschieben Sie diese in entsprechende Funktionen mit demselben Namen im Fragment.

Fragment in der Hostaktivität initialisieren

Nachdem Sie die gesamte UI-Logik nach unten in das Fragment verschoben haben, Logik in der Aktivität bleiben.

Kotlin

class ProductListActivity : AppCompatActivity() {

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

    fun show(product: Product) {
        val intent = Intent(this, ProductActivity::class.java)
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id)
        startActivity(intent)
    }
}

Java

public class ProductListActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.product_list_host);
    }

    public void show(Product product) {
        Intent intent = new Intent(this, ProductActivity.class);
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId());
        startActivity(intent);
    }
}

Im letzten Schritt erstellen Sie eine Instanz des Fragments in onCreate(), nachdem Sie die Inhaltsansicht festgelegt haben:

Kotlin

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

    if (savedInstanceState == null) {
        val fragment = ProductListFragment()
        supportFragmentManager
                .beginTransaction()
                .add(R.id.main_content, fragment)
                .commit()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.product_list_host);

    if (savedInstanceState == null) {
        ProductListFragment fragment = new ProductListFragment();
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.main_content, fragment)
                .commit();
    }
}

Wie in diesem Beispiel gezeigt, speichert FragmentManager die Daten automatisch Vorrang vor Konfigurationsänderungen. Sie müssen das Fragment also nur hinzufügen, savedInstanceState ist null.

Intent-Extras an das Fragment übergeben

Wenn Ihre Aktivität Extras über einen Intent empfängt, können Sie diese an den direkt als Argumente fragmentieren.

In diesem Beispiel erhält der ProductDetailsFragment seine Argumente direkt aus den Intent-Extras der Aktivität entfernen:

Kotlin

...

if (savedInstanceState == null) {
    val fragment = ProductDetailsFragment()

    // Intent extras and Fragment Args are both of type android.os.Bundle.
    fragment.arguments = intent.extras

    supportFragmentManager
            .beginTransaction()
            .add(R.id.main_content, fragment)
            .commit()
}

...

Java

...

if (savedInstanceState == null) {
    ProductDetailsFragment fragment = new ProductDetailsFragment();

    // Intent extras and fragment Args are both of type android.os.Bundle.
    fragment.setArguments(getIntent().getExtras());

    getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.main_content, fragment)
            .commit();
}

...

Jetzt sollten Sie die Ausführung Ihrer App mit dem ersten Bildschirm testen können. zur Verwendung eines Fragments aktualisiert. Migration der restlichen aktivitätsbasierten Bildschirmen und nehmen Sie sich Zeit, nach jeder Iteration zu testen.

Navigationskomponente einbinden

Sobald Sie eine fragmentierte Architektur verwenden, können Sie mit der Integration der Navigationskomponente beginnen.

Fügen Sie Ihrem Projekt zuerst die neuesten Navigationsabhängigkeiten hinzu, die Anweisungen im Versionshinweise zur Navigationsbibliothek

Navigationsdiagramm erstellen

Die Komponente „Navigation“ stellt die Navigationskonfiguration Ihrer App in einem Ressourcendatei als Grafik, ähnlich wie die Aufrufe Ihrer App. Das hilft, die Navigation Ihrer App außerhalb Ihrer Codebasis organisieren und damit Sie Ihre App-Navigation visuell bearbeiten können.

Um ein Navigationsdiagramm zu erstellen, erstellen Sie zuerst einen neuen Ressourcenordner mit dem Namen navigation Klicken Sie zum Hinzufügen der Grafik mit der rechten Maustaste auf dieses Verzeichnis und wählen Sie Neu > Datei mit Navigationsressourcen.

Die Komponente „Navigation“ verwendet eine Aktivität als Host für Navigation und tauscht einzelne Fragmente in diesen Host aus, während sich Ihre Nutzer für Ihre App. Bevor Sie damit beginnen können, die Navigation Ihrer App visuell zu organisieren, müssen Sie eine NavHost in der Aktivität konfigurieren, die Diagramm. Da wir Fragmente verwenden, können wir die Navigationskomponente Standardimplementierung von NavHost, NavHostFragment

Eine NavHostFragment wird über einen FragmentContainerView konfiguriert innerhalb einer Hostaktivität platziert werden, wie im folgenden Beispiel gezeigt:

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

Das Attribut app:NavGraph verweist auf die zugehörige Navigationsgrafik. Navigations-Host. Durch Festlegen dieser Eigenschaft wird die Navigationsgrafik aufgebläht und die Grafik festgelegt auf der NavHostFragment. Das Attribut app:defaultNavHost sorgt dafür, dass dein NavHostFragment die Zurück-Taste des Systems abfängt.

Wenn Sie die Navigation der obersten Ebene verwenden, z. B. DrawerLayout oder BottomNavigationView, dieses FragmentContainerView ersetzt das Hauptelement der Inhaltsansicht. Weitere Informationen finden Sie unter UI-Komponenten mit NavigationUI aktualisieren .

Für ein einfaches Layout können Sie diesen FragmentContainerView einfügen. -Element als untergeordnetes Element des Stamms ViewGroup:

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

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

</FrameLayout>

Wenn Sie unten auf den Tab Design klicken, sollten Sie ein ähnliches Diagramm sehen. mit dem unten angezeigten. Klicken Sie oben links in der Grafik Ziele können Sie im Formular einen Verweis auf die NavHost-Aktivität sehen. von layout_name (resource_id).

Klicken Sie auf das Pluszeichen oben, um dem Diagramm Ihre Fragmente hinzuzufügen.

Bei der Komponente „Navigation“ werden einzelne Bildschirme als Ziele bezeichnet. Ziele können Fragmente, Aktivitäten oder benutzerdefinierte Ziele sein. Sie können beliebige Ziel in die Grafik aufnehmen. Aktivitätsziele sind jedoch werden als Terminalziele betrachtet, da Sie, wenn Sie zu einer Aktivität navigieren, Ziel ist es, innerhalb eines separaten Navigationshosts und einer separaten Grafik zu arbeiten.

Die Navigationskomponente bezieht sich darauf, wie Nutzende von einer Ziel als Aktionen zu einem anderen Ziel hinzufügen. Aktionen können auch eine Umstellung beschreiben Animationen und Popverhalten.

Fragmenttransaktionen entfernen

Wenn Sie nun die Navigationskomponente verwenden und zwischen fragmentierten Bildschirmen innerhalb derselben Aktivität navigieren, können Sie FragmentManager Interaktionen.

Wenn Ihre App mehrere Fragmente unter derselben Aktivität oder auf oberster Ebene verwendet z. B. eine Navigationsleiste am unteren Rand, mit FragmentManager und FragmentTransactions um Fragmente im Hauptinhaltsbereich deiner Benutzeroberfläche hinzuzufügen oder zu ersetzen. Dies kann jetzt ersetzt und mithilfe der Navigationskomponente vereinfacht werden, indem Aktionen können Sie Ziele in der Grafik verknüpfen und dann mithilfe der NavController

Hier sind einige mögliche Szenarien und die mögliche Vorgehensweise, Migration für jedes Szenario.

Einzelne Aktivität zum Verwalten mehrerer Fragmente

Wenn Sie eine einzelne Aktivität haben, die mehrere Fragmente verwaltet, ist Ihre Aktivität könnte wie folgt aussehen:

Kotlin

class MainActivity : AppCompatActivity() {

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

        // Logic to load the starting destination
        //  when the Activity is first created
        if (savedInstanceState == null) {
            val fragment = ProductListFragment()
            supportFragmentManager.beginTransaction()
                    .add(R.id.fragment_container, fragment, ProductListFragment.TAG)
                    .commit()
        }
    }

    // Logic to navigate the user to another destination.
    // This may include logic to initialize and set arguments on the destination
    // fragment or even transition animations between the fragments (not shown here).
    fun navigateToProductDetail(productId: String) {
        val fragment = new ProductDetailsFragment()
        val args = Bundle().apply {
            putInt(KEY_PRODUCT_ID, productId)
        }
        fragment.arguments = args

        supportFragmentManager.beginTransaction()
                .addToBackStack(ProductDetailsFragment.TAG)
                .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG)
                .commit()
    }
}

Java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Logic to load the starting destination when the activity is first created.
        if (savedInstanceState == null) {
            val fragment = ProductListFragment()
            supportFragmentManager.beginTransaction()
                    .add(R.id.fragment_container, fragment, ProductListFragment.TAG)
                    .commit();
        }
    }

    // Logic to navigate the user to another destination.
    // This may include logic to initialize and set arguments on the destination
    // fragment or even transition animations between the fragments (not shown here).
    public void navigateToProductDetail(String productId) {
        Fragment fragment = new ProductDetailsFragment();
        Bundle args = new Bundle();
        args.putInt(KEY_PRODUCT_ID, productId);
        fragment.setArguments(args);

        getSupportFragmentManager().beginTransaction()
                .addToBackStack(ProductDetailsFragment.TAG)
                .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG)
                .commit();
    }
}

Innerhalb des Quellziels rufen Sie möglicherweise eine Navigationsfunktion in auf ein Ereignis reagieren:

Kotlin

class ProductListFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // In this example a callback is passed to respond to an item clicked
        //  in a RecyclerView
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private val productClickCallback = ProductClickCallback { product ->
            (requireActivity() as MainActivity).navigateToProductDetail(product.id)
    }
}

Java

public class ProductListFragment extends Fragment  {
    ...
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
    // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private ProductClickCallback productClickCallback = product -> (
        ((MainActivity) requireActivity()).navigateToProductDetail(product.getId())
    );
}

Sie können dies ersetzen, indem Sie die Navigationsgrafik das Startziel und Aktionen zum Verknüpfen Ihrer Ziele und Argumente, wo erforderlich:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_detail" />
    </fragment>
    <fragment
        android:id="@+id/product_detail"
        android:name="com.example.android.persistence.ui.ProductDetailFragment"
        android:label="Product Detail"
        tools:layout="@layout/product_detail">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>
</navigation>

Anschließend können Sie Ihre Aktivitäten aktualisieren:

Kotlin

class MainActivity : AppCompatActivity() {

    // No need to load the start destination, handled automatically by the Navigation component
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    // No need to load the start destination, handled automatically by the Navigation component
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Für die Aktivität ist keine navigateToProductDetail()-Methode mehr erforderlich. Im nächsten Wir aktualisieren ProductListFragment, um die NavController zum Navigieren zum nächsten Bildschirm mit Produktdetails weitergeleitet.

Argumente sicher übergeben

Die Navigationskomponente verfügt über ein Gradle-Plug-in namens Sichere Argumente generiert einfache Objekt- und Builder-Klassen für typsicheren Zugriff auf für Ziele und Aktionen angegebene Argumente.

Nach der Anwendung des Plug-ins werden alle Argumente, die für ein Ziel in Ihrem Navigationsdiagramm veranlasst, dass das Framework der Komponente "Navigation" ein Die Klasse Arguments, die typsichere Argumente für das Ziel bereitstellt. Wenn Sie eine Aktion definieren, generiert das Plug-in eine Directions-Konfiguration Klasse, mit der dem NavController mitgeteilt werden kann, wie der Nutzer zu auf das Ziel hin. Wenn eine Aktion auf ein Ziel verweist, für das ein enthält die generierte Directions-Klasse Konstruktormethoden, die die diese Parameter erfordern.

Verwenden Sie im Fragment NavController und die generierte Directions-Klasse, um stellen typsichere Argumente für das Ziel bereit, wie im Folgenden gezeigt: Beispiel:

Kotlin

class ProductListFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)
    }
    ...

    // The callback makes the call to the NavController to make the transition.
    private val productClickCallback = ProductClickCallback { product ->
        val directions = ProductListDirections.navigateToProductDetail(product.id)
        findNavController().navigate(directions)
    }
}

Java

public class ProductListFragment extends Fragment  {
    ...
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
        // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private ProductClickCallback productClickCallback = product -> {
        ProductListDirections.ViewProductDetails directions =
                ProductListDirections.navigateToProductDetail(product.getId());
        NavHostFragment.findNavController(this).navigate(directions);
    };
}

Navigation der obersten Ebene

Wenn deine Anwendung ein DrawerLayout verwendet, hast du möglicherweise umfangreiche Konfigurationslogik. das Öffnen und Schließen der Leiste und das Navigieren andere Ziele.

Die daraus resultierende Aktivität könnte etwa so aussehen:

Kotlin

class MainActivity : AppCompatActivity(),
    NavigationView.OnNavigationItemSelectedListener {

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

        val toolbar: Toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)

        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        val toggle = ActionBarDrawerToggle(
                this,
                drawerLayout,
                toolbar,
                R.string.navigation_drawer_open, 
                R.string.navigation_drawer_close
        )
        drawerLayout.addDrawerListener(toggle)
        toggle.syncState()

        navView.setNavigationItemSelectedListener(this)
    }

    override fun onBackPressed() {
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START)
        } else {
            super.onBackPressed()
        }
    }

    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        // Handle navigation view item clicks here.
        when (item.itemId) {
            R.id.home -> {
                val homeFragment = HomeFragment()
                show(homeFragment)
            }
            R.id.gallery -> {
                val galleryFragment = GalleryFragment()
                show(galleryFragment)
            }
            R.id.slide_show -> {
                val slideShowFragment = SlideShowFragment()
                show(slideShowFragment)
            }
            R.id.tools -> {
                val toolsFragment = ToolsFragment()
                show(toolsFragment)
            }
        }
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        drawerLayout.closeDrawer(GravityCompat.START)
        return true
    }
}

private fun show(fragment: Fragment) {

    val drawerLayout = drawer_layout as DrawerLayout
    val fragmentManager = supportFragmentManager

    fragmentManager
            .beginTransaction()
            .replace(R.id.main_content, fragment)
            .commit()

    drawerLayout.closeDrawer(GravityCompat.START)
}

Java

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this,
                drawer,
                toolbar,
                R.string.navigation_drawer_open,
                R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.home) {
            Fragment homeFragment = new HomeFragment();
            show(homeFragment);
        } else if (id == R.id.gallery) {
            Fragment galleryFragment = new GalleryFragment();
            show(galleryFragment);
        } else if (id == R.id.slide_show) {
            Fragment slideShowFragment = new SlideShowFragment();
            show(slideShowFragment);
        } else if (id == R.id.tools) {
            Fragment toolsFragment = new ToolsFragment();
            show(toolsFragment);
        }

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    private void show(Fragment fragment) {

        DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
        FragmentManager fragmentManager = getSupportFragmentManager();

        fragmentManager
                .beginTransaction()
                .replace(R.id.main_content, fragment)
                .commit();

        drawerLayout.closeDrawer(GravityCompat.START);
    }
}

Nachdem Sie die Navigationskomponente zu Ihrem Projekt hinzugefügt und eine Navigationsdiagramm hinzugefügt haben, fügen Sie die einzelnen Inhaltsziele aus Ihrer Grafik hinzu (z. B. Home (Startseite), Gallery (Galerie), SlideShow (Diashow) und Tools (Tools) aus dem obigen Beispiel. Achten Sie darauf, die Werte deines Menüelements id mit den zugehörigen id-Werten für das Ziel übereinstimmen, wie unten dargestellt:

<!-- activity_main_drawer.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/home"
            android:icon="@drawable/ic_menu_camera"
            android:title="@string/menu_home" />
        <item
            android:id="@+id/gallery"
            android:icon="@drawable/ic_menu_gallery"
            android:title="@string/menu_gallery" />
        <item
            android:id="@+id/slide_show"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="@string/menu_slideshow" />
        <item
            android:id="@+id/tools"
            android:icon="@drawable/ic_menu_manage"
            android:title="@string/menu_tools" />
    </group>
</menu>
<!-- activity_main_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_graph"
    app:startDestination="@id/home">

    <fragment
        android:id="@+id/home"
        android:name="com.example.HomeFragment"
        android:label="Home"
        tools:layout="@layout/home" />

    <fragment
        android:id="@+id/gallery"
        android:name="com.example.GalleryFragment"
        android:label="Gallery"
        tools:layout="@layout/gallery" />

    <fragment
        android:id="@+id/slide_show"
        android:name="com.example.SlideShowFragment"
        android:label="Slide Show"
        tools:layout="@layout/slide_show" />

    <fragment
        android:id="@+id/tools"
        android:name="com.example.ToolsFragment"
        android:label="Tools"
        tools:layout="@layout/tools" />

</navigation>

Wenn Sie die id-Werte aus Ihrem Menü und der Grafik anpassen, können Sie die Werte verbinden. NavController für diese Aktivität, um die Navigation basierend auf auf den Menüpunkt. NavController übernimmt auch das Öffnen und Schließen des DrawerLayout und korrektes Verhalten der Schaltflächen „Aufwärts“ und „Zurück“.

Dein MainActivity kann dann aktualisiert werden, um NavController mit dem Toolbar und NavigationView.

Das folgende Snippet zeigt ein Beispiel:

Kotlin

class MainActivity : AppCompatActivity()  {

    val drawerLayout by lazy { findViewById<DrawerLayout>(R.id.drawer_layout) }
    val navController by lazy {
      (supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment).navController
    }
    val navigationView by lazy { findViewById<NavigationView>(R.id.nav_view) }

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

        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)

        // Show and Manage the Drawer and Back Icon
        setupActionBarWithNavController(navController, drawerLayout)

        // Handle Navigation item clicks
        // This works with no further action on your part if the menu and destination id’s match.
        navigationView.setupWithNavController(navController)

    }

    override fun onSupportNavigateUp(): Boolean {
        // Allows NavigationUI to support proper up navigation or the drawer layout
        // drawer menu, depending on the situation
        return navController.navigateUp(drawerLayout)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    private DrawerLayout drawerLayout;
    private NavController navController;
    private NavigationView navigationView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        drawerLayout = findViewById(R.id.drawer_layout);
        NavHostFragment navHostFragment = (NavHostFragment)
            getSupportFragmentManager().findFragmentById(R.id.main_content);
        navController = navHostFragment.getNavController();
        navigationView = findViewById(R.id.nav_view);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Show and Manage the Drawer and Back Icon
        NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

        // Handle Navigation item clicks
        // This works with no further action on your part if the menu and destination id’s match.
        NavigationUI.setupWithNavController(navigationView, navController);

    }

    @Override
    public boolean onSupportNavigateUp() {
        // Allows NavigationUI to support proper up navigation or the drawer layout
        // drawer menu, depending on the situation.
        return NavigationUI.navigateUp(navController, drawerLayout);

    }
}

Sie können das gleiche Verfahren mit der BottomNavigationView-basierten Navigation anwenden. und menübasierte Navigation. Weitere Informationen finden Sie unter UI-Komponenten mit NavigationUI aktualisieren finden Sie weitere Beispiele.

Aktivitätsziele hinzufügen

Sobald jeder Bildschirm in Ihrer App für die Nutzung der Navigationskomponente verbunden ist, Sie verwenden FragmentTransactions nicht mehr für den Wechsel zwischen fragmentierten Zielen besteht der nächste Schritt darin, startActivity Anrufe.

Identifizieren Sie zunächst die Stellen in Ihrer App, an denen zwei separate Navigationsgrafiken vorhanden sind. und verwenden startActivity für den Wechsel zwischen ihnen.

Dieses Beispiel enthält zwei Diagramme (A und B) und einen startActivity()-Aufruf für von A nach B zu wechseln.

Kotlin

fun navigateToProductDetails(productId: String) {
    val intent = Intent(this, ProductDetailsActivity::class.java)
    intent.putExtra(KEY_PRODUCT_ID, productId)
    startActivity(intent)
}

Java

private void navigateToProductDetails(String productId) {
    Intent intent = new Intent(this, ProductDetailsActivity.class);
    intent.putExtra(KEY_PRODUCT_ID, productId);
    startActivity(intent);

Ersetzen Sie als Nächstes diese durch ein Aktivitätsziel in Diagramm A, das die die Navigation zur Hostaktivität in Diagramm B. Wenn Sie Argumente an die Startziel von Graph B, können Sie diese im Aktivitätsziel Definition.

Im folgenden Beispiel definiert Diagramm A ein Aktivitätsziel, das eine product_id-Argument zusammen mit einer Aktion. Grafik B enthält keine Änderungen.

Die XML-Darstellung der Grafiken A und B könnte wie folgt aussehen:

<!-- Graph A -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List"
        tools:layout="@layout/product_list_fragment">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details_activity" />
    </fragment>

    <activity
        android:id="@+id/product_details_activity"
        android:name="com.example.android.persistence.ui.ProductDetailsActivity"
        android:label="Product Details"
        tools:layout="@layout/product_details_host">

        <argument
            android:name="product_id"
            app:argType="integer" />

    </activity>

</navigation>
<!-- Graph B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/product_details">

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details_fragment">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>

</navigation>

Zur Hostaktivität von Graph B gelangen Sie mit den Verwenden Sie zum Aufrufen von Fragmentzielen:

Kotlin

fun navigateToProductDetails(productId: String) {
    val directions = ProductListDirections.navigateToProductDetail(productId)
    findNavController().navigate(directions)
}

Java

private void navigateToProductDetails(String productId) {
    ProductListDirections.NavigateToProductDetail directions =
            ProductListDirections.navigateToProductDetail(productId);
    Navigation.findNavController(getView()).navigate(directions);

Aktivitätszielargumente an ein Startzielfragment übergeben

Wenn die Zielaktivität wie im vorherigen Beispiel Extras erhält, Sie können diese direkt als Argumente an das Startziel übergeben. Sie müssen jedoch Navigationsdiagramm Ihres Hosts manuell in die onCreate()-Methode, sodass Sie die Intent-Extras als Argumente an die wie unten dargestellt:

Kotlin

class ProductDetailsActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.product_details_host)
        val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment
        val navController = navHostFramgent.navController
        navController
                .setGraph(R.navigation.product_detail_graph, intent.extras)
    }

}

Java

public class ProductDetailsActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.product_details_host);
        NavHostFragment navHostFragment = (NavHostFragment)
            getSupportFragmentManager().findFragmentById(R.id.main_content);
        NavController navController = navHostFragment.getNavController();
        navController
                .setGraph(R.navigation.product_detail_graph, getIntent().getExtras());
    }

}

Die Daten können mithilfe der MethodeBundle generierte args-Klasse, wie im folgenden Beispiel gezeigt:

Kotlin

class ProductDetailsFragment : Fragment() {

    val args by navArgs<ProductDetailsArgs>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val productId = args.productId
        ...
    }
    ...

Java

public class ProductDetailsFragment extends Fragment {

    ProductDetailsArgs args;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        args = ProductDetailsArgs.fromBundle(requireArguments());
    }

    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
       int productId = args.getProductId();
       ...
    }
    ...

Aktivitäten kombinieren

Sie können Navigationsdiagramme kombinieren, wenn mehrere Aktivitäten das gleiche Layout zu erhalten, z. B. ein einfaches FrameLayout mit einem einzelnen Fragment. In In den meisten Fällen können Sie einfach alle Elemente aus den Navigationsdiagramm und Aktualisierung aller Elemente des Aktivitätsziels zur Fragmentierung Ziele.

Im folgenden Beispiel werden die Grafiken A und B aus dem vorherigen Abschnitt kombiniert:

Vor dem Kombinieren:

<!-- Graph A -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List Fragment"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details_activity" />
    </fragment>
    <activity
        android:id="@+id/product_details_activity"
        android:name="com.example.android.persistence.ui.ProductDetailsActivity"
        android:label="Product Details Host"
        tools:layout="@layout/product_details_host">
        <argument android:name="product_id"
            app:argType="integer" />
    </activity>

</navigation>
<!-- Graph B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_detail_graph"
    app:startDestination="@id/product_details">

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>
</navigation>

Nach dem Kombinieren:

<!-- Combined Graph A and B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List Fragment"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details" />
    </fragment>

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>

</navigation>

Wenn Sie die Aktionsnamen beibehalten, aber zusammenführen, ist das ein nahtloses Erlebnis. und erfordert keine Änderungen an Ihrer vorhandenen Codebasis. Beispiel: navigateToProductDetail bleibt hier gleich. Der einzige Unterschied ist, Diese Aktion stellt nun die Navigation zu einem Fragmentziel im selben NavHost statt eines Aktivitätsziels:

Kotlin

fun navigateToProductDetails(productId: String) {
    val directions = ProductListDirections.navigateToProductDetail(productId)
    findNavController().navigate(directions)
}

Java

private void navigateToProductDetails(String productId) {
    ProductListDirections.NavigateToProductDetail directions =
            ProductListDirections.navigateToProductDetail(productId);
    Navigation.findNavController(getView()).navigate(directions);

Zusätzliche Ressourcen

Weitere Informationen zur Navigation finden Sie in den folgenden Themen: