使用导航抽屉旋转时的片段更改
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?
另外,让我们提一下onRestoreInstanceState
,Fragment
没有那个方法。所以,你应该使用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());
【讨论】:
以上是关于使用导航抽屉旋转时的片段更改的主要内容,如果未能解决你的问题,请参考以下文章