使用导航抽屉旋转时的片段更改

Posted

技术标签:

【中文标题】使用导航抽屉旋转时的片段更改【英文标题】:Fragment changes on rotation with navigation drawer 【发布时间】:2016-04-30 22:50:55 【问题描述】:

当前情景

在我的应用程序中,我有带有片段的导航抽屉。在纵向模式下一切正常。

问题

假设在纵向模式下,我从导航抽屉中选择了第二个项目。它加载完美,但是当我将手机旋转到横向模式时,导航菜单中的第一个片段被加载而不是第二个。

我知道我必须为片段保存实例,但我不知道该怎么做,我应该在主要活动中还是片段本身中这样做

【问题讨论】:

【参考方案1】:

您应该在 Fragment 中这样做。

只需点击以下链接:

android - save/restore fragment state

或许:

Once for all, how to correctly save instance state of Fragments in back stack?

另外,让我们提一下onRestoreInstanceStateFragment 没有那个方法。所以,你应该使用onActivityCreated 接收带有保存实例状态(或空)的bundle

查看文档:

http://developer.android.com/reference/android/app/Fragment.html#onActivityCreated(android.os.Bundle)

【讨论】:

我已经在 onActivityCreated 中使用了 setRetainState。还需要什么?? 查看此链接:***.com/questions/11182180/… 感谢您指向链接,我的代码经过了某些修改。我正在传递硬编码的 int 以在主要活动中显示片段方法。所以在保存实例状态下,我只是保存了位置,然后在 onCreate 中加载了该位置的片段【参考方案2】:

我在这个帖子中回答了同样的问题:

How to keep the same fragment when activity restarts due to orientation change in a Navigation Drawer Activity

我试图解释为什么我提供的解决方案有效,所以如果您有兴趣,请查看。

为了解决这个问题,我只是将膨胀初始片段的代码放在一个 if 中(在导航抽屉活动的 OnCreate 中):

super.onCreate(savedInstanceState);
if(savedInstanceState==null)
    FragmentManager fM = getSupportFragmentManager();
    fM.beginTransaction().replace(R.id.NavDrawContent,new home_fragment()).commit();

这样当我们在第二个片段中改变方向时它不会膨胀第一个片段

【讨论】:

【参考方案3】:

我使用名为 ViewModel 类的新工具找到了更好的解决方案。当屏幕旋转时,它会自动销毁 de Activity 并重新创建它,因此我们失去了最后选择的 Fragment 的焦点。 ViewModel 帮助我们保存数据,它对表单也非常有用。

    创建一个名为MainActivityViewModel.kt的类

    把这段代码放进去:

    类 MainActivityViewModel : ViewModel() var menuItem : Int = R.id.nav_inicio

我选择 R.id.nav_inicio 作为默认菜单项,根据您的 activity_main_drawer.xml 进行修改(android studio 的默认名称) 3. 然后,使用抽屉式导航将其声明到您的 MainActivity 中:

lateinit var vm : MainActivityViewModel

    将此行添加到您的 onCreate 覆盖方法中:vm = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)

    然后创建一个加载默认Fragment的函数:

    fun loadDefaultFragment() when (vm.menuItem) R.id.nav_inicio -> fab.hide() val fragment = InicioFragment() val manager = supportFragmentManager manager.beginTransaction().replace(R.id.content_main, fragment, fragment.getTag()).commit() R.id.nav_puntosventa -> fab.show() val fragment = ListaPuntosFragment() val manager = supportFragmentManager manager.beginTransaction().replace(R.id.content_main, fragment, fragment.getTag()).commit() else -> // default fragment if you consider necessary

这个函数必须在 ViewModel 初始化之后调用到你的 onCreate 方法中。 6、最后,每次选择新的菜单项都要更新viewModel的值,所以修改override函数onNavigationItemSelected在开头添加:override fun onNavigationItemSelected(item: MenuItem): Boolean // Handle navigation view item clicks here. vm.menuItem = item.itemId // rest of the code ... 现在,当您旋转屏幕时,de viewmodel 值会保存位置,您可以将它与表单一起使用。

【讨论】:

【参考方案4】:

更改配置后导航控制器回栈出现问题

在带有片段的 Activity 中:尝试在更改配置之前保存导航控制器状态。更改配置后,再次将其添加回导航控制器。喜欢:

override fun onSaveInstanceState(savedInstanceState: Bundle) 
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putBundle("nav_state", fragment.findNavController().saveState())

override fun onRestoreInstanceState(savedInstanceState: Bundle) 
super.onRestoreInstanceState(savedInstanceState)
fragment.findNavController().restoreState(savedInstanceState.getBundle("nav_state"))

这就是我的情况

【讨论】:

【参考方案5】:

新的 Android 架构组件引入了一个 ViewModel 类,它有助于创建生命周期感知组件。它解决了大部分关于生命周期管理的问题。如果您在项目中使用 Java 代码,则可以引入 ViewModel 类。请按以下步骤操作:

    将此添加到您的 build.gradle(app module)

    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    

    将此添加到您的 build.gradle(project module)

    dependencies 
    
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
    
    

    使用以下代码创建一个新的 MainActivityViewModel.kt

    class MainActivityViewModel : ViewModel() 
    var menuItem: Int = R.id.nav_home
    
    

    在您的 MainActivity.java 中,在类的顶部添加以下内容

    private MainActivityViewModel vm;
    

    在你的 MainActivity.java onCreate 方法中

    vm = new ViewModelProvider(this).get(MainActivityViewModel.class);
    loadDefaultFragment();
    

    现在在 MainActivity.java 中创建一个方法 loadFragment() 使用下面的代码

    private void loadFragment(Fragment fragment) 
    // load fragment
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.contentMainLayout, fragment);
    transaction.addToBackStack(null);
    transaction.commit();
    
    

    在您的 MainActivity.java 中创建另一个方法 loadDefaultFragment()

    private void loadDefaultFragment()
    if(vm.getMenuItem()==R.id.nav_home)
        fragment = new HomeFragment();
        loadFragment(fragment);
    else if(vm.getMenuItem()==R.id.nav_another_fragment)
        fragment = new AnotherFragment();
        loadFragment(fragment);
    
    //you can add or exclude other  fragments here depending on your requirements
    
    

    最后,我们必须在每次选择新菜单项时更新 viewModel 值,所以 修改覆盖方法onNavigationItemSelected在开头添加:

    // Handle navigation view item clicks here.
    vm.setMenuItem(item.getItemId());
    

【讨论】:

以上是关于使用导航抽屉旋转时的片段更改的主要内容,如果未能解决你的问题,请参考以下文章

Android - 如何更改导航抽屉中的片段

从其他片段(如导航链接)更改片段

导航抽屉的片段管理

如何使用导航抽屉打开和关闭更改按钮图像

将一个片段调用到另一个片段时更改标题TextView

导航组件替换/更改后台堆栈