在底部导航栏中保存片段状态

Posted

技术标签:

【中文标题】在底部导航栏中保存片段状态【英文标题】:save fragment state in a bottom navigation bar 【发布时间】:2021-08-05 00:17:36 【问题描述】:

我有一个活动,它基本上是一个包含片段的导航底部栏。如果用户已登录,底部导航显示 4 个片段:市场、收藏、上传和个人资料。如果用户是访客,则有两个片段:市场和登录。

以前,每次有人从一个片段交换到另一个片段时,它都会再次生成。但是,我想保存片段的状态,所以如果有人在市场上应用了过滤器并回来,它必须保持过滤器的应用。我尝试实现这个解决方案https://medium.com/@oluwabukunmi.aluko/bottom-navigation-view-with-fragments-a074bfd08711,但我觉得目前还不是最好的。

导航活动的代码是:

public class NavigationActivity extends AppCompatActivity 

    final Fragment market= new MarketFragment();
    final Fragment favorite = new FavoriteFragment();
    final Fragment updateProduct = new AddProductFragment();
    final Fragment userProfile = new UserProfileFragment();
    final Fragment loginregister = new LoginOrRegisterFragment();
    final FragmentManager fm = getSupportFragmentManager();
    Fragment active = market;

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        if(UnipopApp.usuariLoggejat.getUserLogged() != null)
            setContentView(R.layout.main);
        
        else
            setContentView(R.layout.main_guest);
        
        setUpNavigation();
    

    public void setUpNavigation() 
        BottomNavigationView bottomNavigationView;
        if(UnipopApp.usuariLoggejat.getUserLogged() != null) 
            bottomNavigationView = findViewById(R.id.bottom_navigation);
            bottomNavigationView.setOnNavigationItemSelectedListener(mOnNavegationItemSelectedListener);
            fm.beginTransaction().add(R.id.nav_host_fragment, favorite, "2").hide(favorite).commit();
            fm.beginTransaction().add(R.id.nav_host_fragment, updateProduct, "3").hide(updateProduct).commit();
            fm.beginTransaction().add(R.id.nav_host_fragment, userProfile, "5").hide(userProfile).commit();
            fm.beginTransaction().add(R.id.nav_host_fragment, market, "1").commit();
        
        else 
            bottomNavigationView = findViewById(R.id.bottom_navigation_guest);
            bottomNavigationView.setOnNavigationItemSelectedListener(mOnNavegationItemSelectedListener);
            fm.beginTransaction().add(R.id.nav_host_fragment_guest, loginregister, "6").hide(loginregister).commit();
            fm.beginTransaction().add(R.id.nav_host_fragment_guest, market, "1").commit();
        
    

    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavegationItemSelectedListener
        = new BottomNavigationView.OnNavigationItemSelectedListener() 
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) 
            switch (item.getItemId())
                case R.id.action_home:
                    fm.beginTransaction().hide(active).show(market).commit();
                    active = market;
                    return true;
                case R.id.action_favorites:
                    fm.beginTransaction().hide(active).show(favorite).commit();
                    active = favorite;
                    return true;
                case R.id.addProduct_fragment:
                    fm.beginTransaction().hide(active).show(updateProduct).commit();
                    active = updateProduct;
                    return true;
                case R.id.action_user_profile:
                    fm.beginTransaction().hide(active).show(userProfile).commit();
                    active = userProfile;
                    return true;
                case R.id.action_user_profile_guest:
                    fm.beginTransaction().hide(active).show(loginregister).commit();
                    active = loginregister;
                    return true;
            
            return false;
        
    ;

它有效,但我不得不删除导航图......但是,问题是当有人将产品标记为市场上的最爱并移动到最喜欢的片段时,它不会出现。显然,它只是再次显示片段,而不是通过来自服务器的调用返回片段。

我正在寻找其他选择,因为我不喜欢这种解决方案。如果有人能帮我一把,那就太好了。

谢谢

【问题讨论】:

您使用的解决方案非常消耗内存并且不可扩展。我希望您必须使用 shareviewmodel 并将您的状态保存到其中。您应该结合使用片段 onSavedInstance() 和共享视图模型。当您切换回底部导航栏中的片段时,视图模型将传回非配置数据,例如最后过滤的结果列表。此外,您可能需要做一些小的 UI 更新,例如显示过滤器计数、通过从保存的实例中检索这些状态数据来平滑滚动到最后滚动的位置 您有任何示例代码吗? 【参考方案1】:

使用底部导航视图和片段导航的推荐解决方案。使用Activity's SharedViewModel 来持久化所有导航片段的状态。

布局 XML (rees/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_
android:layout_
tools:context=".MainActivity">

<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_navigation"
    android:layout_
    android:layout_
    app:layout_constraintBottom_toBottomOf="parent"
    app:labelVisibilityMode="labeled"
    app:menu="@menu/bottom_navigation_menu" />

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_
    android:layout_
    app:defaultNavHost="true"
    app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:navGraph="@navigation/core_navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

导航 XML (res/navigation/core_navigation.xml)

<?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"
app:startDestination="@+id/navigation_feed">

<fragment
    android:id="@+id/navigation_home"
    android:name="com.example.HomeFragment"
    android:label="@string/home_title"
    tools:layout="@layout/home_fragment" />

<fragment
    android:id="@+id/navigation_favorites"
    android:name="com.example.FavoritesFragment"
    android:label="@string/favorites_title"
    tools:layout="@layout/favorites_fragment" />

<!--Fill this with other fragments like above-->

</navigation>

菜单 XML (res/menu/bottom_navigation_menu.xml)

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:id="@+id/navigation_home"
    android:enabled="true"
    android:icon="@drawable/ic_home"
    android:title="@string/home_title"/>
<item
    android:id="@+id/navigation_favorites"
    android:enabled="true"
    android:icon="@drawable/ic_favorites"
    android:title="@string/favorites_title"/>

<!--Fill this with other fragments like above-->

</menu>

活动

class MainActivity : AppCompatActivity() 

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupNavigation()
    

    private fun setupNavigation() 
        val navController = (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController
        binding.bottomNavigation.setupWithNavController(navController)
    

【讨论】:

这就是我之前的情况,但是如果你从一个片段交换到另一个片段,想象你在产品的 RV 中,在产品 60 中,我希望用户在他来时继续看到产品 60回到片段。 @LauraGalera 您可以通过回收器视图侦听器跟踪视图模型中的滚动 XY 坐标,以便在用户返回时重新设置它。【参考方案2】:

您可以在片段的视图模型中保存过滤器选择。

【讨论】:

以上是关于在底部导航栏中保存片段状态的主要内容,如果未能解决你的问题,请参考以下文章

如果在底部导航栏中选择了其他项目,如何删除 floatingActionButton 替换片段

如何保存底部导航片段的状态 - 具有单个导航图的 Android 导航组件

Google 相册应用底部导航栏行为

使用底部导航栏颤动保存 PageView 内每个页面的状态

如何在底部导航栏中删除标题标题 - Android Studio

使用底部导航栏防止片段刷新