去年逛 github 的時候,發(fā)現(xiàn)一個仿今日頭條的項目鏈接,發(fā)現(xiàn)很多好玩的東西,比如顏色的漸變,F(xiàn)ragment 的管理,最后我在使用 Fragment 管理工具類——[主界面Fragment控制器]的時候栽了一個大跟頭,具體情境是我的 Activity 在切換夜間模式的時候,Activity 銷毀以后重建了,但是之前的Fragment并沒有得到重建,導(dǎo)致頁面上同時存在兩個Fragment,而我的Fragment在頁面啟動的時候有一些業(yè)務(wù),比如加載數(shù)據(jù)(進(jìn)度條展示),這個時候進(jìn)度條居然干不掉,物理按鍵沒有響應(yīng),最后才知道是因為 Fragment 在后臺,前臺的 Fragment 將返回事件消費掉了,當(dāng)時的處理方法是在切換日/夜間模式的時候,預(yù)先將所有的 Fragment 從事物中刪除,總算是解決了這個頭痛的問題!代碼如下:
`public void removeFragments() {
? ? ? ? FragmentTransaction ft = fm.beginTransaction();
? ? ? ? for (Fragment fragment : fragments) {
? ? ? ? ? ? if (fragment != null) {
? ? ? ? ? ? ? ? ft.remove(fragment);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? ft.commitAllowingStateLoss();
? ? }
`
直到今天才發(fā)現(xiàn)之前的處理方式是頭痛醫(yī)頭腳痛醫(yī)腳的治標(biāo)不治本的臨時措施,不能算作最后的解決方案,比如最近遇到的兩個問題:> 背景最近做的是簡單的登錄頁,包括用戶密碼登錄、手機號碼和短信驗證碼登錄、注冊賬號、重置密碼幾個狀態(tài),我得到需求后,腦子里第一個反應(yīng)就是使用 Fragment 來實現(xiàn)各個狀態(tài)之間的切換,最后通過上述工具類很輕松的實現(xiàn)了。> bug - 主頁面二次加載Fragm失敗當(dāng)Activity第一次進(jìn)來加載正常,onDestory以后再次進(jìn)來則加載失敗。 - Fragment需要銷毀時,內(nèi)容不能被清除就是 Fragment 切換的時候,內(nèi)容居然保留了,比如我注冊的時候輸入了用戶名,然后放棄注冊直接登錄,然后在登錄的時候忘記密碼選擇重新注冊一個賬號,這個時候加載的注冊 Fragment 之前填寫的內(nèi)容居然還在,,,第一個問題比較容易解決,就是在 Activity 銷毀的時候,調(diào)用工具類的 onDestroy 方法,將靜態(tài)的是否實例化 Fragment 給實例化的標(biāo)志位給置為默認(rèn)值,這樣再次進(jìn)來則會執(zhí)行默認(rèn)的方法,代碼如下:```public static void onDestroy() {isReload = false;fragments = null;controller = null;}//實例化的默認(rèn)方法private void initFragment() {fragments = new ArrayList<>();if (isReload) {//頁面需要加載的Fragmentfragments.add(new FileGruopFragment());fragments.add(new AboutFragment());fragments.add(new EmailFragment());// fragments.add(new AttentionFragment());// fragments.add(new MeFragment());FragmentTransaction ft = fm.beginTransaction();for (int i = 0; i < fragments.size(); i++) {ft.add(containerId, fragments.get(i), "" + i);}ft.commit();//此處可以使用shardPrefresh保存數(shù)量,以便下面for循環(huán)的時候不用手動修改常量} else {for (int i = 0; i < 5; i++) {fragments.add( fm.findFragmentByTag(i+""));}}}```第二個問題其實首頁需要看情況確定是否保留,如果 Fragment 有一些在實例化開銷比較大的情況,則應(yīng)該保留,如果需要保持時刻刷新的話,則需要跟上述業(yè)務(wù)相同的邏輯,F(xiàn)ragment 切換的時候清除內(nèi)容。后來仔細(xì)研究了一下代碼,其實在工具類的構(gòu)造方法里面將 Fragment 添加到了 FragmentTransaction 中,后續(xù)的 showFragment(int position) 方法則是將實例化隊列中的 Fragment 取出來進(jìn)行展示,并在展示之前將 所有隊列里面的 Fragment 調(diào)用 hide 方法進(jìn)行關(guān)閉展示。其實邏輯上很簡單,因為 FragmentTransaction 里面的 Fragment 和 Fragment 集合里面的對象都是一一對應(yīng)的,因此并沒有進(jìn)行通過 new 來實現(xiàn)刷新,而我也沒有在 Fragment 的屬性里面找到刷新頁面的方法,最后我新增加了一個變量,來保存當(dāng)前顯示的 Fragment 下標(biāo)值,而在 showFragment(int position) 的時候通過判斷當(dāng)前的 Fragment 是否是第一次加載,真則直接加載下一個 Fragment ,假則將 Fragment 集合里對應(yīng)的 Fragment 進(jìn)行替換,并將 FragmentTransaction 里面的Fragment也一并進(jìn)行替換,代碼如下:```private int lastPosition = -1;public void showFragment(int position) {hideFragments();Fragment fragment = fragments.get(position);FragmentTransaction ft = fm.beginTransaction();//ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);if(position != -1){ft.remove(fragment);fragment = getFragment(position);fragments.set(position,fragment);ft.add(containerId, fragment, "" + fragment);}ft.show(fragment);ft.commitAllowingStateLoss();lastPosition = position;}```這樣就解決上面的問題了,當(dāng)然,最后可以對這里進(jìn)行一個簡單的封裝,通過一個簡單的標(biāo)志位來判斷是否需要緩存,比如我們在靜態(tài)的方法里面新增一個 boolean 的字段,代碼如下:```package com.sasucen.ui.fragment;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentActivity;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentTransaction;import com.sasucen.ui.fragment.forget.ForgetFragment;import com.sasucen.ui.fragment.mobilelogin.MobileLoginFragment;import com.sasucen.ui.fragment.rigster.RigsterUserFragment;import com.sasucen.ui.fragment.userlogin.UserLoginFragment;import java.util.ArrayList;/*** Created by Vicent on 2018/3/23 0023.* Fragment管理類*/public class FragmentController {private int containerId;private FragmentManager fm;private ArrayList fragments;private static FragmentController controller;private static boolean isReload;private int lastPosition = -1;private static boolean isCache = false;public static FragmentController getInstance(FragmentActivity activity, int containerId, boolean isReload,boolean isCache) {FragmentController.isReload = isReload;FragmentController.isCache = isCache;if (controller == null) {controller = new FragmentController(activity, containerId);}return controller;}public static void onDestroy() {isReload = false;fragments = null;controller = null;}private FragmentController(FragmentActivity activity, int containerId) {this.containerId = containerId;fm = activity.getSupportFragmentManager();initFragment();}private void initFragment() {fragments = new ArrayList<>();if (isReload) {fragments.add(new UserLoginFragment());fragments.add(new MobileLoginFragment());fragments.add(new RigsterUserFragment());fragments.add(new ForgetFragment());FragmentTransaction ft = fm.beginTransaction();for (int i = 0; i < fragments.size(); i++) {ft.add(containerId, fragments.get(i), "" + i);}ft.commit();} else {for (int i = 0; i < 4; i++) {fragments.add( fm.findFragmentByTag(i+""));}}}public void showFragment(int position) {hideFragments();Fragment fragment = fragments.get(position);FragmentTransaction ft = fm.beginTransaction();//ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);if(position != -1 && isCache){ft.remove(fragment);fragment = getFragment(position);fragments.set(position,fragment);ft.add(containerId, fragment, "" + fragment);}ft.show(fragment);ft.commitAllowingStateLoss();lastPosition = position;}public void hideFragments() {FragmentTransaction ft = fm.beginTransaction();for (Fragment fragment : fragments) {if (fragment != null) {ft.hide(fragment);}}ft.commitAllowingStateLoss();}public Fragment getFragment(int position) {Fragment fragment = null;switch (position){case 0:fragment = new UserLoginFragment();break;case 1:fragment = new MobileLoginFragment();break;case 2:fragment = new RigsterUserFragment();break;case 3:fragment = new ForgetFragment();break;}return fragment;}}```上面只是我的一個日??偨Y(jié),如果有描述不正確的地方,請指教!如果這篇文章也能幫助到你,這也是我的幸運!十一點半了,天道酬勤這句雞湯我先干了!