起因是因為TabActivity被Google死亡宣告(deprecated)。
加上客戶的無理要求「在所有的頁面都要有側滑頁」。
首先使用FragmentTabHost,因為跟之前的TabHost的程式碼接近,稍作修改就可以實做出5個Fragment。
但是tab1->tab1-2->tab2->back鍵->回到tab1,發現還是停留在tab1-2並不會回去tab1。很明顯的是FragmentManager的backstack沒辦法處理。
只好硬著頭皮再找其他方案。簡單搜尋了一下,發現android OS 4.2之後出現getChildFragmentManager(),
似乎是可以做到讓fragment裡面還可以有自已的History。於是朝這個方向嘗試。
找到了一篇介紹這類的方法的網頁,裡面使用tabhost跟ontabchanged listener去處理。 本篇就是建構此文之上去做的改善。
設計概念:
- 使用一個FragmenActivity。加上FragmentTabHost讓此Activity可以使用tab 來做 fragment的切換。
在FragmenActivity上面建立N個tab在android.R.id.tabcontent上面操作的fragment。 - 這N個fragment都使用一個設計好的空fragment class建立。
並在此空的N個fragment上面,利用android os 4.2版之後新增的getChildFragmentManager()函式,可以分別取得此N個空fragment的FragmentManager,fragment Transaction history功能即可依tab來分別。
(空fragment class的特性:layout xml只有一個給ChildFragmentManager利用的framelayout,另外加上使用ChildFragmentManager來做回上一層fragment的功能給FragmenActivity取用。) - 各個空fragment直接進行第一次的fragment Transaction。如此就不會出現空白畫面。
要繼續往下進行fragment Transaction的話,使用getSupportFragmentManager()就可以拿到該層的FragmentManager。(也就是空fragment class的FragmentManager。)
注意下面幾點:
- 此文裡面浪費了一個framelayout給Tabhost使用,本方法改善此設計。
- 截自android support library 20版為止,android.support.v13.app的fragment不支援getChildFragmentManager()。
- 有在fragment的onCreateView()保存之前已經inflate好的fragment layout的習慣的話,記得一定要在第二次執行oncreateview()之後對parentview去做clearallview()的動作。(因為原本的layout已經被attach到fragment container之後,執行fragment的re-attach/replace等等動作的時候會因為原本的view已經被attach,有parent了,導致Exception發生。)或是改成每次onCreateView()都重新inflate layout。
這個問題卡了我近一天... 因為Exception完全沒有提示正確的問題點... - getChildFragmentManager(),操作的fragment container為此fragment inflate的layout裡面存在的某一個layout。(不是getSupportFragmentManager()的那個)
- 假如你還想在這樣的架構之下加用ViewPager + FragmentPagerAdapter的話,請使用「FragmentStatePagerAdapter」。它會幫你處理切換tab時所需要處理的fragment load/unload 問題。
系統架構:
public class MainActivity extends FragmentActivity implements View.OnClickListener, TabHost.OnTabChangeListener
{
public static final String ROOT_FRAGMENT_ARGS = "tabrootFragment";
private boolean bBackPressed = false;
private FragmentTabHost tabHost;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
tbWidget = (TabWidget)findViewById(android.R.id.tabs);
tabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
tabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
tabHost.setOnTabChangedListener(this);
addTab("tab1", "Tab1", Fragment1.class);
addTab("tab2", "Tab2", Fragment1.class);
addTab("tab3", "Tab3", Fragment1.class);
}
private void addTab(String tabId, String indicactor, Class fragmentClass, Bundle args)
{
if (args == null)
args = new Bundle();
args.putString(ROOT_FRAGMENT_ARGS, fragmentClass.getName());
tabHost.addTab(tabHost.newTabSpec(tabId).setIndicator(indicactor), EmptyFragment.class, args);
}
}
public EmptyFragment getCurrentFragment() {
return (EmptyFragment) getSupportFragmentManager().findFragmentById(android.R.id.tabcontent);
}
@Override
public void onBackPressed()
{
EmptyFragment rootFragment = getCurrentFragment();
if (rootFragment == null || !rootFragment.popBackStack())
{
if (bBackPressed)
{
finish();
}
else
{
bBackPressed = true;
mHandler.postDelayed(new Runnable(){
public void run() {
bBackPressed = false;
}
}, 3000);
Utils.showToast(this, "Press Back twice to exit app");
}
}
}
EmptyFragment:
public class EmptyFragment extends Fragment
{
private View contentView = null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (contentView == null)
contentView = inflater.inflate(R.layout.emptyfragment, container, false);
else
((FrameLayout)contentView.getParent()).removeAllViews(); //prevent fragment history return child attached exception.
return contentView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initFragment();
}
/**
* Pop fragment backstack.
* @return return true if exists backstack
*/
public boolean popBackStack() {
return getChildFragmentManager().popBackStackImmediate();
}
/**
* Initialize Fragment.
*/
private void initFragment() {
Bundle args = getArguments();
String rootClass = args.getString(MainActivity.ROOT_FRAGMENT_ARGS);
FragmentManager fragmentManager = getChildFragmentManager();
if (fragmentManager.findFragmentById(R.id.fragment) != null) {
return;
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = Fragment.instantiate(getActivity(), rootClass, args);
fragmentTransaction.replace(R.id.fragment, fragment);
fragmentTransaction.commit();
}
/**
* Return Fragment in displayed
* @return Fragment
*/
Fragment getCurrentFragment() {
return getChildFragmentManager().findFragmentById(R.id.fragment);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Call child fragment method
getCurrentFragment().onActivityResult(requestCode, resultCode, data);
}
}
R.layout.mainlayout:
R.layout.emptyfragment:
