Gezinme bileşeniyle programatik olarak etkileşim kurun

Gezinme bileşeni, belirli gezinme öğelerini programatik olarak oluşturmak ve bunlarla etkileşimde bulunmak için yöntemler sağlar.

NavHostFragment oluşturma

Aşağıdaki örnekte gösterildiği gibi, belirli bir grafik kaynağıyla programatik bir şekilde NavHostFragment oluşturmak için NavHostFragment.create() kullanabilirsiniz:

Kotlin

val finalHost = NavHostFragment.create(R.navigation.example_graph)
supportFragmentManager.beginTransaction()
    .replace(R.id.nav_host, finalHost)
    .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true"
    .commit()

Java

NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph);
getSupportFragmentManager().beginTransaction()
    .replace(R.id.nav_host, finalHost)
    .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true"
    .commit();

setPrimaryNavigationFragment(finalHost) işlevinin NavHost sisteminizin Geri düğmesine basmasını engellediğini unutmayın. Bu davranışı app:defaultNavHost="true" ekleyerek NavHost XML'nizde de uygulayabilirsiniz. Özel Geri düğmesi davranışını uyguluyor ve NavHost öğesinin Geri düğmesine basılmasını engellemek istemiyorsanız null politikasını setPrimaryNavigationFragment() değerine geçirebilirsiniz.

Gezinme 2.2.0'dan başlayarak, gezinme yığınındaki herhangi bir hedefe ilişkin NavBackStackEntry referansını, NavController.getBackStackEntry() çağrısı yaparak ve bu hedefe bir hedef kimliği ileterek alabilirsiniz. Arka yığın, belirtilen hedefin birden fazla örneğini içeriyorsa getBackStackEntry(), yığındaki en üst örneği döndürür.

Döndürülen NavBackStackEntry, hedef düzeyinde bir Lifecycle, ViewModelStore ve SavedStateRegistry sağlar. Bu nesneler, arka yığındaki hedefin kullanım ömrü boyunca geçerlidir. İlişkili hedef arka yığından çıkarıldığında Lifecycle yok edilir, durum artık kaydedilmez ve tüm ViewModel nesneleri temizlenir.

Bu özellikler, ne tür bir hedef kullanırsanız kullanın size kaydedilmiş durum ile çalışan ViewModel nesne ve sınıfları için bir Lifecycle ve depolama alanı sağlar. Bu, özellikle özel hedefler gibi otomatik olarak ilişkili bir Lifecycle içermeyen hedef türleriyle çalışırken kullanışlıdır.

Örneğin, bir NavBackStackEntry öğesinin Lifecycle özelliğini, bir parçanın veya etkinliğin Lifecycle özelliğini gözlemlediğiniz gibi gözlemleyebilirsiniz. Ayrıca NavBackStackEntry bir LifecycleOwner olduğundan aşağıdaki örnekte gösterildiği gibi LiveData öğesini gözlemlerken veya yaşam döngüsüne duyarlı diğer bileşenlerle birlikte kullanabilirsiniz:

Kotlin

myViewModel.liveData.observe(backStackEntry, Observer { myData ->
    // react to live data update
})

Java

myViewModel.getLiveData().observe(backStackEntry, myData -> {
    // react to live data update
});

navigate() numaralı telefonu her aradığınızda yaşam döngüsü durumu otomatik olarak güncellenir. Geri yığının en üstünde olmayan hedefler için yaşam döngüsü durumları, hedefler iletişim kutusu hedefi gibi bir FloatingWindow hedefi altında hâlâ görünür durumdaysa RESUMED konumundan STARTED konumuna, aksi takdirde STOPPED konumuna taşınır.

Bir sonucu önceki Hedefe döndürme

Navigasyon 2.3 ve sonraki sürümlerde NavBackStackEntry, bir SavedStateHandle öğesine erişim izni verir. SavedStateHandle, verileri depolamak ve almak için kullanılabilecek bir anahtar/değer eşlemesidir. Bu değerler, yapılandırma değişiklikleri de dahil olmak üzere işlemin tamamlanmasına kadar devam eder ve aynı nesne üzerinden kullanılabilir durumda kalır. Belirtilen SavedStateHandle sayesinde verilere erişebilir ve hedefler arasında veri aktarabilirsiniz. Bu, özellikle verileri bir hedef yığından kaldırıldıktan sonra geri alma mekanizması olarak yararlıdır.

Verileri Hedef B'den A Hedefine geri iletmek için önce A Hedefi'ni SavedStateHandle üzerinden bir sonucu dinleyecek şekilde ayarlayın. Bunu yapmak için getCurrentBackStackEntry() API'yı ve ardından SavedStateHandle tarafından sağlanan observe LiveData öğesini kullanarak NavBackStackEntry alın.

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val navController = findNavController();
    // We use a String here, but any type that can be put in a Bundle is supported
    navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe(
        viewLifecycleOwner) { result ->
        // Do something with the result.
    }
}

