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

当前情景

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

问题

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

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

【问题讨论】:

    标签: android android-fragments navigation-drawer screen-rotation


    【解决方案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。还需要什么??
    • 感谢您指向链接,我的代码经过了某些修改。我正在传递硬编码的 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 帮助我们保存数据,它对表单也非常有用。

      1. 创建一个名为MainActivityViewModel.kt的类
      2. 把这段代码放进去:

        类 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

      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、最后,每次选择新的菜单项都要更新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 类。请按以下步骤操作:

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

            implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
            
          2. 将此添加到您的 build.gradle(project module)

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

            class MainActivityViewModel : ViewModel() {
            var menuItem: Int = R.id.nav_home
            }
            
          4. 在您的 MainActivity.java 中,在类的顶部添加以下内容

            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. 最后,我们必须在每次选择新菜单项时更新 viewModel 值,所以 修改覆盖方法onNavigationItemSelected在开头添加:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多