[英]Multiple Fragments on Orientation Changes
我在Android上遇到了一个烦人的任务,该任务是在定向更改时维护片段的状态。
首先,我在StackOver流上尝试了所有解决方案,但结果只有我没有要求的好结果。
我能够在“方向”更改上保留“片段”中的更改,但是当用户切换片段时,无法保存片段状态,因此我删除了此解决方案,并开始寻找新的更好的解决方案。
我的想法是显示/隐藏片段,而不需要整体替换它们,因为它们将仅隐藏一小段时间,并且如果它们不再可见,则再次创建它们也没有问题。
少说话,多代码。
int mID = (int) drawerItem.getIdentifier();
String mTag = "";
switch (mID){
case 0:
mTag = "ViewPager";
break;
case 1:
mTag = "Browser";
break;
case 2:
mTag = "Settings";
break;
}
if (savedInstanceState == null) {
ShowHideFrags(mID);
} else {
switch (mID){
case 0:
Log.i("ASDSADSA","4");
mViewPager = (ViewPagerFragment) getSupportFragmentManager().findFragmentByTag(mTag);
ShowHideFrags(mID);
break;
case 1:
Log.i("ASDSADSA","5");
mWebFrag = (WVFragment) getSupportFragmentManager().findFragmentByTag(mTag);
ShowHideFrags(mID);
break;
case 2:
Log.i("ASDSADSA","6");
mSettings = (SettingsFragment) getSupportFragmentManager().findFragmentByTag(mTag);
ShowHideFrags(mID);
break;
}
}
该代码处理“导航”抽屉中的单击,我正在使用Mikepenz
“材料抽屉”库。
如代码所示,我有三个片段,分别是ViewPager
, Browser
和Settings
。
我的问题是,重新创建它,没有方向和getFragment
方法,代码中没有问题,但是当我添加对此更改的支持时,将重新创建该片段。 我已经尝试了很多次来更改代码,记录更改并查看问题所在。
在第一次启动时,它将调用ShowHideFrags(int x)
方法,然后转到else
。
ShowHideFrags(int x)的代码:
private void ShowHideFrags(int SelectedFrag){
if(mFragmentManager == null)
mFragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft = mFragmentManager.beginTransaction();
switch (SelectedFrag){
case 0:
if(mViewPager == null)
mViewPager = new ViewPagerFragment();
if(!mViewPager.isAdded())
ft.add(R.id.fragment,mViewPager,"Viewpager");
if(!mViewPager.isVisible()){
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
ft.show(mViewPager);
} else {
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
}
ft.commit();
break;
case 1:
if(mWebFrag == null)
mWebFrag = new WVFragment();
if(!mWebFrag.isAdded())
ft.add(R.id.fragment,mWebFrag,"Browser");
if(!mWebFrag.isVisible()) {
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if(mViewPager != null && mViewPager.isVisible())
ft.hide(mViewPager);
ft.show(mWebFrag);
} else {
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if((mViewPager != null && mViewPager.isVisible()))
ft.hide(mViewPager);
}
ft.commit();
break;
case 2:
if(mSettings == null)
mSettings = new SettingsFragment();
if(!mSettings.isAdded())
ft.add(R.id.fragment,mSettings,"Settings");
if(!mSettings.isVisible()) {
if((mViewPager != null && mViewPager.isVisible()))
ft.hide(mViewPager);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
ft.show(mSettings);
} else {
if((mViewPager != null && mViewPager.isVisible()))
ft.hide(mViewPager);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
}
ft.commit();
break;
}
}
我的onSaveInstanceState的代码:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
try{
long mSelectedItem = result.getCurrentSelection();
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.Fragment currentFragment = fragmentManager.findFragmentById(R.id.fragment);
getSupportFragmentManager().putFragment(outState,currentFragment.getTag(),currentFragment);
outState.putLong("SelectedItem",mSelectedItem);
} catch (Exception e){
e.printStackTrace();
}
}
因此,在尝试解决方案4天后,我坐下来喝了一杯不错的饮料,然后我想出了一种解决此烦人问题的方法。
因为我在解释进度或步骤方面并不出色,所以我会直截了当,并尽我所能,像专业人士回答人们一样。
首先,由于我的应用程序具有导航抽屉,并且我想显示/隐藏片段而无需重新创建它们,因此要花费很长时间来实现这样的任务是一项艰巨的任务,因此我需要了解生命周期以及发生了什么,因此我在每个步骤中都添加了Log,以了解幕后发生的事情。
当应用启动时,仅切换片段.show()
和.show()
.hide()
,捆绑将为null。 因此
if (savedInstanceState == null) {
LogMessage("Bundle Is Null, normal stuff");
ShowHideFrags(mID);
}
最后使用的方法ShowHideFrags
将被附加,它的任务是检查片段是否为空,并隐藏未被选择的片段,并显示所选的片段,如果为空,则该方法将创建一个新片段并添加它。
第一件事很容易,当方向发生时,捆绑包不会为空,它将具有已保存的片段,首先我们需要将其保存在捆绑包中。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
long mSelectedItem = result.getCurrentSelection();
try{
FragmentManager mFM = getSupportFragmentManager();
Fragment currentFragment = null;
switch (mTag){
case "ViewPager":
currentFragment = mViewPager;
break;
case "Browser":
currentFragment = mWebFrag;
break;
case "Settings":
currentFragment = mSettings;
break;
}
mFM.putFragment(outState,mTag,currentFragment);
LogMessage(String.format("The following fragment '%1$s' with position %2$s was saved",mTag,String.valueOf(mSelectedItem)));
} catch (Exception e){
e.printStackTrace();
}
outState.putLong("SelectedItem",mSelectedItem);
}
将提出一个问题,为什么不使用硬编码的字符串而不将其作为变量? 因为我很懒。 重要说明,请勿通过findFragmentByTag
方法保存当前片段,它会为您提供最后一个应用的片段,而不是选定的片段,因此,我必须手动检查是否选择了碎片。
别忘了提到片段,我在代码开头有以下片段
private ViewPagerFragment mViewPager;
private WVFragment mWebFrag;
private SettingsFragment mSettings;
mTag
是片段标签,在导航抽屉的onClick
方法中,我通过以下代码添加了mTag
值:
switch (mID) {
case 0:
mTag = "Viewpager";
break;
case 1:
mTag = "Browser";
break;
case 2:
mTag = "Settings";
break;
}
现在,我们分配了mTag,当bundle为null时,检查完成,并且bundle的保存部分也完成了,现在,当bundle不为null时,我们需要进行检查。
首先是代码,然后是解释
if(getSupportFragmentManager().findFragmentByTag(mTag) != null){
LogMessage(String.format("Fragment with tag '%1$s' was found",mTag));
mViewPager = (ViewPagerFragment) getSupportFragmentManager().findFragmentByTag(mTag);
if(!mViewPager.isVisible()) {
LogMessage("Not Visible");
ShowHideFrags(mID);
} else {
LogMessage("Visible");
}
} else {
LogMessage(String.format("Fragment with tag '%1$s' was NOT found", mTag));
if(!ReturnCurrentFragment().equalsIgnoreCase(mTag)){
LogMessage("Not the Same");
ShowHideFrags(mID);
} else {
LogMessage("It's the Same");
}
}
private Fragment ReturnCurrentFragment(){
return getSupportFragmentManager().findFragmentById(R.id.fragment);
}
当bundle不为null时,以上代码位于else子句中。 首先,我们检查选定的片段(已保存在捆绑包中)是否确实可以加载,如果可以加载,我们将其分配,如果不可见,则意味着其他片段可见,因此我们将其ShowHideFrags
。
当片段可见时,我们什么也不做。 此外,我们检查它是否不在捆绑包中,如果不存在,是否已经可以重新加载,所以我们不会两次创建它* 1。
有了这个代码,我已经几乎抓住每一个恼人的错误护理会发生的希望 。
ShowHideFrags(int x)
的代码:
private void ShowHideFrags(int SelectedFrag){
if(mFragmentManager == null)
mFragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft = mFragmentManager.beginTransaction();
switch (SelectedFrag){
case 0:
if(mViewPager == null)
mViewPager = new ViewPagerFragment();
if(!mViewPager.isAdded())
ft.add(R.id.fragment,mViewPager,"Viewpager");
if(!mViewPager.isVisible()){
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
ft.show(mViewPager);
} else {
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
}
ft.commit();
break;
case 1:
if(mWebFrag == null)
mWebFrag = new WVFragment();
if(!mWebFrag.isAdded())
ft.add(R.id.fragment,mWebFrag,"Browser");
if(!mWebFrag.isVisible()) {
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if(mViewPager != null && mViewPager.isVisible())
ft.hide(mViewPager);
ft.show(mWebFrag);
} else {
if((mSettings != null && mSettings.isVisible()))
ft.hide(mSettings);
if((mViewPager != null && mViewPager.isVisible()))
ft.hide(mViewPager);
}
ft.commit();
break;
case 2:
if(mSettings == null)
mSettings = new SettingsFragment();
if(!mSettings.isAdded())
ft.add(R.id.fragment,mSettings,"Settings");
if(!mSettings.isVisible()) {
if((mViewPager != null && mViewPager.isVisible()))
ft.hide(mViewPager);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
ft.show(mSettings);
} else {
if((mViewPager != null && mViewPager.isVisible()))
ft.hide(mViewPager);
if((mWebFrag != null && mWebFrag.isVisible()))
ft.hide(mWebFrag);
}
ft.commit();
break;
}
}
* 1:例如,让我们删除该代码块,当该片段不存在且该块也不存在时,当您尝试切换时,所选片段可能会覆盖在前一个片段上,或者可能显示空白片段,因此如果它等于当前片段,则需要在其中显示ShowHideFrags,因此我们无需再次重新创建它,并使两个相同的片段彼此重叠。
希望此答案对您有帮助,请随时提出和/或更正此答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.