問題分析
一直在簡書里看別人的技術(shù)貼,今天我也來寫點自己的心得!最近在寫一個項目用到大量的Fragment后的總結(jié)!
我想剛剛接觸安卓的同學(xué)或許會這么寫:
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.add(ViewId,fragment);// 或者fragmentTransaction.replace(ViewId,fragment);
fragmentTransaction.commit();
基礎(chǔ)更好一點的同學(xué)會用show和hide方法
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.hide(new FirstFragment())
.show(new SecondFragment())
.commit();
誠然這兩種都可以切換Fragment,但是面對用戶大量點擊來回切換,或者你的Fragment本來就很多,每次都這樣操作,那么很快你的應(yīng)用就會OOM,就算不崩那也會異常的卡頓!so why?
當我們replace時發(fā)生了以下的生命周期:

想想看每次都replace一下??!這世界會有多美好!?。∧敲磫栴}出在哪?回過頭看看代碼就會發(fā)現(xiàn)每次在add/replace或者show/hide都會new 一個新的實例,這就是致命原因?。。。。?/p>
廢話不多說,亮出我的方法(抽取后的):
/**
* Fragment的添加
* @param manager Fragment管理器
* @param aClass 相應(yīng)的Fragment對象的getClass
* @param containerId 容器的id
* @param args 需要傳值的話可將bundle填入 不需要傳值就填null
*/
protected void addFragment(FragmentManager manager, Class<? extends BaseFragment> aClass, int containerId, Bundle args) {
String tag = aClass.getName();
Logger.d("%s add fragment %s", TAG, aClass.getSimpleName());
Fragment fragment = manager.findFragmentByTag(tag);
FragmentTransaction transaction = manager.beginTransaction(); // 開啟一個事務(wù)
if (fragment == null) {// 沒有添加
try {
fragment = aClass.newInstance(); // 通過反射 new 出一個 fragment 的實例
BaseFragment baseFragment = (BaseFragment) fragment; // 強轉(zhuǎn)成我們base fragment
// 設(shè)置 fragment 進入,退出, 彈進,彈出的動畫
transaction.setCustomAnimations(baseFragment.enter(), baseFragment.exit(), baseFragment.popEnter(), baseFragment.popExit());
transaction.add(containerId, fragment, tag);
if (baseFragment.isNeedToAddBackStack()) { // 判斷是否需要加入回退棧
transaction.addToBackStack(tag); // 加入回退棧時制定一個tag,以便在找到指定的事務(wù)
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
if (fragment.isAdded()) {
if (fragment.isHidden()) {
transaction.show(fragment);
}
} else {
transaction.add(containerId, fragment, tag);
}
}
if (fragment != null) {
fragment.setArguments(args);
hideBeforeFragment(manager, transaction, fragment);
transaction.commit();
}
}
/**
* 除當前 fragment 以外的所有 fragment 進行隱藏
*
* @param manager
* @param transaction
* @param currentFragment
*/
private void hideBeforeFragment(FragmentManager manager, FragmentTransaction transaction, Fragment currentFragment) {
List<Fragment> fragments = manager.getFragments();
for (Fragment fragment : fragments) {
if (fragment != currentFragment && !fragment.isHidden()) {
transaction.hide(fragment);
}
}
}
略微解釋一下:
先查詢fragmentManager 所在的activitiy 中是否已經(jīng)添加了這個fragment
第一步 先從一個mAdded 的一個ArrayList遍歷查找,如果找不到再從 一個 叫 mActive 的 SparseArray的一個map里面查找。
注意:
1.一個 fragment 被 remove 掉后,只會從 mAdded 里面刪除,不會從 mActive 里面刪除,只有當這個fragment 所在的 transaction 從回退棧里面移除后才會 從mActive 刪除
- 當我們add 一個fragment時 會把我們的fragment 添加到 mAdded 里面,不會添加到 mActive。
- 只有當我們把 transaction 添加到回退棧的時候,才會把我們的 fragment 添加到 mActive 里面。所以我們通過 findFragmentByTag 方法查找出來的 fragment 不一定是被添加到我們的 activity 中。
使用:
代碼比較多,但是我個人感覺使用起來比較方便,而且功能也比較完善,使用的時候只需要兩行代碼:
HomeFragment1 homeFragment = new HomeFragment1();
addFragment(getSupportFragmentManager(),homeFragment.getClass(),R.id.main_body,null);
當我們需要傳值的時候,只需要將準備好的bundle以參數(shù)的形式填入即可。
我還增加了一個是否加入回退棧的判斷,用于實現(xiàn)一些有關(guān)回退棧的需求,實現(xiàn)這個功能還需要在BaseFragment中定義一個方法:
protected boolean isNeedToAddBackStack() {
return true;
}