使用导航抽屉旋转时片段发生变化
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?
此外,让我们提一下 onRestoreInstanceState
,Fragment
没有那个 method.So,你应该使用 onActivityCreated
,它接收 bundle
使用保存的实例状态(或 null)。
看看文档:
我在这个帖子里回答了同样的问题:
我试图解释为什么我提供的解决方案有效,所以如果您有兴趣,请查看。
为了解决这个问题,我只是将膨胀初始片段的代码放在 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 帮助我们保存数据,它对表单也非常有用。
- 创建一个名为
MainActivityViewModel.kt
的 class
把这段代码放在里面:
class MainActivityViewModel : ViewModel() {
var 菜单项:Int = R.id.nav_inicio
}
我选择 R.id.nav_inicio
作为默认菜单项,根据您的 activity_main_drawer.xml(android 工作室的默认名称)修改它
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. 最后,每次 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。请按照以下步骤操作:
将此添加到您的 build.gradle(应用程序模块)
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
将此添加到您的 build.gradle(项目模块)
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
}
使用下面的代码创建一个新的MainActivityViewModel.ktclass
class MainActivityViewModel : ViewModel() {
var menuItem: Int = R.id.nav_home
}
在您的 MainActivity.java 中,在 class
的顶部添加以下内容
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
}
最后,我们必须在每次 select 一个新菜单项时更新 viewModel 值,所以
修改覆盖方法 onNavigationItemSelected 在开头添加:
// Handle navigation view item clicks here.
vm.setMenuItem(item.getItemId());
当前情景
在我的应用程序中,我有带片段的导航抽屉。在纵向模式下一切正常。
问题
假设在纵向模式下我 select 导航抽屉中的第二项。它加载完美,但是当我将 phone 旋转到横向模式时,导航菜单中的第一个片段被加载而不是第二个片段。
我知道我必须为片段保存实例,但我不知道该怎么做,我应该在主 activity 还是在片段本身
你应该在 Fragment
中这样做。
只需点击以下链接:
Android - save/restore fragment state
或者也许:
Once for all, how to correctly save instance state of Fragments in back stack?
此外,让我们提一下 onRestoreInstanceState
,Fragment
没有那个 method.So,你应该使用 onActivityCreated
,它接收 bundle
使用保存的实例状态(或 null)。
看看文档:
我在这个帖子里回答了同样的问题:
我试图解释为什么我提供的解决方案有效,所以如果您有兴趣,请查看。
为了解决这个问题,我只是将膨胀初始片段的代码放在 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 帮助我们保存数据,它对表单也非常有用。
- 创建一个名为
MainActivityViewModel.kt
的 class
把这段代码放在里面:
class MainActivityViewModel : ViewModel() { var 菜单项:Int = R.id.nav_inicio }
我选择 R.id.nav_inicio
作为默认菜单项,根据您的 activity_main_drawer.xml(android 工作室的默认名称)修改它
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. 最后,每次 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。请按照以下步骤操作:
将此添加到您的 build.gradle(应用程序模块)
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
将此添加到您的 build.gradle(项目模块)
dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" }
使用下面的代码创建一个新的MainActivityViewModel.ktclass
class MainActivityViewModel : ViewModel() { var menuItem: Int = R.id.nav_home }
在您的 MainActivity.java 中,在 class
的顶部添加以下内容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 }
最后,我们必须在每次 select 一个新菜单项时更新 viewModel 值,所以 修改覆盖方法 onNavigationItemSelected 在开头添加:
// Handle navigation view item clicks here. vm.setMenuItem(item.getItemId());