使用导航抽屉旋转时片段发生变化

Fragment changes on rotation with navigation drawer

当前情景

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

问题

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

我知道我必须为片段保存实例,但我不知道该怎么做,我应该在主 activity 还是在片段本身

你应该在 Fragment 中这样做。

只需点击以下链接:

Android - save/restore fragment state

或者也许:

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

此外,让我们提一下 onRestoreInstanceStateFragment 没有那个 method.So,你应该使用 onActivityCreated,它接收 bundle使用保存的实例状态(或 null)。

看看文档:

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

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

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

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

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

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

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

  1. 创建一个名为 MainActivityViewModel.kt
  2. 的 class
  3. 把这段代码放在里面:

    class MainActivityViewModel : ViewModel() { var 菜单项:Int = R.id.nav_inicio }


我选择 R.id.nav_inicio 作为默认菜单项,根据您的 activity_main_drawer.xml(android 工作室的默认名称)修改它

3. 然后,用导航抽屉将其声明到您的 MainActivity 中:

lateinit var vm : MainActivityViewModel

  1. 将此行添加到您的 onCreate 覆盖方法中:

    vm = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)

  2. 然后创建一个加载默认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. 最后,每次 select 一个新的菜单项时,我们都必须更新 viewModel 值,因此修改覆盖函数 onNavigationItemSelected 在开头添加它:

override fun onNavigationItemSelected(item: MenuItem): Boolean { // Handle navigation view item clicks here. vm.menuItem = item.itemId // rest of the code ... }

现在,当您旋转屏幕时,de viewmodel 值会保存位置,您也许可以将其用于表单。

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

在您的 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"))}

这就是我的情况

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

  1. 将此添加到您的 build.gradle(应用程序模块)

    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    
  2. 将此添加到您的 build.gradle(项目模块)

    dependencies {
    
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
    }
    
  3. 使用下面的代码创建一个新的MainActivityViewModel.ktclass

    class MainActivityViewModel : ViewModel() {
    var menuItem: Int = R.id.nav_home
    }
    
  4. 在您的 MainActivity.java 中,在 class

    的顶部添加以下内容
    private MainActivityViewModel vm;
    

    并在您的 MainActivity.java onCreate 方法中

    vm = new ViewModelProvider(this).get(MainActivityViewModel.class);
    loadDefaultFragment();
    
  5. 现在使用下面的代码在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();
    }
    
  6. 在您的 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
    }
    
  7. 最后,我们必须在每次 select 一个新菜单项时更新 viewModel 值,所以 修改覆盖方法 onNavigationItemSelected 在开头添加:

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