Android使用Fragment打造萬(wàn)能頁(yè)面切換框架
首先我們來(lái)回憶一下傳統(tǒng)用Activity進(jìn)行的頁(yè)面切換,activity之間切換,首先需要新建intent對(duì)象,給該對(duì)象設(shè)置一些必須的參數(shù),然后調(diào)用startActivity方法進(jìn)行頁(yè)面跳轉(zhuǎn)。如果需要activity返回結(jié)果,則調(diào)用startActivityForResult方法,在onActivityResult方法中獲得返回結(jié)果。此外,每一個(gè)要展示的activity需要在AndroidManifest.xml文件中注冊(cè)。而且,如果在某些特定的情況下(比如65536方法數(shù)爆炸)要?jiǎng)討B(tài)加載dex,還得手動(dòng)管理activity的生命周期。那么,有沒(méi)有這么一種方法進(jìn)行頁(yè)面切換時(shí),無(wú)需在AndroidManifest.xml文件中聲明這些信息,動(dòng)態(tài)加載時(shí)又無(wú)需我們管理生命周期,等等優(yōu)點(diǎn)呢。
我們來(lái)回憶一下,在android3.0之后,谷歌出了一個(gè)Fragment,這個(gè)東西依賴(lài)于activity,其生命周期由宿主activity進(jìn)行管理,并且可以通過(guò)FragmentManager和FragmentTransaction等相關(guān)的類(lèi)進(jìn)行管理。那么我們能不能從Fragment入手,打造一個(gè)完全由Fragment組成的頁(yè)面跳轉(zhuǎn)框架呢。
使用Fragment其實(shí)很簡(jiǎn)單,首先開(kāi)啟一個(gè)事務(wù),通過(guò)add,replace,remove等方法進(jìn)行添加,替換,移除等操作,這一切的操作可能需要依賴(lài)一個(gè)容器,這個(gè)容器提供一個(gè)id,進(jìn)行對(duì)應(yīng)操作時(shí)將這個(gè)id作為參數(shù)傳入。之后通過(guò)相應(yīng)方法提交事務(wù)就可以了,就像這樣子。
- FragmentManager fragmentManager = getSupportFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
- fragmentTransaction.replace(R.id.fragment_container, fragment);
- fragmentTransaction.commit();
然而我相信你一定有這樣的經(jīng)歷,在使用Fragment進(jìn)行頁(yè)面切換時(shí)又得不斷用代碼控制其顯示與隱藏的邏輯,那么有沒(méi)有這樣一種方法在程序中不斷復(fù)用這段代碼呢?
首先,我們希望Fragment能像Activity那樣,進(jìn)行正確的跳轉(zhuǎn)。那么需要什么,答案是Fragment對(duì)象,我們肯定需要它的Class全類(lèi)名,當(dāng)然跳轉(zhuǎn)的時(shí)候可能會(huì)帶上一些參數(shù),這個(gè)參數(shù)應(yīng)該通過(guò)Bundle進(jìn)行傳遞。而且,全類(lèi)名可能太長(zhǎng),不便記憶,我們參考web的架構(gòu),應(yīng)該還要取一個(gè)別名alias。就這樣,一個(gè)Fragment頁(yè)面的三個(gè)基本屬性就被我們抽取出來(lái)了,組成了如下的實(shí)體類(lèi)。在這個(gè)實(shí)體類(lèi)中,頁(yè)面?zhèn)鬟f的參數(shù)為json形式的String字符串對(duì)象,在需要使用的時(shí)候我們通過(guò)該json構(gòu)造出bundle。頁(yè)面名變量mName是整個(gè)程序唯一標(biāo)示該頁(yè)面的參數(shù),其值唯一,但是其對(duì)應(yīng)的class全類(lèi)名可以不唯一,也就是說(shuō)從name到class的映射可以一對(duì)多。
public class CorePage implements Serializable { private static final long serialVersionUID = 3736359137726536495L; private String mName; //頁(yè)面名 private String mClazz; //頁(yè)面class private String mParams; //傳入?yún)?shù),json object結(jié)構(gòu) public CorePage(String name, String clazz, String params) { mName = name; mClazz = clazz; mParams = params; } public String getClazz() { return mClazz; } public void setClazz(String clazz) { mClazz = clazz; } public String getName() { return mName; } public void setName(String name) { mName = name; } public String getParams() { return mParams; } public void setParams(String params) { mParams = params; } @Override public String toString() { return "Page{" + "mName='" + mName + '\'' + ", mClazz='" + mClazz + '\'' + ", mParams='" + mParams + '\'' + '}'; } }
實(shí)體類(lèi)編寫(xiě)好了,為了更方便的進(jìn)行頁(yè)面跳轉(zhuǎn),我們需要像Activity那樣,有一個(gè)配置文件,里面存著Fragment名到其全類(lèi)名的映射關(guān)系。那么這些數(shù)據(jù)存在哪呢。我們參考網(wǎng)絡(luò)數(shù)據(jù),一般從網(wǎng)絡(luò)上獲取的數(shù)據(jù)有兩種格式,一種是json,一種是xml,json由于其優(yōu)點(diǎn),在網(wǎng)絡(luò)傳輸中被大量使用,這里,我們優(yōu)先使用json,選定了json之后,就要選定一個(gè)json解析的框架,我們不使用android系統(tǒng)自帶的,我們使用阿里的fastjson,當(dāng)然你也可以使用gson或者jackson。我們的Fragment有很多,所以這個(gè)Fragment的配置文件應(yīng)該是一個(gè)json數(shù)組。就像這個(gè)樣子
[ { "name": "test1", "class": "cn.edu.zafu.corepage.sample.TestFragment1", "params": { "param1": "value1", "param2": "value2" } }, { "name": "test2", "class": "cn.edu.zafu.corepage.sample.TestFragment2", "params": "" } ]
有了這個(gè)配置,我們就要在程序進(jìn)入時(shí)讀取這個(gè)配置。我們將這個(gè)配置放在assets目錄下,當(dāng)然你也可以放在其他目錄下,只有你能讀取到就行,甚至你可以放在壓縮包里。然而,實(shí)際情況下,這個(gè)文件不應(yīng)該暴露,因?yàn)橐坏┍┞毒痛嬖陲L(fēng)險(xiǎn)。因此,大家可以采用更安全的方式存這些數(shù)據(jù),比如把數(shù)據(jù)壓縮到壓縮包中,增加一個(gè)密碼,而讀取文件的內(nèi)容的代碼我們移到native中取實(shí)現(xiàn),畢竟java層太容易被反編譯了,而c/c++層相對(duì)來(lái)說(shuō)會(huì)畢竟有難度。
這里為了簡(jiǎn)單,我們暫時(shí)放在assets目錄下,那么要從assets目錄中讀取這個(gè)文件內(nèi)容就必須有這么一個(gè)讀取該目錄下文件的函數(shù),該目錄在android中也算是一個(gè)比較特殊的目錄了,可以通過(guò)getAssets()函數(shù),然后獲得一個(gè)輸入流,將文件內(nèi)容讀出,然后將對(duì)應(yīng)的json解析出來(lái)就可以了。
/** * 從assets目錄下讀取文件 * * @param context 上下文 * @param fileName 文件名 * @return */ private String readFileFromAssets(Context context, String fileName) { String result = ""; try { InputStreamReader inputReader = new InputStreamReader(context.getResources().getAssets().open(fileName)); BufferedReader bufReader = new BufferedReader(inputReader); String line = ""; while ((line = bufReader.readLine()) != null) result += line; } catch (Exception e) { e.printStackTrace(); } return result; }
然后根據(jù)該文件內(nèi)容讀取json配置。讀取出來(lái)后需要將這些數(shù)據(jù)保存下來(lái)。因此要有一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)保存這個(gè)對(duì)象,存完之后還要方便取出,存取的依據(jù)應(yīng)該是Fragment的表示,即前面提到的name,因此Map這個(gè)數(shù)據(jù)結(jié)構(gòu)是最適合不過(guò)了。
private Map<String, CorePage> mPageMap = new HashMap<String, CorePage>(); //保存page的map
將配置讀取出來(lái)存進(jìn)該map,讀取的時(shí)候判斷name和class是否為空,為空則跳過(guò)。
/** * 從配置文件中讀取page */ private void readConfig() { Log.d(TAG, "readConfig from json"); String content = readFileFromAssets(mContext, "page.json"); JSONArray jsonArray = JSON.parseArray(content); Iterator<Object> iterator = jsonArray.iterator(); JSONObject jsonPage = null; String pageName = null; String pageClazz = null; String pageParams = null; while (iterator.hasNext()) { jsonPage = (JSONObject) iterator.next(); pageName = jsonPage.getString("name"); pageClazz = jsonPage.getString("class"); pageParams = jsonPage.getString("params"); if (TextUtils.isEmpty(pageName) || TextUtils.isEmpty(pageClazz)) { Log.d(TAG, "page Name is null or pageClass is null"); return; } mPageMap.put(pageName, new CorePage(pageName, pageClazz, pageParams)); Log.d(TAG, "put a page:" + pageName); } Log.d(TAG, "finished read pages,page size:" + mPageMap.size()); }
此外,除了從配置文件中讀取,我們應(yīng)該可以動(dòng)態(tài)添加,對(duì)外提供這個(gè)函數(shù)。
/** * 新增新頁(yè)面 * * @param name 頁(yè)面名 * @param clazz 頁(yè)面class * @param params 頁(yè)面參數(shù) * @return 是否新增成功 */ public boolean putPage(String name, Class<? extends BaseFragment> clazz, Map<String, String> params) { if (TextUtils.isEmpty(name) || clazz == null) { Log.d(TAG, "page Name is null or pageClass is null"); return false; } if (mPageMap.containsKey(name)) { Log.d(TAG, "page has already put!"); return false; } CorePage corePage = new CorePage(name, clazz.getName(), buildParams(params)); Log.d(TAG, "put a page:" + name); return true; } /** * 從hashMap中得到參數(shù)的json格式 * * @param params 頁(yè)面map形式參數(shù) * @return json格式參數(shù) */ private String buildParams(Map<String, String> params) { if (params == null) { return ""; } String result = JSON.toJSONString(params); Log.d(TAG, "params:" + result); return result; }
文章開(kāi)頭已經(jīng)說(shuō)了,頁(yè)面跳轉(zhuǎn)的參數(shù)是json形式的字符串,我們還要這么一個(gè)函數(shù),能夠根據(jù)json字符串構(gòu)造出一個(gè)bundle
/** * 根據(jù)page,從pageParams中獲得bundle * * @param corePage 頁(yè)面 * @return 頁(yè)面的參數(shù) */ private Bundle buildBundle(CorePage corePage) { Bundle bundle = new Bundle(); String key = null; Object value = null; if (corePage != null && corePage.getParams() != null) { JSONObject j = JSON.parseObject(corePage.getParams()); if (j != null) { Set<String> keySet = j.keySet(); if (keySet != null) { Iterator<String> ite = keySet.iterator(); while (ite.hasNext()) { key = ite.next(); value = j.get(key); bundle.putString(key, value.toString()); } } } } return bundle; }
以上配置讀取的一系列函數(shù),構(gòu)成了頁(yè)面管理類(lèi)CorePageManager,我們對(duì)其應(yīng)用單例模式。
/** * 跳轉(zhuǎn)頁(yè)面管理 */ public class CorePageManager { private volatile static CorePageManager mInstance = null; //單例 private Context mContext; //Context上下文 /** * 構(gòu)造函數(shù)私有化 */ private CorePageManager() { } /** * 獲得單例 * * @return PageManager */ public static CorePageManager getInstance() { if (mInstance == null) { synchronized (CorePageManager.class) { if (mInstance == null) { mInstance = new CorePageManager(); } } } return mInstance; } /** * 初始化配置 * * @param context 上下文 */ public void init(Context context) { try { mContext = context.getApplicationContext(); readConfig(); } catch (Exception e) { e.printStackTrace(); } } }
其中init函數(shù)暴露給程序入口,進(jìn)行配置文件的讀取。一般放在Application的子類(lèi)的onCreate方法中即可。
到這里為止,基本上我們一切已經(jīng)就緒了。就差如何切換了。這里,在CorePageManager類(lèi)中再提供兩個(gè)核心函數(shù),用于處理頁(yè)面切換。
下面這個(gè)函數(shù)是頁(yè)面切換的核心函數(shù),首先根據(jù)參數(shù)從map中拿到對(duì)應(yīng)的實(shí)體類(lèi),假設(shè)存在這個(gè)頁(yè)面,通過(guò)class名用反射獲得該Fragment對(duì)象,調(diào)用前面寫(xiě)好的創(chuàng)建Bundle的函數(shù)得到頁(yè)面參數(shù),并與當(dāng)前函數(shù)中的入?yún)undle進(jìn)行合并。將參數(shù)設(shè)置給fragment對(duì)象,開(kāi)啟一個(gè)fragment事務(wù),查找id為fragment_container的fragment容器,如果該容器已經(jīng)有fragment,則隱藏它,如果該函數(shù)傳遞了動(dòng)畫(huà)參數(shù),則添加頁(yè)面切換動(dòng)畫(huà),然后將反射獲得的fragment對(duì)象添加到該容器中,如果需要添加到返回棧,則調(diào)用addToBackStack,最后提交事務(wù)并返回該fragment對(duì)象。
整個(gè)函數(shù)很簡(jiǎn)單,就是我們平常在activity中寫(xiě)的切換fragment的代碼
/** * 頁(yè)面跳轉(zhuǎn)核心函數(shù)之一 * 打開(kāi)一個(gè)fragemnt * * @param fragmentManager FragmentManager管理類(lèi) * @param pageName 頁(yè)面名 * @param bundle 參數(shù) * @param animations 動(dòng)畫(huà)類(lèi)型 * @param addToBackStack 是否添加到返回棧 * @return */ public Fragment openPageWithNewFragmentManager(FragmentManager fragmentManager, String pageName, Bundle bundle, int[] animations, boolean addToBackStack) { BaseFragment fragment = null; try { CorePage corePage = this.mPageMap.get(pageName); if (corePage == null) { Log.d(TAG, "Page:" + pageName + " is null"); return null; } fragment = (BaseFragment) Class.forName(corePage.getClazz()).newInstance(); Bundle pageBundle = buildBundle(corePage); if (bundle != null) { pageBundle.putAll(bundle); } fragment.setArguments(pageBundle); fragment.setPageName(pageName); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); if (animations != null && animations.length >= 4) { fragmentTransaction.setCustomAnimations(animations[0], animations[1], animations[2], animations[3]); } Fragment fragmentContainer = fragmentManager.findFragmentById(R.id.fragment_container); if (fragmentContainer != null) { fragmentTransaction.hide(fragmentContainer); } fragmentTransaction.add(R.id.fragment_container, fragment, pageName); if (addToBackStack) { fragmentTransaction.addToBackStack(pageName); } fragmentTransaction.commitAllowingStateLoss(); //fragmentTransaction.commit(); } catch (Exception e) { e.printStackTrace(); Log.d(TAG, "Fragment.error:" + e.getMessage()); return null; } return fragment; }
而上面這個(gè)函數(shù)中的id值在一個(gè)基礎(chǔ)的布局中,之后的Fragment都會(huì)添加到該布局中去。我們的基類(lèi)Activity也將使用這個(gè)布局,這個(gè)后續(xù)編寫(xiě)B(tài)aseActivity的時(shí)候會(huì)提到
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:layout_width="fill_parent" android:layout_height="fill_parent" > </FrameLayout>
此外,我們?cè)偬峁┮粋€(gè)核心函數(shù)。就是如果返回棧中存在了目標(biāo)fragment,則將其彈出,否則新建fragment打開(kāi)。
/** * 頁(yè)面跳轉(zhuǎn)核心函數(shù)之一 * 打開(kāi)一個(gè)Fragement,如果返回棧中有則出棧,否則新建 * * @param fragmentManager FragmentManager管理類(lèi) * @param pageName 頁(yè)面別名 * @param bundle 參數(shù) * @param animations 動(dòng)畫(huà) * @return 成功跳轉(zhuǎn)到的fragment */ public Fragment gotoPage(FragmentManager fragmentManager, String pageName, Bundle bundle, int[] animations) { Log.d(TAG, "gotoPage:" + pageName); Fragment fragment = null; if (fragmentManager != null) { fragment = fragmentManager.findFragmentByTag(pageName); } if (fragment != null) { fragmentManager.popBackStackImmediate(pageName, 0); } else { fragment = this.openPageWithNewFragmentManager(fragmentManager, pageName, bundle, animations, true); } return fragment; }
細(xì)心的你可能已經(jīng)注意到了頁(yè)面跳轉(zhuǎn)函數(shù)中用到了動(dòng)畫(huà),其實(shí)這個(gè)動(dòng)畫(huà)是一個(gè)數(shù)組,為了方便使用,我們將其封裝為枚舉類(lèi),提供常見(jiàn)的幾種動(dòng)畫(huà)形式。
package cn.edu.zafu.corepage.core; /** * 頁(yè)面切換動(dòng)畫(huà)類(lèi)別 */ public enum CoreAnim { none, /* 沒(méi)有動(dòng)畫(huà) */ present, /*由下到上動(dòng)畫(huà) */ slide,/* 從左到右動(dòng)畫(huà) */ fade;/*漸變 */ }
之后我們還要根據(jù)該枚舉類(lèi)獲得對(duì)應(yīng)的動(dòng)畫(huà)的xml文件。
/** * 動(dòng)畫(huà)轉(zhuǎn)化,根據(jù)枚舉類(lèi)返回int數(shù)組 * * @param coreAnim * @return */ public static int[] convertAnimations(CoreAnim coreAnim) { if (coreAnim == CoreAnim.present) { int[] animations = {R.anim.push_in_down, R.anim.push_no_ani, R.anim.push_no_ani, R.anim.push_out_down}; return animations; } else if (coreAnim == CoreAnim.fade) { int[] animations = {R.anim.alpha_in, R.anim.alpha_out, R.anim.alpha_in, R.anim.alpha_out}; return animations; } else if (coreAnim == CoreAnim.slide) { int[] animations = {R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right}; return animations; } return null; }
這里貼出一個(gè)alpha_in.xml中的代碼,其他文件類(lèi)似,這些文件都位于res/anim目錄下
<?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@android:integer/config_mediumAnimTime" android:fromAlpha="0.0" android:toAlpha="1.0" />
到了這里,如果你都明白了,那么后面基本上就沒(méi)有什么難度了,因?yàn)橹蟮墓δ芏际腔谝陨蟽?nèi)容。
前面我們定義了一個(gè)CorePage實(shí)體類(lèi)用于保存配置文件中實(shí)體類(lèi)的信息,而頁(yè)面切換過(guò)程中需要傳遞一些參數(shù),比如是否添加到fragment返回棧,是否在新的activity中打開(kāi)fragment,頁(yè)面切換時(shí)的動(dòng)畫(huà),傳遞的參數(shù)等等,通樣,我們將其封裝為實(shí)體類(lèi)。由于該對(duì)象可能需要通過(guò)intent傳遞,這里我們將其實(shí)現(xiàn)Parcelable接口。實(shí)現(xiàn)該接口方法很簡(jiǎn)單,假設(shè)使用的是android studio,使用快捷鍵alt+insert選擇Parcelable即可創(chuàng)建一個(gè)模板,我們將其補(bǔ)齊就好了。整個(gè)類(lèi)如下,我們對(duì)外提供了多個(gè)重載的構(gòu)造函數(shù),其本質(zhì)都是一樣的,而前面的動(dòng)畫(huà)轉(zhuǎn)換函數(shù)我們將其放入這個(gè)類(lèi)中。
/** * 頁(yè)面跳轉(zhuǎn)控制參數(shù) */ public class CoreSwitchBean implements Parcelable { public static final Parcelable.Creator<CoreSwitchBean> CREATOR = new Parcelable.Creator<CoreSwitchBean>() { @Override public CoreSwitchBean createFromParcel(Parcel in) { return new CoreSwitchBean(in); } @Override public CoreSwitchBean[] newArray(int size) { return new CoreSwitchBean[size]; } }; private String mPageName; //頁(yè)面名 private Bundle mBundle; //相關(guān)數(shù)據(jù) private int[] mAnim = null; //動(dòng)畫(huà)類(lèi)型 private boolean mAddToBackStack = true; //是否添加到棧中 private boolean mNewActivity = false; //是否起新的Activity private int requestCode = -1; //fragment跳轉(zhuǎn) public CoreSwitchBean(String pageName) { this.mPageName = pageName; } public CoreSwitchBean(String pageName, Bundle bundle) { this.mPageName = pageName; this.mBundle = bundle; } public CoreSwitchBean(String pageName, Bundle bundle, CoreAnim coreAnim) { this.mPageName = pageName; this.mBundle = bundle; this.setAnim(coreAnim); } public void setAnim(CoreAnim anim) { mAnim = convertAnimations(anim); } /** * 動(dòng)畫(huà)轉(zhuǎn)化,根據(jù)枚舉類(lèi)返回int數(shù)組 * * @param coreAnim * @return */ public static int[] convertAnimations(CoreAnim coreAnim) { if (coreAnim == CoreAnim.present) { int[] animations = {R.anim.push_in_down, R.anim.push_no_ani, R.anim.push_no_ani, R.anim.push_out_down}; return animations; } else if (coreAnim == CoreAnim.fade) { int[] animations = {R.anim.alpha_in, R.anim.alpha_out, R.anim.alpha_in, R.anim.alpha_out}; return animations; } else if (coreAnim == CoreAnim.slide) { int[] animations = {R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right}; return animations; } return null; } public CoreSwitchBean(String pageName, Bundle bundle, int[] anim) { this.mPageName = pageName; this.mBundle = bundle; this.mAnim = anim; } public CoreSwitchBean(String pageName, Bundle bundle, CoreAnim coreAnim, boolean addToBackStack) { this.mPageName = pageName; this.mBundle = bundle; this.setAnim(coreAnim); this.mAddToBackStack = addToBackStack; } public CoreSwitchBean(String pageName, Bundle bundle, int[] anim, boolean addToBackStack) { this.mPageName = pageName; this.mBundle = bundle; this.mAnim = anim; this.mAddToBackStack = addToBackStack; } public CoreSwitchBean(String pageName, Bundle bundle, CoreAnim coreAnim, boolean addToBackStack, boolean newActivity) { this.mPageName = pageName; this.mBundle = bundle; this.setAnim(coreAnim); this.mAddToBackStack = addToBackStack; this.mNewActivity = newActivity; } public CoreSwitchBean(String pageName, Bundle bundle, int[] anim, boolean addToBackStack, boolean newActivity) { this.mPageName = pageName; this.mBundle = bundle; this.mAnim = anim; this.mAddToBackStack = addToBackStack; this.mNewActivity = newActivity; } public CoreSwitchBean(String pageName, Bundle bundle, int[] anim, boolean addToBackStack, boolean newActivity, int requestCode) { this.mPageName = pageName; this.mBundle = bundle; this.mAnim = anim; this.mAddToBackStack = addToBackStack; this.mNewActivity = newActivity; this.requestCode = requestCode; } protected CoreSwitchBean(Parcel in) { mPageName = in.readString(); mBundle = in.readBundle(); int[] a = {in.readInt(), in.readInt(), in.readInt(), in.readInt()}; mAnim = a; mAddToBackStack = in.readInt() == 1 ? true : false; mNewActivity = in.readInt() == 1 ? true : false; requestCode = in.readInt(); } public String getPageName() { return mPageName; } public void setPageName(String pageName) { mPageName = pageName; } public boolean isNewActivity() { return mNewActivity; } public void setNewActivity(boolean newActivity) { mNewActivity = newActivity; } public boolean isAddToBackStack() { return mAddToBackStack; } public void setAddToBackStack(boolean addToBackStack) { mAddToBackStack = addToBackStack; } public int[] getAnim() { return mAnim; } public void setAnim(int[] anim) { mAnim = anim; } public Bundle getBundle() { return mBundle; } public void setBundle(Bundle bundle) { mBundle = bundle; } public int getRequestCode() { return requestCode; } public void setRequestCode(int requestCode) { this.requestCode = requestCode; } @Override public String toString() { return "SwitchBean{" + "mPageName='" + mPageName + '\'' + ", mBundle=" + mBundle + ", mAnim=" + Arrays.toString(mAnim) + ", mAddToBackStack=" + mAddToBackStack + ", mNewActivity=" + mNewActivity + ", requestCode=" + requestCode + '}'; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { if (mPageName == null) { mPageName = ""; } if (mBundle == null) { mBundle = new Bundle(); } if (mAnim == null) { int[] a = {-1, -1, -1, -1}; mAnim = a; } out.writeString(mPageName); mBundle.writeToParcel(out, flags); if (mAnim != null && mAnim.length == 4) { out.writeInt(mAnim[0]); out.writeInt(mAnim[1]); out.writeInt(mAnim[2]); out.writeInt(mAnim[3]); } else { out.writeInt(-1); out.writeInt(-1); out.writeInt(-1); out.writeInt(-1); } out.writeInt(mAddToBackStack ? 1 : 0); out.writeInt(mNewActivity ? 1 : 0); out.writeInt(requestCode); } }
該類(lèi)中的部分屬性有一些默認(rèn)值,比如是否添加到返回棧,是否起新Activity,我們默認(rèn)在當(dāng)前activity中打開(kāi)fragment,并且添加到返回棧。有了這個(gè)類(lèi),之后的頁(yè)面切換都通過(guò)該實(shí)體類(lèi)進(jìn)行傳參就可以了。
然后,我們定義一個(gè)接口,讓基類(lèi)activity實(shí)現(xiàn)該接口,用于切換時(shí)的一些常用操作。fragment中調(diào)用宿主activity中該接口的方法即可。
/** * 頁(yè)面跳轉(zhuǎn)接口,用于控制頁(yè)面跳轉(zhuǎn)或啟動(dòng)新的activity */ public interface CoreSwitcher { /** * 返回到前一個(gè)頁(yè)面(只有一個(gè)fragment時(shí)會(huì)關(guān)閉Activityt) */ void popPage(); /** * fragmentTag 是否在當(dāng)前頂上activity上的最頂上的fragment * * @param fragmentTag * @return */ boolean isFragmentTop(String fragmentTag); /** * 是否查找到某個(gè)page * * @param pageName * @return */ boolean findPage(final String pageName); /** * 跳轉(zhuǎn)到某一個(gè)頁(yè)面。 * * @param bean * @return */ Fragment gotoPage(CoreSwitchBean bean); /** * 打開(kāi)一個(gè)新的頁(yè)面 * * @param bean * @return */ Fragment openPage(CoreSwitchBean bean); /** * 移除當(dāng)前Acitivity不需要的fragment * * @param fragmentLists */ void removeUnlessFragment(List<String> fragmentLists); /** * 頁(yè)面跳轉(zhuǎn),支持跨Activity進(jìn)行傳遞數(shù)據(jù) * * @param page * @param fragment * @return */ public Fragment openPageForResult(final CoreSwitchBean page, final BaseFragment fragment); }
到了這里,似乎已經(jīng)初具模型了,接下來(lái),我們實(shí)現(xiàn)該接口。為了保證在子線程中也能調(diào)用這些方法,我們需要一個(gè)主線程的handler來(lái)幫我們完成一部分工作。假設(shè)我們已經(jīng)獲得了這個(gè)handler。具體細(xì)節(jié)看下面的代碼實(shí)現(xiàn)吧,仔細(xì)閱讀以下不難理解的。
private static List<WeakReference<BaseActivity>> mActivities = new ArrayList<WeakReference<BaseActivity>>(); //所有activity的引用 private Handler mHandler = null; //線程安全的handler private WeakReference<BaseActivity> mCurrentInstance = null; //當(dāng)前activity的引用 /** * 彈出頁(yè)面 */ @Override public void popPage() { popOrFinishActivity(); //如果只有一個(gè)Fagment則退出activty } /** * 保證在主線程操作 */ private void popOrFinishActivity() { if (this.isFinishing()) { return; } if (this.getSupportFragmentManager().getBackStackEntryCount() > 1) { if (isMainThread()) { this.getSupportFragmentManager().popBackStackImmediate(); } else { this.mHandler.post(new Runnable() { @Override public void run() { getSupportFragmentManager().popBackStackImmediate(); } }); } } else { finishActivity(this, true); } } /** * 是否是主線程 * @return */ private boolean isMainThread() { return Thread.currentThread() == this.getMainLooper().getThread(); } /** * 是否位于棧頂 * @param fragmentTag * @return */ @Override public boolean isFragmentTop(String fragmentTag) { int size = mActivities.size(); if (size > 0) { WeakReference<BaseActivity> ref = mActivities.get(size - 1); BaseActivity item = ref.get(); if (item != null && item == this) { FragmentActivity activity = item; FragmentManager manager = activity.getSupportFragmentManager(); if (manager != null) { int count = manager.getBackStackEntryCount(); if (count >= 1) { FragmentManager.BackStackEntry entry = manager.getBackStackEntryAt(count - 1); if (entry.getName().equalsIgnoreCase(fragmentTag)) { return true; } } } } } return false; } /** * 查找fragment * @param pageName * @return */ @Override public boolean findPage(String pageName) { int size = mActivities.size(); int j = size - 1; boolean hasFind = false; for (; j >= 0; j--) { WeakReference<BaseActivity> ref = mActivities.get(j); if (ref != null) { BaseActivity item = ref.get(); if (item == null) { Log.d(TAG, "item is null"); continue; } FragmentManager manager = item.getSupportFragmentManager(); int count = manager.getBackStackEntryCount(); for (int i = count - 1; i >= 0; i--) { String name = manager.getBackStackEntryAt(i).getName(); if (name.equalsIgnoreCase(pageName)) { hasFind = true; break; } } if (hasFind) { break; } } } return hasFind; } /** * 彈出并用bundle刷新數(shù)據(jù),在onFragmentDataReset中回調(diào) * @param page * @return */ @Override public Fragment gotoPage(CoreSwitchBean page) { if (page == null) { Log.e(TAG, "page name empty"); return null; } String pageName = page.getPageName(); if (!findPage(pageName)) { Log.d(TAG, "Be sure you have the right pageName" + pageName); return this.openPage(page); } int size = mActivities.size(); int i = size - 1; for (; i >= 0; i--) { WeakReference<BaseActivity> ref = mActivities.get(i); if (ref != null) { BaseActivity item = ref.get(); if (item == null) { Log.d(TAG, "item null"); continue; } boolean findInActivity = popFragmentInActivity(pageName, page.getBundle(), item); if (findInActivity) { break; } else { item.finish(); // 找不到就彈出 } } } return null; } /** * 當(dāng)前activiti中彈fragment * @param pageName * @param bundle * @param findAcitivity * @return */ protected boolean popFragmentInActivity(final String pageName, Bundle bundle, BaseActivity findAcitivity) { if (pageName == null || findAcitivity == null || findAcitivity.isFinishing()) { return false; } else { final FragmentManager fragmentManager = findAcitivity.getSupportFragmentManager(); if (fragmentManager != null) { Fragment frg = fragmentManager.findFragmentByTag(pageName); if (frg != null && frg instanceof BaseFragment) { if (fragmentManager.getBackStackEntryCount() > 1 && mHandler != null) { mHandler.postDelayed(new Runnable() { @Override public void run() { fragmentManager.popBackStack(pageName, 0); } }, 100); } ((BaseFragment) frg).onFragmentDataReset(bundle);//默認(rèn)為空實(shí)現(xiàn),用于舒心返回時(shí)的頁(yè)面數(shù)據(jù),重寫(xiě)改方法完成數(shù)據(jù)刷新 return true; } } } return false; } /** * 根據(jù)Switchpage打開(kāi)activity * @param page */ public void startActivity(CoreSwitchBean page) { try { Intent intent = new Intent(this, BaseActivity.class); intent.putExtra("SwitchBean", page); this.startActivity(intent); int[] animations = page.getAnim(); if (animations != null && animations.length >= 2) { this.overridePendingTransition(animations[0], animations[1]); } } catch (Exception e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } } /** * 根據(jù)SwitchBean打開(kāi)fragment * @param page * @return */ @Override public Fragment openPage(CoreSwitchBean page) { boolean addToBackStack = page.isAddToBackStack(); boolean newActivity = page.isNewActivity(); Bundle bundle = page.getBundle(); int[] animations = page.getAnim(); if (newActivity) { startActivity(page); return null; } else { String pageName = page.getPageName(); return CorePageManager.getInstance().openPageWithNewFragmentManager(getSupportFragmentManager(), pageName, bundle, animations, addToBackStack); } } /** * 移除無(wú)用fragment * @param fragmentLists */ @Override public void removeUnlessFragment(List<String> fragmentLists) { if (this.isFinishing()) { return; } FragmentManager manager = getSupportFragmentManager(); if (manager != null) { FragmentTransaction transaction = manager.beginTransaction(); for (String tag : fragmentLists) { Fragment fragment = manager.findFragmentByTag(tag); if (fragment != null) { transaction.remove(fragment); } } transaction.commitAllowingStateLoss(); int count = manager.getBackStackEntryCount(); if (count == 0) { this.finish(); } } } /** * 給BaseFragment調(diào)用 * @param page * @param fragment * @return */ @Override public Fragment openPageForResult(CoreSwitchBean page, BaseFragment fragment) { if (page != null) { if (page.isNewActivity()) { Log.d(TAG,"openPageForResult start new activity-----"+fragment.getPageName()); mFragmentForResult=fragment; mFragmentRequestCode=page.getRequestCode(); startActivityForResult(page); return null; }else{ String pageName=page.getPageName(); Bundle bundle=page.getBundle(); int[] animations=page.getAnim(); boolean addToBackStack=page.isAddToBackStack(); BaseFragment frg = (BaseFragment) CorePageManager.getInstance().openPageWithNewFragmentManager(getSupportFragmentManager(), pageName, bundle, animations, addToBackStack); if (frg==null){ return null; } final BaseFragment opener= fragment; frg.setRequestCode(page.getRequestCode()); frg.setFragmentFinishListener(new BaseFragment.OnFragmentFinishListener() { @Override public void onFragmentResult(int requestCode, int resultCode, Intent intent) { opener.onFragmentResult(requestCode,resultCode,intent); } }); return frg; } }else{ Log.d(TAG, "openPageForResult.SwitchBean is null"); } return null; } public void startActivityForResult(CoreSwitchBean page) { try { Intent intent = new Intent(this, BaseActivity.class); intent.putExtra("SwitchBean", page); intent.putExtra("startActivityForResult", "true"); this.startActivityForResult(intent, page.getRequestCode()); int[] animations = page.getAnim(); if (animations != null && animations.length >= 2) { this.overridePendingTransition(animations[0], animations[1]); } } catch (Exception e) { e.printStackTrace(); } } /** * 如果是fragment發(fā)起的由fragment處理,否則默認(rèn)處理 * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult from baseActivity" + requestCode + " " + resultCode); if (mFragmentRequestCode == requestCode && mFragmentForResult != null) { mFragmentForResult.onFragmentResult(mFragmentRequestCode, resultCode, data); } super.onActivityResult(requestCode, resultCode, data); }
除此之外,提供一些函數(shù)的重載便于調(diào)用以及一些工具函數(shù)。
/** * 僅用于接受應(yīng)用退出廣播,程序退出時(shí)有機(jī)會(huì)做一些必要的清理工作 */ private BroadcastReceiver mExitReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Config.ACTION_EXIT_APP)) { Log.d(TAG,"exit from broadcast"); finish(); } } }; /** * 返回最上層的activity * * @return */ public static BaseActivity getTopActivity() { if (mActivities != null) { int size = mActivities.size(); if (size >= 1) { WeakReference<BaseActivity> ref = mActivities.get(size - 1); if (ref != null) { return ref.get(); } } } return null; } /** * 廣播退出時(shí)清理activity列表 */ public static void unInit() { if (mActivities != null) { mActivities.clear(); } } /** * 獲得當(dāng)前活動(dòng)頁(yè)面名 * @return */ protected String getPageName() { BaseFragment frg = getActiveFragment(); if (frg != null) { return frg.getPageName(); } return ""; } /** * 打開(kāi)fragment,并設(shè)置是否新開(kāi)activity,設(shè)置是否添加到返回棧 * * @param pageName * @param bundle * @param coreAnim * @param addToBackStack * @param newActivity * @return */ public Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim, boolean addToBackStack, boolean newActivity) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, coreAnim, addToBackStack, newActivity); return openPage(page); } /** * 打開(kāi)fragment,并設(shè)置是否新開(kāi)activity,設(shè)置是否添加到返回棧 * * @param pageName * @param bundle * @param anim * @param addToBackStack * @param newActivity * @return */ public Fragment openPage(String pageName, Bundle bundle, int[] anim, boolean addToBackStack, boolean newActivity) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, anim, addToBackStack, newActivity); return openPage(page); } /** * 打開(kāi)fragment,并設(shè)置是否添加到返回棧 * * @param pageName * @param bundle * @param coreAnim * @param addToBackStack * @return */ public Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim, boolean addToBackStack) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, coreAnim, addToBackStack); return openPage(page); } /** * 打開(kāi)fragment,并設(shè)置是否添加到返回棧 * * @param pageName * @param bundle * @param anim * @param addToBackStack * @return */ public Fragment openPage(String pageName, Bundle bundle, int[] anim, boolean addToBackStack) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, anim, addToBackStack); return openPage(page); } /** * 打開(kāi)fragment * * @param pageName * @param bundle * @param coreAnim * @return */ public Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, coreAnim); return openPage(page); } /** * 打開(kāi)fragment * * @param pageName * @param bundle * @param anim * @return */ public Fragment openPage(String pageName, Bundle bundle, int[] anim) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, anim); return openPage(page); } /** * 如果當(dāng)前activity中只有一個(gè)activity,則關(guān)閉activity,否則父類(lèi)處理 */ @Override public void onBackPressed() { if (this.getSupportFragmentManager().getBackStackEntryCount() == 1) { this.finishActivity(this, true); } else { super.onBackPressed(); } } /** * 如果fragment中處理了則fragment處理否則activity處理 * @param keyCode * @param event * @return */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { BaseFragment activeFragment = getActiveFragment(); boolean isHanlde = false; if (activeFragment != null) { isHanlde = activeFragment.onKeyDown(keyCode, event); } if (!isHanlde) { return super.onKeyDown(keyCode, event); } else { return isHanlde; } } /** * 獲得當(dāng)前活動(dòng)fragmnet * * @return */ public BaseFragment getActiveFragment() { if (this.isFinishing()) { return null; } FragmentManager manager = this.getSupportFragmentManager(); if (manager != null) { int count = manager.getBackStackEntryCount(); if (count > 0) { String tag = manager.getBackStackEntryAt(count - 1).getName(); return (BaseFragment) manager.findFragmentByTag(tag); } } return null; } /** * 打印,調(diào)試用 */ private void printAllActivities() { Log.d(TAG, "------------BaseActivity print all------------activities size:" + mActivities.size()); for (WeakReference<BaseActivity> ref : mActivities) { if (ref != null) { BaseActivity item = ref.get(); if (item != null) { Log.d(TAG, item.toString()); } } } } /** * 結(jié)束activity,設(shè)置是否顯示動(dòng)畫(huà) * * @param activity * @param showAnimation */ private void finishActivity(BaseActivity activity, boolean showAnimation) { if (activity != null) { activity.finish(); } if (showAnimation) { //動(dòng)畫(huà) int[] animations = null; if (activity.mFirstCoreSwitchBean != null && activity.mFirstCoreSwitchBean.getAnim() != null) { animations = activity.mFirstCoreSwitchBean.getAnim(); } if (animations != null && animations.length >= 4) { overridePendingTransition(animations[2], animations[3]); } } }
并在BaseActivity的onCreate方法中完成一些初始化工作。
/** * 頁(yè)面跳轉(zhuǎn)都通過(guò)BaseActivity 嵌套Fragment來(lái)實(shí)現(xiàn),動(dòng)態(tài)替換fragment只需要指定相應(yīng)的參數(shù)。 避免Activity 需要再manifest中注冊(cè)的問(wèn)題。 * 1.管理應(yīng)用中所有BaseActivity 實(shí)例。 2.管理BaseActivity 實(shí)例和fragment的跳轉(zhuǎn) */ public class BaseActivity extends FragmentActivity implements CoreSwitcher { private static final String TAG = BaseActivity.class.getSimpleName(); private static List<WeakReference<BaseActivity>> mActivities = new ArrayList<WeakReference<BaseActivity>>(); protected CoreSwitchBean mFirstCoreSwitchBean;//記錄首個(gè),用于頁(yè)面切換 //所有activity的引用 private Handler mHandler = null; private WeakReference<BaseActivity> mCurrentInstance = null; //當(dāng)前activity的引用 private BaseFragment mFragmentForResult = null; //forResult 的fragment private int mFragmentRequestCode = -1; //請(qǐng)求碼,必須大于等于0 /** * 僅用于接受應(yīng)用退出廣播,程序退出時(shí)有機(jī)會(huì)做一些必要的清理工作 */ private BroadcastReceiver mExitReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Config.ACTION_EXIT_APP)) { Log.d(TAG,"exit from broadcast"); finish(); } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); Intent mNewIntent = getIntent(); //處理新開(kāi)activity的情況 if (null != savedInstanceState) { loadActivitySavedData(savedInstanceState); //恢復(fù)數(shù)據(jù) //需要用注解SaveWithActivity } mHandler = new Handler(getMainLooper()); //獲得主線程handler mCurrentInstance = new WeakReference<BaseActivity>(this); //當(dāng)前activity弱引用 mActivities.add(mCurrentInstance); //當(dāng)前activity增加到activity列表中 printAllActivities(); //打印所有activity情況 init(mNewIntent); //處理新開(kāi)activity跳轉(zhuǎn) IntentFilter filter = new IntentFilter(); filter.addAction(Config.ACTION_EXIT_APP); filter.addCategory(Intent.CATEGORY_DEFAULT); BaseApplication.getLocalBroadcastManager().registerReceiver(mExitReceiver, filter); //注冊(cè)本地廣播,接收程序退出廣播 } }
接下來(lái)就是處理基類(lèi)BaseFragment的問(wèn)題了,這里貼出該類(lèi)所有代碼,具體請(qǐng)參考注釋。
public class BaseFragment extends Fragment { private static final String TAG = BaseFragment.class.getSimpleName(); protected Activity mActivity; //所在activity private String mPageName; //頁(yè)面名 private int mRequestCode; //用于startForResult的requestCode private CoreSwitcher mPageCoreSwitcher; //openPageForResult接口,用于傳遞返回結(jié)果 private OnFragmentFinishListener mFragmentFinishListener; /** * 設(shè)置該接口用于返回結(jié)果 * @param listener OnFragmentFinishListener對(duì)象 */ public void setFragmentFinishListener(OnFragmentFinishListener listener) { this.mFragmentFinishListener = listener; } /** * 設(shè)置openPageForResult打開(kāi)的頁(yè)面的返回結(jié)果 * @param resultCode 返回結(jié)果碼 * @param intent 返回的intent對(duì)象 */ public void setFragmentResult(int resultCode, Intent intent) { if (mFragmentFinishListener != null) { mFragmentFinishListener.onFragmentResult(mRequestCode, resultCode, intent); } } /** * 得到requestCode * @return 請(qǐng)求碼 */ public int getRequestCode() { return this.mRequestCode; } /** * 設(shè)置requestCode * @param code 請(qǐng)求碼 */ public void setRequestCode(int code) { this.mRequestCode = code; } /** * 將Activity中onKeyDown在Fragment中實(shí)現(xiàn), * @param keyCode * @param event * @return */ public boolean onKeyDown(int keyCode, KeyEvent event) { return false; } /** * 數(shù)據(jù)設(shè)置,回調(diào) * @param bundle */ public void onFragmentDataReset(Bundle bundle) { } /** * 彈出棧頂?shù)腇ragment。如果Activity中只有一個(gè)Fragemnt時(shí),Acitivity也退出。 */ public void popToBack() { this.popToBack(null, null); } /** * 如果在fragment棧中找到,則跳轉(zhuǎn)到該fragment中去,否則彈出棧頂 * @param pageName 頁(yè)面名 * @param bundle 參數(shù) */ public final void popToBack(String pageName, Bundle bundle) { CoreSwitcher coreSwitcher = getSwitcher(); if (coreSwitcher != null) { if (pageName == null) { coreSwitcher.popPage(); } else { if (this.findPage(pageName)) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle); coreSwitcher.gotoPage(page); } else { coreSwitcher.popPage(); } } } else { Log.d(TAG, "pageSwitcher null"); } } /** * 得到頁(yè)面切換Switcher,即BaseActivity * @return Switcher */ public CoreSwitcher getSwitcher() { synchronized (BaseFragment.this) {// 加強(qiáng)保護(hù),保證pageSwitcher 不為null if (mPageCoreSwitcher == null) { if (this.mActivity != null && this.mActivity instanceof CoreSwitcher) { mPageCoreSwitcher = (CoreSwitcher) this.mActivity; } if (mPageCoreSwitcher == null) { BaseActivity topActivity = BaseActivity.getTopActivity(); if (topActivity != null && topActivity instanceof CoreSwitcher) { mPageCoreSwitcher = (CoreSwitcher) topActivity; } } } } return mPageCoreSwitcher; } public void setSwitcher(CoreSwitcher pageCoreSwitcher) { this.mPageCoreSwitcher = pageCoreSwitcher; } /** * 查找fragment是否存在,通過(guò)Switcher查找 * @param pageName 頁(yè)面名 * @return 是否找到 */ public boolean findPage(String pageName) { if (pageName == null) { Log.d(TAG, "pageName is null"); return false; } CoreSwitcher coreSwitcher = getSwitcher(); if (coreSwitcher != null) { return coreSwitcher.findPage(pageName); } else { Log.d(TAG, "pageSwitch is null"); return false; } } /** * 對(duì)應(yīng)fragment是否位于棧頂,通過(guò)Switcher查找 * @param fragmentTag fragment的tag * @return 是否位于棧頂 */ public boolean isFragmentTop(String fragmentTag) { CoreSwitcher pageCoreSwitcher = this.getSwitcher(); if (pageCoreSwitcher != null) { return pageCoreSwitcher.isFragmentTop(fragmentTag); } else { Log.d(TAG, "pageSwitcher is null"); return false; } } /** * 重新該方法用于獲得返回的數(shù)據(jù) * @param requestCode 請(qǐng)求碼 * @param resultCode 返回結(jié)果碼 * @param data 返回?cái)?shù)據(jù) */ public void onFragmentResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onFragmentResult from baseFragment:requestCode-" + requestCode + " resultCode-" + resultCode); } /** * 在當(dāng)前activity中打開(kāi)一個(gè)fragment,并添加到返回棧中 * @param pageName Fragemnt 名,在page.json中配置。 * @param bundle 頁(yè)面跳轉(zhuǎn)時(shí)傳遞的參數(shù) * @param coreAnim 指定的動(dòng)畫(huà)理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫(huà)) * @return */ public final Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim) { return this.openPage(pageName, bundle, CoreSwitchBean.convertAnimations(coreAnim), true); } /** * 在當(dāng)前activity中打開(kāi)一個(gè)fragment,并設(shè)置是否添加到返回棧 * @param pageName Fragemnt 名,在page.json中配置。 * @param bundle 頁(yè)面跳轉(zhuǎn)時(shí)傳遞的參數(shù) * @param anim 指定的動(dòng)畫(huà)農(nóng)林 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫(huà)) * @param addToBackStack 是否添加到用戶操作棧中 * @return */ public final Fragment openPage(String pageName, Bundle bundle, int[] anim, boolean addToBackStack) { return this.openPage(pageName, bundle, anim, addToBackStack, false); } /** * 打開(kāi)一個(gè)fragment并設(shè)置是否新開(kāi)activity,設(shè)置是否添加返回棧 * @param pageName Fragemnt 名,在page.json中配置。 * @param bundle 頁(yè)面跳轉(zhuǎn)時(shí)傳遞的參數(shù) * @param anim 指定的動(dòng)畫(huà)理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫(huà)) * @param addToBackStack 是否添加到用戶操作棧中 * @param newActivity 該頁(yè)面是否新建一個(gè)Activity * @return */ public final Fragment openPage(String pageName, Bundle bundle, int[] anim, boolean addToBackStack, boolean newActivity) { if (pageName == null) { Log.d(TAG, "pageName is null"); return null; } CoreSwitcher coreSwitcher = this.getSwitcher(); if (coreSwitcher != null) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, anim, addToBackStack, newActivity); return coreSwitcher.openPage(page); } else { Log.d(TAG, "pageSwitcher is null"); return null; } } /** * 在當(dāng)前activity中打開(kāi)一個(gè)fragment,并添加到返回棧中 * * @param pageName Fragemnt 名,在page.json中配置。 * @param bundle 頁(yè)面跳轉(zhuǎn)時(shí)傳遞的參數(shù) * @param anim 指定的動(dòng)畫(huà)理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫(huà)) * @return */ public final Fragment openPage(String pageName, Bundle bundle, int[] anim) { return this.openPage(pageName, bundle, anim, true); } /** * 在當(dāng)前activity中打開(kāi)一個(gè)fragment,并設(shè)置是否添加到返回棧 * * @param pageName Fragemnt 名,在page.json中配置。 * @param bundle 頁(yè)面跳轉(zhuǎn)時(shí)傳遞的參數(shù) * @param coreAnim 指定的動(dòng)畫(huà)理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫(huà)) * @param addToBackStack 是否添加到用戶操作棧中 * @return */ public final Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim, boolean addToBackStack) { return this.openPage(pageName, bundle, CoreSwitchBean.convertAnimations(coreAnim), addToBackStack, false); } /** * 打開(kāi)一個(gè)fragment并設(shè)置是否新開(kāi)activity,設(shè)置是否添加返回棧 * @param pageName Fragemnt 名,在page.json中配置。 * @param bundle 頁(yè)面跳轉(zhuǎn)時(shí)傳遞的參數(shù) * @param coreAnim 指定的動(dòng)畫(huà)理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫(huà)) * @param addToBackStack 是否添加到用戶操作棧中 * @param newActivity 該頁(yè)面是否新建一個(gè)Activity * @return */ public final Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim, boolean addToBackStack, boolean newActivity) { return this.openPage(pageName, bundle, CoreSwitchBean.convertAnimations(coreAnim), addToBackStack, newActivity); } /** * @param pageName * @param bundle * @param coreAnim * @return */ public Fragment gotoPage(String pageName, Bundle bundle, CoreAnim coreAnim) { return this.gotoPage(pageName, bundle, coreAnim,false); } /** * 新建或跳轉(zhuǎn)到一個(gè)頁(yè)面(Fragment)。找不到pageName Fragment時(shí),就新建Fragment。找到pageName * Fragment時(shí),則彈出該Fragement到棧頂上的所有actvity和fragment * * @param pageName Fragemnt 名,在在configure.zip 的pageContext.txt中配置。 * @param bundle 頁(yè)面跳轉(zhuǎn)時(shí)傳遞的參數(shù) * @param coreAnim 指定的動(dòng)畫(huà)理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫(huà)) * @param newActivity 該頁(yè)面是否新建一個(gè)Activity * @return */ public Fragment gotoPage(String pageName, Bundle bundle, CoreAnim coreAnim, boolean newActivity) { CoreSwitcher pageCoreSwitcher = this.getSwitcher(); if (pageCoreSwitcher != null) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, coreAnim, true, newActivity); return pageCoreSwitcher.gotoPage(page); } else { Log.d(TAG, "pageSwitcher is null"); return null; } } /** * 打開(kāi)fragment并請(qǐng)求獲得返回值 * @param pageName * @param bundle * @param coreAnim * @param requestCode 請(qǐng)求碼 * @return */ public final Fragment openPageForResult(String pageName, Bundle bundle, CoreAnim coreAnim, int requestCode) { return this.openPageForResult(false, pageName, bundle, coreAnim, requestCode); } /** * 打開(kāi)fragment并請(qǐng)求獲得返回值,并設(shè)置是否在新activity中打開(kāi) * @param newActivity * @param pageName * @param bundle * @param coreAnim * @param requestCode * @return */ public final Fragment openPageForResult(boolean newActivity, String pageName, Bundle bundle, CoreAnim coreAnim, int requestCode) { CoreSwitcher pageCoreSwitcher = this.getSwitcher(); if (pageCoreSwitcher != null) { CoreSwitchBean page = new CoreSwitchBean(pageName, bundle, coreAnim, true, newActivity); page.setRequestCode(requestCode); return pageCoreSwitcher.openPageForResult(page, this); } else { Log.d(TAG, "pageSwitcher is null"); return null; } } @Override public void onAttach(Activity activity) { super.onAttach(activity); mActivity = activity; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getPageName() != null) { Log.d(TAG, "====Fragment.onCreate====" + getPageName()); } } public String getPageName() { return mPageName; } public void setPageName(String pageName) { mPageName = pageName; } @Override public void onDetach() { super.onDetach(); mActivity = null; } //頁(yè)面跳轉(zhuǎn)接口 public interface OnFragmentFinishListener { void onFragmentResult(int requestCode, int resultCode, Intent intent); } }
其實(shí)無(wú)論BaseActivity還是BaseFragment的原理都是一樣的,都是通過(guò)接口中的函數(shù)進(jìn)行切換,最終都是調(diào)用了我們前面所說(shuō)的兩個(gè)核心函數(shù)。所謂的難點(diǎn),其實(shí)就是openPageForResult的函數(shù)。如果調(diào)整函數(shù)中指定了新開(kāi)activity,則直接調(diào)用startActivityForResult函數(shù)進(jìn)行跳轉(zhuǎn),目標(biāo)Activity中為BaseActivity,傳遞一些參數(shù)用于識(shí)別。
Intent intent = new Intent(this, BaseActivity.class); intent.putExtra("SwitchBean", page); intent.putExtra("startActivityForResult", "true");
然后再onCreate中獲得intent
Intent mNewIntent = getIntent(); init(mNewIntent);
調(diào)用了init函數(shù),在里面獲得傳遞的兩個(gè)參數(shù),如果是startActivityForResult,則對(duì)fragment設(shè)置回調(diào)函數(shù),當(dāng)我們手動(dòng)設(shè)置了setFragmentResult函數(shù)后回調(diào)就會(huì)被調(diào)用,即onFragmentResult函數(shù)回調(diào)
private void init(Intent mNewIntent) { try { CoreSwitchBean page = mNewIntent.getParcelableExtra("SwitchBean"); String startActivityForResult = mNewIntent.getStringExtra("startActivityForResult"); this.mFirstCoreSwitchBean = page; if (page != null) { BaseFragment fragment = null; boolean addToBackStack = page.isAddToBackStack(); String pageName = page.getPageName(); Bundle bundle = page.getBundle(); fragment = (BaseFragment) CorePageManager.getInstance().openPageWithNewFragmentManager(getSupportFragmentManager(), pageName, bundle, null, addToBackStack); if (fragment != null) { if ("true".equalsIgnoreCase(startActivityForResult)) { fragment.setRequestCode(page.getRequestCode()); fragment.setFragmentFinishListener(new BaseFragment.OnFragmentFinishListener() { @Override public void onFragmentResult(int requestCode, int resultCode, Intent intent) { BaseActivity.this.setResult(resultCode, intent); } }); } } else { this.finish(); } } } catch (Exception e) { e.printStackTrace(); Log.d(TAG, e.getMessage()); this.finish(); } }
最后的最后,也就是整個(gè)Fragment跳轉(zhuǎn)框架的初始化了,繼承Application編寫(xiě)一個(gè)應(yīng)用程序類(lèi)完成初始化。
public class BaseApplication extends Application { private static LocalBroadcastManager mLocalBroadcatManager; private static Context mContext; private static BaseApplication instance; public static Context getContext() { return mContext; } @Override public void onCreate() { super.onCreate(); instance = this; mContext = this.getApplicationContext(); CorePageManager.getInstance().init(this); } /** * 發(fā)送本地廣播退出程序 */ public void exitApp() { Intent intent = new Intent(); intent.setAction(Config.ACTION_EXIT_APP); intent.addCategory(Intent.CATEGORY_DEFAULT); BaseApplication.getLocalBroadcastManager().sendBroadcast(intent); BaseActivity.unInit(); } public static LocalBroadcastManager getLocalBroadcastManager() { if (mLocalBroadcatManager == null) { mLocalBroadcatManager = LocalBroadcastManager.getInstance(mContext); } return mLocalBroadcatManager; } }
完成聲明
<manifest package="cn.edu.zafu.corepage" xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:label="@string/app_name" > <activity android:name="cn.edu.zafu.corepage.base.BaseActivity" android:configChanges="keyboardHidden|orientation|screenSize" android:exported="false" android:launchMode="standard" android:screenOrientation="portrait" android:theme="@style/BaseActivityTheme" android:windowSoftInputMode="adjustUnspecified|stateHidden" > </activity> </application> </manifest>
之后的一切就會(huì)變得特別簡(jiǎn)單,調(diào)用即可。由于我是用android studio建的module,因此直接引用該module即可,然后提供一個(gè)程序入口Activity,該類(lèi)繼承BaseActivity,將該Activity聲明在manifest文件中,然后我們?cè)僖膊挥眯陆ˋctivity了,后續(xù)的頁(yè)面跳轉(zhuǎn)全都使用Fragment來(lái)完成。并且可以設(shè)置動(dòng)畫(huà)類(lèi)型等一系列的參數(shù)。
使用非常簡(jiǎn)單
openPage("test1",null, CoreAnim.slide); //打開(kāi)一個(gè)頁(yè)面,不傳遞參數(shù)第二個(gè)傳null,第三個(gè)參數(shù)為動(dòng)畫(huà)類(lèi)型,此方法有重載方法,第四個(gè)參數(shù)表示是否添加到返回棧,第五個(gè)參數(shù)表示是否新開(kāi)activity,一般情況下,只需傳遞前三個(gè)參數(shù)即可,而動(dòng)畫(huà)類(lèi)型,處理傳遞枚舉類(lèi),還支持自定義的動(dòng)畫(huà),對(duì)應(yīng)的文件參考res/anim目錄下的相應(yīng)文件 openPageForResult("test2",bundle,CoreAnim.fade,requestCode); //打開(kāi)一個(gè)頁(yè)面并獲得返回結(jié)果,之后調(diào)用setFragmentResult和popToBack設(shè)置結(jié)果 setFragmentResult(500, intent); popToBack(); 重寫(xiě)onFragmentResult函數(shù)獲取返回結(jié)果 @Override public void onFragmentResult(int requestCode, int resultCode, Intent data) { } //這個(gè)使用過(guò)程同startActivityFor的整個(gè)過(guò)程
效果圖:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- Android基礎(chǔ)之使用Fragment控制切換多個(gè)頁(yè)面
- Android App中使用ViewPager+Fragment實(shí)現(xiàn)滑動(dòng)切換效果
- Android Fragment中使用SurfaceView切換時(shí)閃一下黑屏的解決辦法
- 一個(gè)Activity中多個(gè)Fragment的切換
- Android中Fragment相互切換間不被回收的實(shí)現(xiàn)方法
- Android fragment實(shí)現(xiàn)多個(gè)頁(yè)面切換效果
- Android中使用TabHost 與 Fragment 制作頁(yè)面切換效果
- Android使用TabLayou+fragment+viewpager實(shí)現(xiàn)滑動(dòng)切換頁(yè)面效果
- Android開(kāi)發(fā)使用Activity嵌套多個(gè)Fragment實(shí)現(xiàn)橫豎屏切換功能的方法
- fragment實(shí)現(xiàn)隱藏及界面切換效果
相關(guān)文章
ReactiveCocoa代碼實(shí)踐之-RAC網(wǎng)絡(luò)請(qǐng)求重構(gòu)
這篇文章主要介紹了ReactiveCocoa代碼實(shí)踐之-RAC網(wǎng)絡(luò)請(qǐng)求重構(gòu) 的相關(guān)資料,需要的朋友可以參考下2016-04-04深入理解Android熱修復(fù)技術(shù)原理之so庫(kù)熱修復(fù)技術(shù)
通常情況下,大多數(shù)人希望android下熱補(bǔ)丁方案能夠做到補(bǔ)丁的全方位修復(fù),包括類(lèi)修復(fù)/資源修復(fù)/so庫(kù)的修復(fù)。 這里主要介紹熱補(bǔ)丁之so庫(kù)修復(fù)思路2021-06-06Android實(shí)現(xiàn)excel/pdf/word/odt/圖片相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了Android如何實(shí)現(xiàn)excel/pdf/word/odt/圖片之間的相互轉(zhuǎn)換,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-04-04Android 菜單欄DIY實(shí)現(xiàn)效果詳解
這篇文章主要為大家介紹了Android 菜單欄DIY實(shí)現(xiàn)效果詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Android實(shí)現(xiàn)快速滾動(dòng)FastScrollView效果
這篇文章主要介紹了Android實(shí)現(xiàn)快速滾動(dòng)FastScrollView效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08Android 使用SwipeRefreshLayout控件仿抖音做的視頻下拉刷新效果
這篇文章主要介紹了Android 使用SwipeRefreshLayout控件仿抖音做的視頻下拉刷新效果,需要的朋友可以參考下2018-05-05Android創(chuàng)建一個(gè)Activity的方法分析
這篇文章主要介紹了Android創(chuàng)建一個(gè)Activity的方法,結(jié)合實(shí)例形式分析了Android創(chuàng)建Activity的具體步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-04-04Android仿QQ微信實(shí)時(shí)監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài)
這篇文章主要為大家詳細(xì)介紹了Android仿QQ微信實(shí)時(shí)監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android?Hilt?Retrofit?Paging3使用實(shí)例
這篇文章主要介紹了Android?Hilt依賴(lài)注入的使用,首先,某個(gè)類(lèi)的成員變量稱(chēng)為依賴(lài),如若此變量想要實(shí)例化引用其類(lèi)的方法,可以通過(guò)構(gòu)造函數(shù)傳參或者通過(guò)某個(gè)方法獲取對(duì)象,此等通過(guò)外部方法獲取對(duì)象實(shí)例的稱(chēng)為依賴(lài)注入2023-01-01Flutter 快速實(shí)現(xiàn)聊天會(huì)話列表效果示例詳解
這篇文章主要為大家介紹了Flutter 快速實(shí)現(xiàn)聊天會(huì)話列表效果示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10