Java

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    NavController navController = NavHostFragment.findNavController(this);
    // We use a String here, but any type that can be put in a Bundle is supported
    MutableLiveData<String> liveData = navController.getCurrentBackStackEntry()
            .getSavedStateHandle()
            .getLiveData("key");
    liveData.observe(getViewLifecycleOwner(), new Observer<String>() {
        @Override
        public void onChanged(String s) {
            // Do something with the result.
        }
    });
}

Hedef B'de, getPreviousBackStackEntry() API'yi kullanarak A Hedefi'nin SavedStateHandle sonucu için set gerekir.

Kotlin

navController.previousBackStackEntry?.savedStateHandle?.set("key", result)

Java

navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);

Bir sonucu yalnızca bir kez işlemek isterseniz sonucu temizlemek için SavedStateHandle üzerinde remove() işlevini çağırmanız gerekir. Sonucu kaldırmazsanız LiveData, son sonucu yeni Observer örneklerine döndürmeye devam eder.

İletişim kutusu hedeflerini kullanırken dikkat edilmesi gereken noktalar

NavHost öğesinin tam görünümünü alan bir hedefe navigate (ör. bir <fragment> hedefi) gittiğinizde, önceki hedefin yaşam döngüsü durdurulur ve SavedStateHandle tarafından sağlanan LiveData öğesine geri arama yapılması engellenir.

Bununla birlikte, bir iletişim kutusu hedefine gidildiğinde önceki hedef de ekranda görünür ve bu nedenle, geçerli hedef olmamasına rağmen yine STARTED olur. Diğer bir deyişle, onViewCreated() gibi yaşam döngüsü yöntemlerinden getCurrentBackStackEntry() için yapılan çağrılar, bir yapılandırma değişikliğinden veya işlemin kaybedilmesi ve yeniden oluşturulmasından sonra (iletişim kutusu diğer hedefin üzerine geri yüklendiği için) iletişim kutusu hedefinin NavBackStackEntry değerini döndürür. Bu nedenle, her zaman doğru NavBackStackEntry kodunu kullandığınızdan emin olmak için hedefinizin kimliğiyle birlikte getBackStackEntry() etiketini kullanmanız gerekir.

Bu aynı zamanda, LiveData sonucunda ayarladığınız tüm Observer öğelerinin, iletişim kutusu hedefleri hâlâ ekranda açık olsa bile tetikleneceği anlamına gelir. Sonucu, yalnızca iletişim kutusu hedefi kapatıldığında ve temel hedef geçerli hedef haline geldiğinde kontrol etmek isterseniz NavBackStackEntry ile ilişkili Lifecycle öğesini gözlemleyebilir ve sonucu yalnızca RESUMED olduğunda alabilirsiniz.

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val navController = findNavController();
    // After a configuration change or process death, the currentBackStackEntry
    // points to the dialog destination, so you must use getBackStackEntry()
    // with the specific ID of your destination to ensure we always
    // get the right NavBackStackEntry
    val navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment)

    // Create our observer and add it to the NavBackStackEntry's lifecycle
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME
            && navBackStackEntry.savedStateHandle.contains("key")) {
            val result = navBackStackEntry.savedStateHandle.get<String>("key");
            // Do something with the result
        }
    }
    navBackStackEntry.lifecycle.addObserver(observer)

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            navBackStackEntry.lifecycle.removeObserver(observer)
        }
    })
}

Java

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    NavController navController = NavHostFragment.findNavController(this);
    // After a configuration change or process death, the currentBackStackEntry
    // points to the dialog destination, so you must use getBackStackEntry()
    // with the specific ID of your destination to ensure we always
    // get the right NavBackStackEntry
    final NavBackStackEntry navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment);

    // Create our observer and add it to the NavBackStackEntry's lifecycle
    final LifecycleEventObserver observer = new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
            if (event.equals(Lifecycle.Event.ON_RESUME)
                && navBackStackEntry.getSavedStateHandle().contains("key")) {
                String result = navBackStackEntry.getSavedStateHandle().get("key");
                // Do something with the result
            }
        }
    };
    navBackStackEntry.getLifecycle().addObserver(observer);

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    getViewLifecycleOwner().getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
            if (event.equals(Lifecycle.Event.ON_DESTROY)) {
                navBackStackEntry.getLifecycle().removeObserver(observer)
            }
        }
    });
}

Geri gezinme yığını, yalnızca her bir hedef için değil, bağımsız hedefi içeren her bir üst gezinme grafiği için de bir NavBackStackEntry depolar. Bu işlem, gezinme grafiğine ayarlanmış bir NavBackStackEntry almanıza olanak tanır. Gezinme grafiği kapsamlı NavBackStackEntry, gezinme grafiğine ayarlanmış bir ViewModel oluşturmanın yolunu sağlar. Böylece, kullanıcı arayüzüyle ilgili verileri grafiğin hedefleri arasında paylaşabilirsiniz. Bu şekilde oluşturulan tüm ViewModel nesneleri, ilişkili NavHost ve ViewModelStore öğeleri temizlenene veya gezinme grafiği arka yığından çıkana kadar aktif kalır.

