Android使用Fragment打造萬能頁面切換框架
首先我們來回憶一下傳統(tǒng)用Activity進(jìn)行的頁面切換,activity之間切換,首先需要新建intent對(duì)象,給該對(duì)象設(shè)置一些必須的參數(shù),然后調(diào)用startActivity方法進(jìn)行頁面跳轉(zhuǎn)。如果需要activity返回結(jié)果,則調(diào)用startActivityForResult方法,在onActivityResult方法中獲得返回結(jié)果。此外,每一個(gè)要展示的activity需要在AndroidManifest.xml文件中注冊。而且,如果在某些特定的情況下(比如65536方法數(shù)爆炸)要?jiǎng)討B(tài)加載dex,還得手動(dòng)管理activity的生命周期。那么,有沒有這么一種方法進(jìn)行頁面切換時(shí),無需在AndroidManifest.xml文件中聲明這些信息,動(dòng)態(tài)加載時(shí)又無需我們管理生命周期,等等優(yōu)點(diǎn)呢。
我們來回憶一下,在android3.0之后,谷歌出了一個(gè)Fragment,這個(gè)東西依賴于activity,其生命周期由宿主activity進(jìn)行管理,并且可以通過FragmentManager和FragmentTransaction等相關(guān)的類進(jìn)行管理。那么我們能不能從Fragment入手,打造一個(gè)完全由Fragment組成的頁面跳轉(zhuǎn)框架呢。
使用Fragment其實(shí)很簡單,首先開啟一個(gè)事務(wù),通過add,replace,remove等方法進(jìn)行添加,替換,移除等操作,這一切的操作可能需要依賴一個(gè)容器,這個(gè)容器提供一個(gè)id,進(jìn)行對(duì)應(yīng)操作時(shí)將這個(gè)id作為參數(shù)傳入。之后通過相應(yīng)方法提交事務(wù)就可以了,就像這樣子。
- FragmentManager fragmentManager = getSupportFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
- fragmentTransaction.replace(R.id.fragment_container, fragment);
- fragmentTransaction.commit();
然而我相信你一定有這樣的經(jīng)歷,在使用Fragment進(jìn)行頁面切換時(shí)又得不斷用代碼控制其顯示與隱藏的邏輯,那么有沒有這樣一種方法在程序中不斷復(fù)用這段代碼呢?
首先,我們希望Fragment能像Activity那樣,進(jìn)行正確的跳轉(zhuǎn)。那么需要什么,答案是Fragment對(duì)象,我們肯定需要它的Class全類名,當(dāng)然跳轉(zhuǎn)的時(shí)候可能會(huì)帶上一些參數(shù),這個(gè)參數(shù)應(yīng)該通過Bundle進(jìn)行傳遞。而且,全類名可能太長,不便記憶,我們參考web的架構(gòu),應(yīng)該還要取一個(gè)別名alias。就這樣,一個(gè)Fragment頁面的三個(gè)基本屬性就被我們抽取出來了,組成了如下的實(shí)體類。在這個(gè)實(shí)體類中,頁面?zhèn)鬟f的參數(shù)為json形式的String字符串對(duì)象,在需要使用的時(shí)候我們通過該json構(gòu)造出bundle。頁面名變量mName是整個(gè)程序唯一標(biāo)示該頁面的參數(shù),其值唯一,但是其對(duì)應(yīng)的class全類名可以不唯一,也就是說從name到class的映射可以一對(duì)多。
public class CorePage implements Serializable {
private static final long serialVersionUID = 3736359137726536495L;
private String mName;
//頁面名
private String mClazz;
//頁面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í)體類編寫好了,為了更方便的進(jìn)行頁面跳轉(zhuǎn),我們需要像Activity那樣,有一個(gè)配置文件,里面存著Fragment名到其全類名的映射關(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ì)來說會(huì)畢竟有難度。
這里為了簡單,我們暫時(shí)放在assets目錄下,那么要從assets目錄中讀取這個(gè)文件內(nèi)容就必須有這么一個(gè)讀取該目錄下文件的函數(shù),該目錄在android中也算是一個(gè)比較特殊的目錄了,可以通過getAssets()函數(shù),然后獲得一個(gè)輸入流,將文件內(nèi)容讀出,然后將對(duì)應(yīng)的json解析出來就可以了。
/**
* 從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配置。讀取出來后需要將這些數(shù)據(jù)保存下來。因此要有一個(gè)數(shù)據(jù)結(jié)構(gòu)來保存這個(gè)對(duì)象,存完之后還要方便取出,存取的依據(jù)應(yīng)該是Fragment的表示,即前面提到的name,因此Map這個(gè)數(shù)據(jù)結(jié)構(gòu)是最適合不過了。
private Map<String, CorePage> mPageMap = new HashMap<String, CorePage>(); //保存page的map
將配置讀取出來存進(jìn)該map,讀取的時(shí)候判斷name和class是否為空,為空則跳過。
/**
* 從配置文件中讀取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ù)。
/**
* 新增新頁面
*
* @param name 頁面名
* @param clazz 頁面class
* @param params 頁面參數(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 頁面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;
}
文章開頭已經(jīng)說了,頁面跳轉(zhuǎn)的參數(shù)是json形式的字符串,我們還要這么一個(gè)函數(shù),能夠根據(jù)json字符串構(gòu)造出一個(gè)bundle
/**
* 根據(jù)page,從pageParams中獲得bundle
*
* @param corePage 頁面
* @return 頁面的參數(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)成了頁面管理類CorePageManager,我們對(duì)其應(yīng)用單例模式。
/**
* 跳轉(zhuǎn)頁面管理
*/
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的子類的onCreate方法中即可。
到這里為止,基本上我們一切已經(jīng)就緒了。就差如何切換了。這里,在CorePageManager類中再提供兩個(gè)核心函數(shù),用于處理頁面切換。
下面這個(gè)函數(shù)是頁面切換的核心函數(shù),首先根據(jù)參數(shù)從map中拿到對(duì)應(yīng)的實(shí)體類,假設(shè)存在這個(gè)頁面,通過class名用反射獲得該Fragment對(duì)象,調(diào)用前面寫好的創(chuàng)建Bundle的函數(shù)得到頁面參數(shù),并與當(dāng)前函數(shù)中的入?yún)undle進(jìn)行合并。將參數(shù)設(shè)置給fragment對(duì)象,開啟一個(gè)fragment事務(wù),查找id為fragment_container的fragment容器,如果該容器已經(jīng)有fragment,則隱藏它,如果該函數(shù)傳遞了動(dòng)畫參數(shù),則添加頁面切換動(dòng)畫,然后將反射獲得的fragment對(duì)象添加到該容器中,如果需要添加到返回棧,則調(diào)用addToBackStack,最后提交事務(wù)并返回該fragment對(duì)象。
整個(gè)函數(shù)很簡單,就是我們平常在activity中寫的切換fragment的代碼
/**
* 頁面跳轉(zhuǎn)核心函數(shù)之一
* 打開一個(gè)fragemnt
*
* @param fragmentManager FragmentManager管理類
* @param pageName 頁面名
* @param bundle 參數(shù)
* @param animations 動(dòng)畫類型
* @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ì)添加到該布局中去。我們的基類Activity也將使用這個(gè)布局,這個(gè)后續(xù)編寫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>
此外,我們再提供一個(gè)核心函數(shù)。就是如果返回棧中存在了目標(biāo)fragment,則將其彈出,否則新建fragment打開。
/**
* 頁面跳轉(zhuǎn)核心函數(shù)之一
* 打開一個(gè)Fragement,如果返回棧中有則出棧,否則新建
*
* @param fragmentManager FragmentManager管理類
* @param pageName 頁面別名
* @param bundle 參數(shù)
* @param animations 動(dòng)畫
* @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)注意到了頁面跳轉(zhuǎn)函數(shù)中用到了動(dòng)畫,其實(shí)這個(gè)動(dòng)畫是一個(gè)數(shù)組,為了方便使用,我們將其封裝為枚舉類,提供常見的幾種動(dòng)畫形式。
package cn.edu.zafu.corepage.core;
/**
* 頁面切換動(dòng)畫類別
*/
public enum CoreAnim {
none, /* 沒有動(dòng)畫 */
present, /*由下到上動(dòng)畫 */
slide,/* 從左到右動(dòng)畫 */
fade;/*漸變 */
}
之后我們還要根據(jù)該枚舉類獲得對(duì)應(yīng)的動(dòng)畫的xml文件。
/**
* 動(dòng)畫轉(zhuǎn)化,根據(jù)枚舉類返回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中的代碼,其他文件類似,這些文件都位于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" />
到了這里,如果你都明白了,那么后面基本上就沒有什么難度了,因?yàn)橹蟮墓δ芏际腔谝陨蟽?nèi)容。
前面我們定義了一個(gè)CorePage實(shí)體類用于保存配置文件中實(shí)體類的信息,而頁面切換過程中需要傳遞一些參數(shù),比如是否添加到fragment返回棧,是否在新的activity中打開fragment,頁面切換時(shí)的動(dòng)畫,傳遞的參數(shù)等等,通樣,我們將其封裝為實(shí)體類。由于該對(duì)象可能需要通過intent傳遞,這里我們將其實(shí)現(xiàn)Parcelable接口。實(shí)現(xiàn)該接口方法很簡單,假設(shè)使用的是android studio,使用快捷鍵alt+insert選擇Parcelable即可創(chuàng)建一個(gè)模板,我們將其補(bǔ)齊就好了。整個(gè)類如下,我們對(duì)外提供了多個(gè)重載的構(gòu)造函數(shù),其本質(zhì)都是一樣的,而前面的動(dòng)畫轉(zhuǎn)換函數(shù)我們將其放入這個(gè)類中。
/**
* 頁面跳轉(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;
//頁面名
private Bundle mBundle;
//相關(guān)數(shù)據(jù)
private int[] mAnim = null;
//動(dòng)畫類型
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)畫轉(zhuǎn)化,根據(jù)枚舉類返回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);
}
}
該類中的部分屬性有一些默認(rèn)值,比如是否添加到返回棧,是否起新Activity,我們默認(rèn)在當(dāng)前activity中打開fragment,并且添加到返回棧。有了這個(gè)類,之后的頁面切換都通過該實(shí)體類進(jìn)行傳參就可以了。
然后,我們定義一個(gè)接口,讓基類activity實(shí)現(xiàn)該接口,用于切換時(shí)的一些常用操作。fragment中調(diào)用宿主activity中該接口的方法即可。
/**
* 頁面跳轉(zhuǎn)接口,用于控制頁面跳轉(zhuǎn)或啟動(dòng)新的activity
*/
public interface CoreSwitcher {
/**
* 返回到前一個(gè)頁面(只有一個(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è)頁面。
*
* @param bean
* @return
*/
Fragment gotoPage(CoreSwitchBean bean);
/**
* 打開一個(gè)新的頁面
*
* @param bean
* @return
*/
Fragment openPage(CoreSwitchBean bean);
/**
* 移除當(dāng)前Acitivity不需要的fragment
*
* @param fragmentLists
*/
void removeUnlessFragment(List<String> fragmentLists);
/**
* 頁面跳轉(zhuǎn),支持跨Activity進(jìn)行傳遞數(shù)據(jù)
*
* @param page
* @param fragment
* @return
*/
public Fragment openPageForResult(final CoreSwitchBean page, final BaseFragment fragment);
}
到了這里,似乎已經(jīng)初具模型了,接下來,我們實(shí)現(xiàn)該接口。為了保證在子線程中也能調(diào)用這些方法,我們需要一個(gè)主線程的handler來幫我們完成一部分工作。假設(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的引用
/**
* 彈出頁面
*/
@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í)的頁面數(shù)據(jù),重寫改方法完成數(shù)據(jù)刷新
return true;
}
}
}
return false;
}
/**
* 根據(jù)Switchpage打開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打開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);
}
}
/**
* 移除無用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)頁面名
* @return
*/
protected String getPageName() {
BaseFragment frg = getActiveFragment();
if (frg != null) {
return frg.getPageName();
}
return "";
}
/**
* 打開fragment,并設(shè)置是否新開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);
}
/**
* 打開fragment,并設(shè)置是否新開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);
}
/**
* 打開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);
}
/**
* 打開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);
}
/**
* 打開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);
}
/**
* 打開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,否則父類處理
*/
@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)畫
*
* @param activity
* @param showAnimation
*/
private void finishActivity(BaseActivity activity, boolean showAnimation) {
if (activity != null) {
activity.finish();
}
if (showAnimation) {
//動(dòng)畫
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方法中完成一些初始化工作。
/**
* 頁面跳轉(zhuǎn)都通過BaseActivity 嵌套Fragment來實(shí)現(xiàn),動(dòng)態(tài)替換fragment只需要指定相應(yīng)的參數(shù)。 避免Activity 需要再manifest中注冊的問題。
* 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è),用于頁面切換
//所有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();
//處理新開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);
//處理新開activity跳轉(zhuǎn)
IntentFilter filter = new IntentFilter();
filter.addAction(Config.ACTION_EXIT_APP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
BaseApplication.getLocalBroadcastManager().registerReceiver(mExitReceiver, filter);
//注冊本地廣播,接收程序退出廣播
}
}
接下來就是處理基類BaseFragment的問題了,這里貼出該類所有代碼,具體請(qǐng)參考注釋。
public class BaseFragment extends Fragment {
private static final String TAG = BaseFragment.class.getSimpleName();
protected Activity mActivity;
//所在activity
private String mPageName;
//頁面名
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打開的頁面的返回結(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 頁面名
* @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");
}
}
/**
* 得到頁面切換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是否存在,通過Switcher查找
* @param pageName 頁面名
* @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是否位于棧頂,通過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中打開一個(gè)fragment,并添加到返回棧中
* @param pageName Fragemnt 名,在page.json中配置。
* @param bundle 頁面跳轉(zhuǎn)時(shí)傳遞的參數(shù)
* @param coreAnim 指定的動(dòng)畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫)
* @return
*/
public final Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim) {
return this.openPage(pageName, bundle, CoreSwitchBean.convertAnimations(coreAnim), true);
}
/**
* 在當(dāng)前activity中打開一個(gè)fragment,并設(shè)置是否添加到返回棧
* @param pageName Fragemnt 名,在page.json中配置。
* @param bundle 頁面跳轉(zhuǎn)時(shí)傳遞的參數(shù)
* @param anim 指定的動(dòng)畫農(nóng)林 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫)
* @param addToBackStack 是否添加到用戶操作棧中
* @return
*/
public final Fragment openPage(String pageName, Bundle bundle, int[] anim, boolean addToBackStack) {
return this.openPage(pageName, bundle, anim, addToBackStack, false);
}
/**
* 打開一個(gè)fragment并設(shè)置是否新開activity,設(shè)置是否添加返回棧
* @param pageName Fragemnt 名,在page.json中配置。
* @param bundle 頁面跳轉(zhuǎn)時(shí)傳遞的參數(shù)
* @param anim 指定的動(dòng)畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫)
* @param addToBackStack 是否添加到用戶操作棧中
* @param newActivity 該頁面是否新建一個(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中打開一個(gè)fragment,并添加到返回棧中
*
* @param pageName Fragemnt 名,在page.json中配置。
* @param bundle 頁面跳轉(zhuǎn)時(shí)傳遞的參數(shù)
* @param anim 指定的動(dòng)畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫)
* @return
*/
public final Fragment openPage(String pageName, Bundle bundle, int[] anim) {
return this.openPage(pageName, bundle, anim, true);
}
/**
* 在當(dāng)前activity中打開一個(gè)fragment,并設(shè)置是否添加到返回棧
*
* @param pageName Fragemnt 名,在page.json中配置。
* @param bundle 頁面跳轉(zhuǎn)時(shí)傳遞的參數(shù)
* @param coreAnim 指定的動(dòng)畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫)
* @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);
}
/**
* 打開一個(gè)fragment并設(shè)置是否新開activity,設(shè)置是否添加返回棧
* @param pageName Fragemnt 名,在page.json中配置。
* @param bundle 頁面跳轉(zhuǎn)時(shí)傳遞的參數(shù)
* @param coreAnim 指定的動(dòng)畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫)
* @param addToBackStack 是否添加到用戶操作棧中
* @param newActivity 該頁面是否新建一個(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è)頁面(Fragment)。找不到pageName Fragment時(shí),就新建Fragment。找到pageName
* Fragment時(shí),則彈出該Fragement到棧頂上的所有actvity和fragment
*
* @param pageName Fragemnt 名,在在configure.zip 的pageContext.txt中配置。
* @param bundle 頁面跳轉(zhuǎn)時(shí)傳遞的參數(shù)
* @param coreAnim 指定的動(dòng)畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動(dòng)畫)
* @param newActivity 該頁面是否新建一個(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;
}
}
/**
* 打開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);
}
/**
* 打開fragment并請(qǐng)求獲得返回值,并設(shè)置是否在新activity中打開
* @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;
}
//頁面跳轉(zhuǎn)接口
public interface OnFragmentFinishListener {
void onFragmentResult(int requestCode, int resultCode, Intent intent);
}
}
其實(shí)無論BaseActivity還是BaseFragment的原理都是一樣的,都是通過接口中的函數(shù)進(jìn)行切換,最終都是調(diào)用了我們前面所說的兩個(gè)核心函數(shù)。所謂的難點(diǎn),其實(shí)就是openPageForResult的函數(shù)。如果調(diào)整函數(shù)中指定了新開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編寫一個(gè)應(yīng)用程序類完成初始化。
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ì)變得特別簡單,調(diào)用即可。由于我是用android studio建的module,因此直接引用該module即可,然后提供一個(gè)程序入口Activity,該類繼承BaseActivity,將該Activity聲明在manifest文件中,然后我們再也不用新建Activity了,后續(xù)的頁面跳轉(zhuǎn)全都使用Fragment來完成。并且可以設(shè)置動(dòng)畫類型等一系列的參數(shù)。
使用非常簡單
openPage("test1",null, CoreAnim.slide);
//打開一個(gè)頁面,不傳遞參數(shù)第二個(gè)傳null,第三個(gè)參數(shù)為動(dòng)畫類型,此方法有重載方法,第四個(gè)參數(shù)表示是否添加到返回棧,第五個(gè)參數(shù)表示是否新開activity,一般情況下,只需傳遞前三個(gè)參數(shù)即可,而動(dòng)畫類型,處理傳遞枚舉類,還支持自定義的動(dòng)畫,對(duì)應(yīng)的文件參考res/anim目錄下的相應(yīng)文件
openPageForResult("test2",bundle,CoreAnim.fade,requestCode);
//打開一個(gè)頁面并獲得返回結(jié)果,之后調(diào)用setFragmentResult和popToBack設(shè)置結(jié)果
setFragmentResult(500, intent);
popToBack();
重寫onFragmentResult函數(shù)獲取返回結(jié)果
@Override
public void onFragmentResult(int requestCode, int resultCode, Intent data) {
}
//這個(gè)使用過程同startActivityFor的整個(gè)過程
效果圖:

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- Android基礎(chǔ)之使用Fragment控制切換多個(gè)頁面
- 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è)頁面切換效果
- Android中使用TabHost 與 Fragment 制作頁面切換效果
- Android使用TabLayou+fragment+viewpager實(shí)現(xiàn)滑動(dòng)切換頁面效果
- Android開發(fā)使用Activity嵌套多個(gè)Fragment實(shí)現(xiàn)橫豎屏切換功能的方法
- fragment實(shí)現(xiàn)隱藏及界面切換效果
相關(guān)文章
Android實(shí)現(xiàn)粒子中心擴(kuò)散動(dòng)畫效果
粒子動(dòng)畫效果相比其他動(dòng)畫來說是非常復(fù)雜了的,主要涉及三個(gè)方面,粒子初始化、粒子位移、粒子回收等問題,本篇將實(shí)現(xiàn)兩種動(dòng)畫效果,代碼基本相同,只是旋轉(zhuǎn)速度不一樣,需要的朋友可以參考下2024-02-02
Android Dialog對(duì)話框?qū)嵗a講解
本文通過實(shí)例代碼給大家介紹了Android Dialog對(duì)話框的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08
android實(shí)現(xiàn)通過NFC讀取卡號(hào)
這篇文章主要介紹了android實(shí)現(xiàn)通過NFC讀取卡號(hào),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Android實(shí)現(xiàn)自定義手勢和識(shí)別手勢的功能
這篇文章主要介紹了Android實(shí)現(xiàn)自定義手勢和識(shí)別手勢的功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10
Android實(shí)現(xiàn)Activities之間進(jìn)行數(shù)據(jù)傳遞的方法
這篇文章主要介紹了Android實(shí)現(xiàn)Activities之間進(jìn)行數(shù)據(jù)傳遞的方法,涉及Android中Activities的使用技巧,需要的朋友可以參考下2015-04-04
Android Canvas和Bitmap結(jié)合繪圖詳解流程
在 Android Canvas 上繪圖非常難,在繪圖時(shí)需要理解許多不同的類和概念。這篇文章中,將介紹 Android 框架中可用的一些類,它們可以讓畫布使用時(shí)更輕松2021-11-11
Android庫項(xiàng)目中的資源ID沖突的解決方法
本篇文章主要介紹了Android庫項(xiàng)目中的資源ID沖突的解決方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03

