BottomNavigationView'ü yeni NavController ile kullanırken parçayı canlı tutmanın bir yolu var mı?


96

Yeni gezinme bileşenini kullanmaya çalışıyorum. NavController ile BottomNavigationView kullanıyorum: NavigationUI.setupWithNavController (bottomNavigation, navController)

Ancak parçaları değiştirdiğimde, daha önce kullanılmış olsalar bile her seferinde yok ediyor / oluşturuyorlar.

BottomNavigationView ile ana fragmanlarımızı canlı tutmanın bir yolu var mı?


2
İssuetracker.google.com/issues/80029773 adresindeki yorum başına, bunun sonunda çözüleceği anlaşılıyor. Bununla birlikte, insanların bu arada bu işi yapmak için kütüphaneyi terk etmeyi içermeyen ucuz bir çözümü olup olmadığını da merak ediyorum.
Jimmy Alexander

Yanıtlar:


69

Bunu dene.

Navigator

Özel gezgin oluşturun.

@Navigator.Name("custom_fragment")  // Use as custom tag at navigation.xml
class CustomNavigator(
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?) {
        val tag = destination.id.toString()
        val transaction = manager.beginTransaction()

        val currentFragment = manager.primaryNavigationFragment
        if (currentFragment != null) {
            transaction.detach(currentFragment)
        }

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            fragment = destination.createFragment(args)
            transaction.add(containerId, fragment, tag)
        } else {
            transaction.attach(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
        transaction.setReorderingAllowed(true)
        transaction.commit()

        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED)
    }
}

NavHostFragment

Özel NavHostFragment oluşturun.

class CustomNavHostFragment: NavHostFragment() {
    override fun onCreateNavController(navController: NavController) {
        super.onCreateNavController(navController)
        navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id)
    }
}

navigation.xml

Parça etiketi yerine özel etiket kullanın.

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

    <custom_fragment
        android:id="@+id/navigation_first"
        android:name="com.example.sample.FirstFragment"
        android:label="FirstFragment" />
    <custom_fragment
        android:id="@+id/navigation_second"
        android:name="com.example.sample.SecondFragment"
        android:label="SecondFragment" />
</navigation>

aktivite düzeni

NavHostFragment yerine CustomNavHostFragment'ı kullanın.

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="com.example.sample.CustomNavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/navigation" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

Güncelleme

Örnek proje oluşturdum. bağlantı

Özel NavHostFragment oluşturmuyorum. Kullanıyorum navController.navigatorProvider += navigator.


3
Bu çözüm çalışırken ( fragmentsyeniden kullanılır, yeniden oluşturulmaz), gezinmeyle ilgili bir sorun vardır. Her menuitemgiriş bottomnavigationviewbirincil olarak kabul edilir - bu nedenle, BACKbasıldığında uygulama biter (veya en üst bileşene gider). Daha iyi bir çözüm, tıpkı YouTube uygulaması gibi davranmak olacaktır: (1) Ana Ekran'a dokunun; (2) Trend Olan'a dokunun; (3) Gelen Kutusu'na dokunun; (4) Trend'e dokunun; (5) Dokun BACK- Gelen Kutusu'na gider; (6) Dokun BACK- Ana Sayfaya gider; (7) Dokun BACK- uygulama var. Özetle, BACK" menuitems" arasındaki YouTube işlevi , öğeleri tekrar etmeden en yeniye döner.
miguelt

3
Düğme işlevselliği nasıl geri alınır? Geri düğmesine basıldığında uygulama tamamlanıyor.
Francis

5
@ jL4, aynı sorunu yaşadım, muhtemelen app:navGraphaktivitenizin NavHostFragment'ından kaldırmayı unuttunuz.
Paul Chernenko

6
Google neden bu işlevi kutudan çıkarmıyor?
Arsenius

55
NavigationComponent bir çözüm haline gelmeli ve başka bir sorun haline gelmemeli, bu nedenle programcı böyle bir geçici çözüm oluşturmalıdır.
Arie Agung

24

Google örnekleri bağlantısı Yalnızca NavigationExtensions'ı uygulamanıza kopyalayın ve örnek olarak yapılandırın. Harika çalışıyor.


1
Ancak yalnızca alt gezinme için çalışır. Genel bir navigasyonunuz varsa, bir sorununuz var
Georgiy Chebotarev

Aynı sorunla karşı karşıyayım ve kontrol ettiğim her çözümde kotlin kodu var ama korkarım ki projemde Java kullanıyorum. Birisi bu sorunu java'da nasıl çözeceğime yardımcı olabilir.
CodeRED Innovations

