1、Fragment全解析系列(一):那些年踩過的坑
本篇主要介紹一些最常見的Fragment的坑以及官方Fragment庫的那些自身的BUG,並給出解決方案;這些BUG在你深度使用時會遇到,比如Fragment嵌套時或者單Activity+多Fragment架構時遇到的坑。
簡陋的目錄
1、getActivity()空指針
2、異常:Can not perform this action after onSaveInstanceState
3、Fragment重疊異常—–正確使用hide、show的姿勢
4、Fragment嵌套的那些坑
5、未必靠譜的出棧方法remove()
6、多個Fragment同時出棧的深坑BUG
7、深坑 Fragment轉場動畫
開始之前
最新版知乎,單Activity多Fragment的架構,響應可以說非常“絲滑”,非要說缺點的話,就是沒有轉場動畫,並且轉場會有類似閃屏現象。我猜測可能和Fragment轉場動畫的一些BUG有關。(這系列的最後一篇文章我會給出我的解決方案,可以自定義轉場動畫,並能在各種特殊情況下正常運行。)
但是!Fragment相比較Activity要難用很多,在多Fragment以及嵌套Fragment的情況下更是如此。
更重要的是Fragment的坑真的太多了,看Square公司的這篇文章吧,Square:從今天開始拋棄Fragment吧!
當然,不能說不再用Fragment,Fragment的這些坑都是有解決辦法的,官方也在逐步修復一些BUG。
下面羅列一些,有常見的,也有極度隱蔽的一些坑,也是我在用單Activity多Fragment時遇到的坑,可能有更多坑可以挖掘…
在這之前爲了方便後面文章的介紹,先規定一個“術語”,安卓app有一種特殊情況,就是 app運行在後臺的時候,系統資源緊張的時候導致把app的資源全部回收(殺死app的進程),這時把app再從後臺返回到前臺時,app會重啓。這種情況下文簡稱爲:“內存重啓”。(屏幕旋轉等配置變化也會造成當前Activity重啓,本質與“內存重啓”類似)
在系統要把app回收之前,系統會把Activity的狀態保存下來,Activity的FragmentManager負責把Activity中的Fragment保存起來。在“內存重啓”後,Activity的恢復是從棧頂逐步恢復,Fragment會在宿主Activity的onCreate
方法調用後緊接着恢復(從onAttach
生命週期開始)。
getActivity()空指針
可能你遇到過getActivity()返回null,或者平時運行完好的代碼,在“內存重啓”之後,調用getActivity()的地方卻返回null,報了空指針異常。
大多數情況下的原因:你在調用了getActivity()時,當前的Fragment已經onDetach()
了宿主Activity。
比如:你在pop了Fragment之後,該Fragment的異步任務仍然在執行,並且在執行完成後調用了getActivity()方法,這樣就會空指針。
解決辦法:
更”安全”的方法:(對於Fragment已經onDetach這種情況,我們應該避免在這之後再去調用宿主Activity對象,比如取消這些異步任務,但我們的團隊可能會有粗心大意的情況,所以下面給出的這個方案會保證安全)
在Fragment基類裏設置一個Activity mActivity的全局變量,在onAttach(Activity activity)
裏賦值,使用mActivity代替getActivity()
,保證Fragment即使在onDetach
後,仍持有Activity的引用(有引起內存泄露的風險,但是異步任務沒停止的情況下,本身就可能已內存泄漏,相比Crash,這種做法“安全”些),即:
protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
/**
* 如果你用了support 23的庫,上面的方法會提示過時,有強迫症的小夥伴,可以用下面的方法代替
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity)context;
}
異常:Can not perform this action after onSaveInstanceState
有很多小夥伴遇到這個異常,這個異常產生的原因是:
在你離開當前Activity等情況下,系統會調用onSaveInstanceState()
幫你保存當前Activity的狀態、數據等,直到再回到該Activity之前(onResume()
之前),你執行Fragment事務,就會拋出該異常!(一般是其他Activity的回調讓當前頁面執行事務的情況,會引發該問題)
解決方法:
- 1、該事務使用
commitAllowingStateLoss()
方法提交,但是有可能導致該次提交無效!(在此次離開時恰巧Activity被強殺時) - 2、在重新回到該Activity的時候(
onResumeFragments()
或onPostResume()
),再執行該事務,配合數據保存,可以做到事務的完整性,不會丟失事務。
示例代碼 (以EventBus通知執行事務爲例,其他場景思路一致):
@Override
// 如果是在Fragment內, 則複寫onResumeFragments()改爲onResume()即可
protected void onResumeFragments() {
super.onResumeFragments();
mIsSaved = true;
if (mTransactionEvent != null) {
// 這裏執行事務
mTransactionEvent = null;
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mIsSaved = false;
}
@Subscribe(sticky = true) // sticky事件可以保證即使Activity被強殺,也會在恢復後拿到數據
public void onEvent(TransactionEvent event) {
if (mIsSaved) {
// 這裏執行事務
} else {
mTransactionEvent = event;
}
}
Fragment重疊異常—–正確使用hide、show的姿勢
在類onCreate()
的方法加載Fragment,並且沒有判斷saveInstanceState==null
或if(findFragmentByTag(mFragmentTag) == null)
,導致重複加載了同一個Fragment導致重疊。(PS:replace
情況下,如果沒有加入回退棧,則不判斷也不會造成重疊,但建議還是統一判斷下)
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 在頁面重啓時,Fragment會被保存恢復,而此時再加載Fragment會重複加載,導致重疊 ;
if(saveInstanceState == null){
// 或者 if(findFragmentByTag(mFragmentTag) == null)
// 正常情況下去 加載根Fragment
}
}
詳細原因:從源碼角度分析,爲什麼會發生Fragment重疊?
如果你add()
了幾個Fragment,使用show()、hide()
方法控制,比如微信、QQ的底部tab等情景,如果你什麼都不做的話,在“內存重啓”後回到前臺,app的這幾個Fragment界面會重疊。
原因是FragmentManager幫我們管理Fragment,當發生“內存重啓”,他會從棧底向棧頂的順序一次性恢復Fragment;
但是因爲官方沒有保存Fragment的mHidden屬性,默認爲false,即show狀態,所以所有Fragment都是以show的形式恢復,我們看到了界面重疊。
(如果是replace
,恢復形式和Activity一致,只有當你pop之後上一個Fragment纔開始重新恢復,所有使用replace
不會造成重疊現象)
v4-24.0.0+ 開始,官方修復了上述 沒有保存mHidden的問題,所以如果你在使用24.0.0+的v4包,下面分析的2個解決方案可以自行跳過…
這裏給出2個解決方案:
1、是大家比較熟悉的 findFragmentByTag
:
即在add()
或者replace()
時綁定一個tag,一般我們是用fragment的類名作爲tag,然後在發生“內存重啓”時,通過findFragmentByTag
找到對應的Fragment,並hide()
需要隱藏的fragment。
下面是個標準恢復寫法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “內存重啓”時調用
targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
// 解決重疊問題
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常時
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
getFragmentManager().beginTransaction()
.add(R.id.container, targetFragment, targetFragment.getClass().getName())
.add(R.id,container,hideFragment,hideFragment.getClass().getName())
.hide(hideFragment)
.commit();
}
}
如果你想恢復到用戶離開時的那個Fragment的界面,你還需要在onSaveInstanceState(Bundle outState)
裏保存離開時的那個可見的tag或下標,在onCreate
“內存重啓”代碼塊中,取出tag/下標,進行恢復。
2、我的解決方案,9行代碼解決所有情況的Fragment重疊:傳送門
Fragment嵌套的那些坑
其實一些小夥伴遇到的很多嵌套的坑,大部分都是由於對嵌套的棧視圖產生混亂,只要理清棧視圖關係,做好恢複相關工作以及正確選擇是使用getFragmentManager()
還是getChildFragmentManager()
就可以避免這些問題。
這部分內容是我們感覺Fragment非常難用的一個點,我會在下一篇中,詳細介紹使用Fragment嵌套的一些技巧,以及如何清晰分析各個層級的棧視圖。
附:startActivityForResult接收返回問題
在support 23.2.0以下的支持庫中,對於在嵌套子Fragment的startActivityForResult ()
,會發現無論如何都不能在onActivityResult()
中接收到返回值,只有最頂層的父Fragment才能接收到,這是一個support v4庫的一個BUG,不過在前兩天發佈的support 23.2.0庫中,已經修復了該問題,嵌套的子Fragment也能正常接收到返回數據了!
未必靠譜的出棧方法remove()
如果你想讓某一個Fragment出棧,使用remove()
在加入回退棧時並不靠譜。
如果你在add的同時將Fragment加入回退棧:addToBackStack(name)的情況下,它並不能真正將Fragment從棧內移除,如果你在2秒後(確保Fragment事務已經完成)打印getSupportFragmentManager().getFragments()
,會發現該Fragment依然存在,並且依然可以返回到被remove的Fragment,而且是空白頁面。
如果你沒有將Fragment加入回退棧,remove方法可以正常出棧。
如果你加入了回退棧,popBackStack()
系列方法才能真正出棧,這也就引入下一個深坑,popBackStack(String tag,int flags)
等系列方法的BUG。
多個Fragment同時出棧的深坑BUG
在Fragment庫中如下4個方法是有BUG的:
1、popBackStack(String tag,int flags)
2、popBackStack(int id,int flags)
3、popBackStackImmediate(String tag,int flags)
4、popBackStackImmediate(int id,int flags)
上面4個方法作用是,出棧到tag/id的fragment,即一次多個Fragment被出棧。
1、FragmentManager棧中管理fragment下標位置的數組ArrayList<Integer> mAvailIndeices的BUG
下面的方法FragmentManagerImpl類方法,產生BUG的罪魁禍首是管理Fragment棧下標的mAvailIndeices
屬性:
void makeActive(Fragment f) {
if (f.mIndex >= 0) {
return;
}
if (mAvailIndices == null || mAvailIndices.size() <= 0) {
if (mActive == null) {
mActive = new ArrayList<Fragment>();
}
f.setIndex(mActive.size(), mParent);
mActive.add(f);
} else {
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
}
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
上面代碼最終導致了棧內順序不正確的問題,如下圖:
上面的這個情況,會一次異常,一次正常。帶來的問題就是“內存重啓”後,各種異常甚至Crash。
發現這BUG的時候,我一臉懵比,幸好,stackoverflow上有大神給出了解決方案!hack FragmentManagerImpl
的mAvailIndices
,對其進行一次Collections.reverseOrder()
降序排序,保證棧內Fragment的index的正確。
public class FragmentTransactionBugFixHack {
public static void reorderIndices(FragmentManager fragmentManager) {
if (!(fragmentManager instanceof FragmentManagerImpl))
return;
FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
if (fragmentManagerImpl.mAvailIndices != null && fragmentManagerImpl.mAvailIndices.size() > 1) {
Collections.sort(fragmentManagerImpl.mAvailIndices, Collections.reverseOrder());
}
}
}
使用方法就是通過popBackStackImmediate(tag/id)
多個Fragment後,調用
hanler.post(new Runnable(){
@Override
public void run() {
FragmentTransactionBugFixHack.reorderIndices(fragmentManager));
}
});
2、popBackStack的坑popBackStack
和popBackStackImmediate
的區別在於前者是加入到主線隊列的末尾,等其它任務完成後纔開始出棧,後者是隊列內的任務立即執行,再將出棧任務放到隊列尾(可以理解爲立即出棧)。
如果你popBackStack
多個Fragment後,緊接着beginTransaction()
add新的一個Fragment,接着發生了“內存重啓”後,你再執行popBackStack()
,app就會Crash,解決方案是postDelay出棧動畫時間再執行其它事務,但是根據我的觀察不是很穩定。
我的建議是:如果你想出棧多個Fragment,你應儘量使用popBackStackImmediate(tag/id)
,而不是popBackStack(tag/id)
,如果你想在出棧後,立刻beginTransaction()
開始一項事務,你應該把事務的代碼post/postDelay到主線程的消息隊列裏,下一篇有詳細描述。
深坑 Fragment轉場動畫(僅分析v4包下的Fragment)
如果你的Fragment沒有轉場動畫,或者使用setCustomAnimations(enter, exit)
的話,那麼上面的那些坑解決後,你可以愉快的玩耍了。
getFragmentManager().beginTransaction()
.setCustomAnimations(enter, exit)
// 如果你有通過tag/id同時出棧多個Fragment的情況時,
// 請謹慎使用.setCustomAnimations(enter, exit, popEnter, popExit)
// 因爲在出棧多Fragment時,伴隨出棧動畫,會在某些情況下發生異常
// 你需要搭配Fragment的onCreateAnimation()臨時取消出棧動畫,或者延遲一個動畫時間再執行一次上面提到的Hack方法,排序
(注意:如果你想給下一個Fragment設置進棧動畫和出棧動畫,.setCustomAnimations(enter, exit)只能設置進棧動畫,第二個參數並不是設置出棧動畫;
請使用.setCustomAnimations(enter, exit, popEnter, popExit),這個方法的第1個參數對應進棧動畫,第4個參數對應出棧動畫,所以是.setCustomAnimations(進棧動畫, exit, popEnter, 出棧動畫))
總結起來就是Fragment沒有出棧動畫的話,可以避免很多坑。
如果想讓出棧動畫運作正常的話,需要使用Fragment的onCreateAnimation
中控制動畫。
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
// 此處設置動畫
}
但是用代價也是有的,你需要解決出棧動畫帶來的幾個坑。
1、pop多個Fragment時轉場動畫 帶來的問題
在使用 pop(tag/id)
出棧多個Fragment的這種情況下,將轉場動畫臨時取消或者延遲一個動畫的時間再去執行其他事務;
原因在於這種情景下,如果發生“內存重啓”後,因爲Fragment轉場動畫沒結束時再執行其他方法,會導致Fragment狀態不會被FragmentManager正常保存下來。
2、進入新的Fragment並立刻關閉當前Fragment 時的一些問題
(1)如果你想從當前Fragment進入一個新的Fragment,並且同時要關閉當前Fragment。由於數據結構是棧,所以正確做法是先pop
,再add
,但是轉場動畫會有覆蓋的不正常現象,你需要特殊處理,不然會閃屏!
(2)Fragment的根佈局要設置android:clickable = true
,原因是在pop
後又立刻add
新的Fragment時,在轉場動畫過程中,如果你的手速太快,在動畫結束前你多點擊了一下,上一個Fragment的可點擊區域可能會在下一個Fragment上依然可用。
Tip:
如果你遇到Fragment的mNextAnim空指針的異常(通常是在你的Fragment被重啓的情況下),那麼你首先需要檢查是否操作的Fragment是否爲null;其次在你的Fragment轉場動畫還沒結束時,你是否就執行了其他事務等方法;解決思路就是延遲一個動畫時間再執行事務,或者臨時將該Fragment設爲無動畫
總結
看了上面的介紹,你可能會覺得Fragment有點可怕。
但是我想說,如果你只是淺度使用,比如一個Activity容器包含列表Fragment+詳情Fragment這種簡單情景下,不涉及到popBackStack/Immediate(tag/id)
這些的方法,還是比較輕鬆使用的,出現的問題,網上都可以找到解決方案。
但是如果你的Fragment邏輯比較複雜,有特殊需求,或者你的app架構是僅有一個Activity + 多個Fragment,上面說的這些坑,你都應該全部解決。
在下一篇中,介紹了一些非常實用的使用技巧,包括如何解決Fragment嵌套、各種環境、組件下Fragment的使用等技巧,推薦閱讀!
還有一些比較隱蔽的問題,不影響app的正常運行,僅僅是一些顯示的BUG,並沒有在上面介紹,在本系列的最後一篇,我給出了我的解決方案,一個我封裝的Fragmentation庫,解決了所有動畫問題,非常適合單Activity+多Fragment 或者 多模塊Activity+多Fragment的架構。有興趣的可以看看 :)
2、Fragment全解析系列(二):正確的使用姿勢
Fragment是可以讓你的app縱享絲滑的設計,如果你的app想在現在基礎上性能大幅度提高,並且佔用內存降低,同樣的界面Activity佔用內存比Fragment要多,響應速度Fragment比Activty在中低端手機上快了很多,甚至能達到好幾倍!如果你的app當前或以後有移植平板等平臺時,可以讓你節省大量時間和精力。
簡陋的目錄
1、一些使用建議
2、add(), show(), hide(), replace()的那點事
3、關於FragmentManager你需要知道的
4、使用FragmentPagerAdapter+ViewPager的注意事項
5、是使用單Activity+多Fragment的架構,還是多模塊Activity+多Fragment的架構?
作爲一個穩定的app,從後臺且回到前臺,一定會在任何情況都能恢復到離開前的頁面,並且保證數據的完整性。
如果你沒看過本系列的第一篇,爲了方便後面文章的介紹,先規定一個“術語”,安卓app有一種特殊情況,就是 app運行在後臺的時候,系統資源緊張的時候導致把app的資源全部回收(殺死app的進程),這時把app再從後臺返回到前臺時,app會重啓。這種情況下文簡稱爲:“內存重啓”。(屏幕旋轉等配置變化也會造成當前Activity重啓,本質與“內存重啓”類似)
1、一些使用建議
1、對Fragment傳遞數據,建議使用setArguments(Bundle args)
,而後在onCreate
中使用getArguments()
取出,在 “內存重啓”前,系統會幫你保存數據,不會造成數據的丟失。和Activity的Intent恢復機制類似。
2、使用newInstance(參數)
創建Fragment對象,優點是調用者只需要關係傳遞的哪些數據,而無需關心傳遞數據的Key是什麼。
3、如果你需要在Fragment中用到宿主Activity對象,建議在你的基類Fragment定義一個Activity的全局變量,在onAttach
中初始化,這不是最好的解決辦法,但這可以有效避免一些意外Crash。詳細原因參考第一篇的“getActivity()空指針”部分。
protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
2、add(), show(), hide(), replace()的那點事
1、區別
show()
,hide()
最終是讓Fragment的View setVisibility
(true還是false),不會調用生命週期;
replace()
的話會銷燬視圖,即調用onDestoryView、onCreateView等一系列生命週期;
add()
和 replace()
不要在同一個階級的FragmentManager裏混搭使用。
2、使用場景
如果你有一個很高的概率會再次使用當前的Fragment,建議使用show()
,hide()
,可以提高性能。
在我使用Fragment過程中,大部分情況下都是用show()
,hide()
,而不是replace()
。
注意:如果你的app有大量圖片,這時更好的方式可能是replace,配合你的圖片框架在Fragment視圖銷燬時,回收其圖片所佔的內存。
3、onHiddenChanged的回調時機
當使用add()
+show(),hide()
跳轉新的Fragment時,舊的Fragment回調onHiddenChanged()
,不會回調onStop()
等生命週期方法,而新的Fragment在創建時是不會回調onHiddenChanged()
,這點要切記。
4、Fragment重疊問題
使用show()
,hide()
帶來的一個問題就是,如果你不做任何額外處理,在“內存重啓”後,Fragment會重疊;(該BUG在support-v4 24.0.0+以上 官方已修復)
有些小夥伴可能就是爲了避免Fragment重疊問題,而選擇使用replace()
,但是使用show()
,hide()
時,重疊問題很簡單解決的:
- 如果你在用24.0.0+的版本,需要特殊處理,官方已經修復該BUG;
- 如果你在使用小於24.0.0以下的v4包,可以參考9行代碼讓你App內的Fragment對重疊說再見
3、關於FragmentManager你需要知道的
FragmentManager棧視圖:
(1)每個Fragment以及宿主Activity(繼承自FragmentActivity)都會在創建時,初始化一個FragmentManager對象,處理好Fragment嵌套問題的關鍵,就是理清這些不同階級的棧視圖。
下面給出一個簡要的關係圖:
(2)對於宿主Activity,getSupportFragmentManager()
獲取的FragmentActivity的FragmentManager對象;
對於Fragment,getFragmentManager()
是獲取的是父Fragment(如果沒有,則是FragmentActivity)的FragmentManager對象,而getChildFragmentManager()
是獲取自己的FragmentManager對象。
4、使用FragmentPagerAdapter+ViewPager的注意事項
-
使用FragmentPagerAdapter+ViewPager時,切換回上一個Fragment頁面時(已經初始化完畢),不會回調任何生命週期方法以及
onHiddenChanged()
,只有setUserVisibleHint(boolean isVisibleToUser)
會被回調,所以如果你想進行一些懶加載,需要在這裏處理。 -
在給ViewPager綁定FragmentPagerAdapter時,
new FragmentPagerAdapter(fragmentManager)
的FragmentManager,一定要保證正確,如果ViewPager是Activity內的控件,則傳遞getSupportFragmentManager()
,如果是Fragment的控件中,則應該傳遞getChildFragmentManager()
。只要記住ViewPager內的Fragments是當前組件的子Fragment這個原則即可。 -
你不需要考慮在“內存重啓”的情況下,去恢復的Fragments的問題,因爲FragmentPagerAdapter已經幫我們處理啦。
5、是使用單Activity+多Fragment的架構,還是多模塊Activity+多Fragment的架構?
單Activity+多Fragment:
一個app僅有一個Activity,界面皆是Frament,Activity作爲app容器使用。
優點:性能高,速度最快。參考:新版知乎 、google系app
缺點:邏輯比較複雜,尤其當Fragment之間聯動較多或者嵌套較深時,比較複雜。
多模塊Activity+多Fragment:
一個模塊用一個Activity,比如
1、登錄註冊流程:
LoginActivity + 登錄Fragment + 註冊Fragment + 填寫信息Fragment + 忘記密碼Fragment
2、或者常見的數據展示流程:
DataActivity + 數據列表Fragment + 數據詳情Fragment + …
優點:速度快,相比較單Activity+多Fragment,更易維護。
我的觀點:
權衡利弊,我認爲多模塊Activity+多Fragment是最合適的架構,開發起來不是很複雜,app的性能又很高效。
當然。Fragment只是官方提供的靈活組件,請優先遵從你的項目設計!真的特別複雜的界面,或者單個Activity就可以完成一個流程的界面,使用Activity可能是更好的方案。
最後
如果你讀完了第一篇和這篇文章,那麼我相信你使用多模塊Activity+多Fragment的架構所遇到的坑,大部分都應該能找到解決辦法。
但是如果流程較爲複雜,比如Fragment A需要啓動一個新的Fragment B並且關閉當前A,或者A啓動B,B在獲取數據後,想在返回到A時把數據交給A(類似Activity的startActivityForResult
),又或者你保證在Fragment轉場動畫的情況下,使用pop(tag\id)
從棧內退出多個Fragment,或者你甚至想Fragment有一個類似Activity的SingleTask
啓動模式,那麼你可以參考下一篇,我的解決方案庫,Fragmentation。它甚至提供了一個讓你在開發時,可以隨時查看所有階級的棧視圖的UI界面。
Fragment之我的解決方案:Fragmentation
如果你通讀了本系列的前兩篇,我相信你可以寫出大部分場景都能正常運行的Fragment了。如果你想了解更多,那麼你可以看看我封裝的這個庫:Fragmentation。
本篇主要介紹這個庫,解決了一些BUG,使用簡單,提供實時查看棧視圖等實用功能。
源碼地址:Github,歡迎Star,Fork。
Demo網盤下載(V_0.9.0)
Demo演示:
單Activity + 多Fragment,項目中有3個Demo。
流式的單Activity+多Fragment:
類似微信交互方式的單Activity+多Fragment:(全頁面支持滑動返回)
類似新版仿知乎交互方式的單Activity+多Frgment:
Fragmentation
爲”單Activity + 多Fragment的架構”,”多模塊Activity + 多Fragment的架構”而生,幫你簡化使用過程,輕鬆解決各種複雜嵌套等問題,修復了官方Fragment庫存在的一些BUG。
特性
1、快速開發出各種嵌套設計的Fragment App
2、實時查看Fragment的(包括嵌套Fragment)棧視圖的對話框和Log,方便調試
3、增加啓動模式、startForResult等類似Activity方法
4、類似Android事件分發機制的Fragment回退方法:onBackPressedSupport(),輕鬆爲每個Fragment實現Back按鍵事件
5、New!!! 提供onSupportVisible()等生命週期方法,簡化嵌套Fragment的開發過程; 提供統一的onLazyInitView()懶加載方法
6、提供靠譜的 Fragment轉場動畫 的解決方案
7、更強的兼容性, 解決多點觸控、重疊等問題
8、支持SwipeBack滑動邊緣退出(需要使用Fragmentation_SwipeBack庫,詳情README)
TODO
* 棧視圖懸浮球/搖一搖喚出 棧視圖
- Activity側滑返回:更換實現方式
- Fragment側滑返回:實現視覺差效果
- replace進一步的支持
- Fragment路由module
重大更新日誌
0.10.X 詳情點這裏
1、添加可全局配置的Fragmentaion Builder類:
-
提供方便打開棧視圖Dialog的方法:
- bubble: 顯示懸浮球,可點擊喚出棧視圖Dialog,可自由拖拽
- shake: 搖一搖喚出棧視圖Dialog
- none: 默認不顯示棧視圖Dialog
-
根據是否是Debug環境,方便區別處理異常(”Can not perform this action after onSaveInstanceState!”)
2、兼容v4-25.1.1
0.9.X
1、解決多點觸控問題,多項優化、兼容、Fix
2、對於25.1.0+的 v4包,完善了SharedElement!
0.8.X
1、提供onLazyInitView()懶加載,onSupportVisible(),onSupportInvisible()等生命週期方法,簡化開發;
2、SupportActivity提供registerFragmentLifecycleCallbacks()來監控其下所有Fragment的生命週期;
3、自定義Tag
如何使用
1. 項目下app的build.gradle中依賴:
// appcompat v7包是必須的
compile 'me.yokeyword:fragmentation:0.10.1'
// 如果想使用SwipeBack 滑動邊緣退出Fragment/Activity功能,請再添加下面的庫
// compile 'me.yokeyword:fragmentation-swipeback:0.7.9'
2. Activity繼承SupportActivity:
public class MainActivity extends SupportActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState == null) {
loadRootFragment(R.id.fl_container, HomeFragment.newInstance()); // 加載根Fragment
}
// 棧視圖功能,大大降低Fragment的開發難度,建議在Application裏初始化
Fragmentation.builder()
// 顯示懸浮球 ; 其他Mode:SHAKE: 搖一搖喚出 NONE:隱藏
.stackViewMode(Fragmentation.BUBBLE)
.install();
}
3. Fragment繼承SupportFragment:
public class HomeFragment extends SupportFragment {
private void xxx() {
// 啓動新的Fragment, 同時還有start(fragment,SINGTASK)、startForResult、startWithPop等啓動方法
start(DetailFragment.newInstance(HomeBean));
// ... 其他方法請自行查看 API
}
進一步使用,查看wiki
</div>
<!-- -->
<div class="show-foot">
<a class="notebook" href="/nb/8373247">
<i class="iconfont ic-search-notebook"></i> <span>Fragment隨筆</span>
© 著作權歸作者所有
文/YoKeyword(簡書作者)
原文鏈接:http://www.jianshu.com/p/d9143a92ad94
著作權歸作者所有,轉載請聯繫作者獲得授權,並標註“簡書作者”。