[英]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.