Aşağıdaki örnekte, bir gezinme grafiğine ayarlanmış bir ViewModel öğesinin nasıl alınacağı gösterilmektedir:

Kotlin

val viewModel: MyViewModel
        by navGraphViewModels(R.id.my_graph)

Java

NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph);
MyViewModel viewModel = new ViewModelProvider(backStackEntry).get(MyViewModel.class);

Navigation 2.2.0 veya önceki bir sürümü kullanıyorsanız ViewModels ile Kayıtlı Durum'u kullanmak için aşağıdaki örnekte gösterildiği gibi kendi fabrikanızı sağlamanız gerekir:

Kotlin

val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph) {
    SavedStateViewModelFactory(requireActivity().application, requireParentFragment())
}

Java

NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph);

ViewModelProvider viewModelProvider = new ViewModelProvider(
        backStackEntry.getViewModelStore(),
        new SavedStateViewModelFactory(
                requireActivity().getApplication(), requireParentFragment()));

MyViewModel myViewModel = provider.get(myViewModel.getClass());

ViewModel hakkında daha fazla bilgi için ViewModel'e Genel Bakış bölümüne bakın.

Şişirilmiş gezinme grafiklerini değiştirme

Şişirilmiş bir gezinme grafiğini çalışma zamanında dinamik olarak değiştirebilirsiniz.

Örneğin, NavGraph öğesine bağlı bir BottomNavigationView varsa NavGraph öğesinin varsayılan hedefi uygulama başlatılırken seçilen sekmeyi belirtir. Ancak, bir kullanıcı tercihinin uygulama başlangıcında yüklenmesini tercih edilen bir sekme belirtmesi gibi bu davranışı geçersiz kılmanız gerekebilir. Alternatif olarak, uygulamanızın başlangıç sekmesini geçmişteki kullanıcı davranışına göre değiştirmesi gerekebilir. Bu durumları, NavGraph öğesinin varsayılan hedefini dinamik olarak belirterek destekleyebilirsiniz.

Şunu düşünün: NavGraph:

<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
    app:startDestination="@id/home">
    <fragment
        android:id="@+id/home"
        android:name="com.example.android.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />
    <fragment
        android:id="@+id/location"
        android:name="com.example.android.navigation.LocationFragment"
        android:label="fragment_location"
        tools:layout="@layout/fragment_location" />
    <fragment
        android:id="@+id/shop"
        android:name="com.example.android.navigation.ShopFragment"
        android:label="fragment_shop"
        tools:layout="@layout/fragment_shop" />
    <fragment
        android:id="@+id/settings"
        android:name="com.example.android.navigation.SettingsFragment"
        android:label="fragment_settings"
        tools:layout="@layout/fragment_settings" />
</navigation>

Bu grafik yüklendiğinde app:startDestination özelliği, HomeFragment değerinin gösterileceğini belirtir. Başlangıç hedefini dinamik olarak geçersiz kılmak için aşağıdakileri yapın:

  1. Önce NavGraph şişirme işlemini manuel olarak yapın.
  2. Başlangıç hedefini geçersiz kılın.
  3. Son olarak, grafiği NavController öğesine manuel olarak ekleyin.

Kotlin

val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

val navController = navHostFragment.navController
val navGraph = navController.navInflater.inflate(R.navigation.bottom_nav_graph)
navGraph.startDestination = R.id.shop
navController.graph = navGraph
binding.bottomNavView.setupWithNavController(navController)

Java

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
        .findFragmentById(R.id.nav_host_fragment);

NavController navController = navHostFragment.getNavController();
NavGraph navGraph = navController.getNavInflater().inflate(R.navigation.bottom_nav_graph);
navGraph.setStartDestination(R.id.shop);
navController.setGraph(navGraph);
NavigationUI.setupWithNavController(binding.bottomNavView, navController);

Artık uygulamanız başladığında HomeFragment yerine ShopFragment gösterilir.

NavController, derin bağlantıları kullanırken derin bağlantı hedefi için otomatik olarak bir geri yığını oluşturur. Kullanıcı derin bağlantıya gidip daha sonra geriye giderse bir noktada başlangıç hedefine ulaşır. Önceki örnekte verilen tekniği kullanarak başlangıç hedefinin geçersiz kılınması, oluşturulan arka yığına doğru başlangıç hedefinin eklenmesini sağlar.

Bu tekniğin, gerektiğinde NavGraph öğesinin diğer yönlerinin geçersiz kılınmasına da olanak tanıdığını unutmayın. Derin bağlantıları işleme, durumu geri yükleme ve grafiğinizin başlangıç hedefine taşıma sırasında doğru yapının kullanıldığından emin olmak için, grafikteki tüm değişikliklerin setGraph() çağrısından önce yapılması gerekir.