@CodeREDInnovations ben de denedim ve başarılı oldum kotlin dosyasıyla derlememe rağmen navigasyon şu şekilde kalıyor: imgur.com/a/DcsKqPr "değiştirmiyor", bunun yanında, uygulama daha ağır ve sıkışmış görünüyor.
Acauã Pitta

@ AcauãPitta Java'da bir çözüm buldunuz mu?
DevelopKinberg

11

Saatler süren araştırmalardan sonra çözüm buldum. Her zaman önümüzdeydi :) Bir fonksiyon var: popBackStack(destination, inclusive)backStack'te bulunursa verilen hedefe giden. Boolean'ı döndürür, böylece denetleyici parçayı bulamazsa oraya manuel olarak gidebiliriz.

if(findNavController().popBackStack(R.id.settingsFragment, false)) {
        Log.d(TAG, "SettingsFragment found in backStack")
    } else {
        Log.d(TAG, "SettingsFragment not found in backStack, navigate manually")
        findNavController().navigate(R.id.settingsFragment)
    }

Bunu nasıl ve nerede kullanmalı? Eğer Kısım A'dan B'ye, sonra B'den A'ya gidersem, kısaca A parçasını yeniden başlatmadan oncraeteView () ve onViewCreated () yöntemlerini çağırmadan nasıl gidebilirim
Kishan Solanki

@UsamaSaeedUS: Lütfen kodu paylaşır mısınız, nasıl kullanılır? Kishan'ın bahsettiği gibi.
Code_Life

2

Bağımsız değişkenleri iletmekte sorun yaşıyorsanız şunu ekleyin:

fragment.arguments = args

sınıfta KeepStateNavigator


1

Şu an için mevcut değil.

Geçici bir çözüm olarak, getirilen tüm verilerinizi görünüm modelinde depolayabilir ve parçayı yeniden oluşturduğunuzda bu verileri kolayca kullanılabilir hale getirebilirsiniz. Görünümü etkinlikler bağlamını kullanarak aldığınızdan emin olun.

Veri yaşam döngüsüne duyarlı gözlemlenebilir kılmak için LiveData kullanabilirsiniz


1
Bu gerçekten doğrudur ve veri> görünümü ancak sorun, görünüm durumunu da korumak istediğinizde ortaya çıkar, örneğin, birden çok sayfa yüklendi ve kullanıcı aşağı kaydırdı :).
Joaquim Ley

1

@STAR_ZERO tarafından sağlanan bağlantıyı kullandım ve iyi çalışıyor. Geri düğmesiyle sorun yaşayanlar için, bunu etkinlik / gezinme ana bilgisayarında bu şekilde halledebilirsiniz.

override fun onBackPressed() {
        if(navController.currentDestination!!.id!=R.id.homeFragment){
            navController.navigate(R.id.homeFragment)
        }else{
            super.onBackPressed()
        }
    }

Geçerli hedefin kök / ana bölüm (normalde alt gezinme görünümünde ilk olan) olup olmadığını kontrol edin, değilse, parçaya geri gidin, evet ise, yalnızca uygulamadan çıkın veya istediğinizi yapın.

Btw, bu çözümün, keep_state_fragment kullanarak STAR_ZERO tarafından sağlanan yukarıdaki çözüm bağlantısı ile birlikte çalışması gerekir .


idk neden ama güncelHedef !!. id her 3 fragman için bana hep aynı değerleri veriyor
Avishek Das

1

@ Piotr-prus tarafından sağlanan çözüm bana yardımcı oldu, ancak bazı güncel hedef kontrolü eklemem gerekiyordu:

if (navController.currentDestination?.id == resId) {
    return       //do not navigate
}

bu kontrol olmadan, eğer yanlışlıkla ona giderseniz mevcut hedef yeniden oluşturulacaktır, çünkü arka yığında bulunmayacaktır.


Çözümümün sizin için işe yaramasına sevindim. Aslında backStack kullanarak parçayı çağırmadan önce bottomNavigationView'daki geçerli menuItem'i kontrol ediyorum: bottomNavigationView.setOnNavigationItemSelectedListener
Piotr Prus

0

Android Belgelerine göre ,

FragmentContainerView kullanarak NavHostFragment'ı oluştururken veya NavHostFragment'ı bir FragmentTransaction yoluyla aktivitenize manuel olarak ekliyorsanız, NavController'ı Navigasyon.findNavController (Activity, @IdRes int) yoluyla bir Activity'nin onCreate () 'sinde almaya çalışmak başarısız olur. Bunun yerine NavController'ı doğrudan NavHostFragment'dan almalısınız.

Nav_host_fragment öğenizi -> FragmentContainerView olarak değiştirin ve navController'ı geri alın

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

Bu yaklaşımı kullanarak parça durumunu Navigasyon Bileşenleri ile koruyabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.