Android使用Fragment打造萬能頁面切換框架

安卓弟的專欄發表於2016-01-04

首先我們來回憶一下傳統用Activity進行的頁面切換,activity之間切換,首先需要新建intent物件,給該物件設定一些必須的引數,然後呼叫startActivity方法進行頁面跳轉。如果需要activity返回結果,則呼叫startActivityForResult方法,在onActivityResult方法中獲得返回結果。此外,每一個要展示的activity需要在AndroidManifest.xml檔案中註冊。而且,如果在某些特定的情況下(比如65536方法數爆炸)要動態載入dex,還得手動管理activity的生命週期。那麼,有沒有這麼一種方法進行頁面切換時,無需在AndroidManifest.xml檔案中宣告這些資訊,動態載入時又無需我們管理生命週期,等等優點呢。

我們來回憶一下,在androiD3.0之後,谷歌出了一個Fragment,這個東西依賴於activity,其生命週期由宿主activity進行管理,並且可以通過FragmentManager和FragmentTransaction等相關的類進行管理。那麼我們能不能從Fragment入手,打造一個完全由Fragment組成的頁面跳轉框架呢。

使用Fragment其實很簡單,首先開啟一個事務,通過add,replace,remove等方法進行新增,替換,移除等操作,這一切的操作可能需要依賴一個容器,這個容器提供一個id,進行對應操作時將這個id作為引數傳入。之後通過相應方法提交事務就可以了,就像這樣子。

FragmentManager fragmentManager = getSupportFragmentManager();  
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();  
fragmentTransaction.replace(R.id.fragment_container, fragment);  
fragmentTransaction.commit();

然而我相信你一定有這樣的經歷,在使用Fragment進行頁面切換時又得不斷用程式碼控制其顯示與隱藏的邏輯,那麼有沒有這樣一種方法在程式中不斷複用這段程式碼呢?

首先,我們希望Fragment能像Activity那樣,進行正確的跳轉。那麼需要什麼,答案是Fragment物件,我們肯定需要它的Class全類名,當然跳轉的時候可能會帶上一些引數,這個引數應該通過Bundle進行傳遞。而且,全類名可能太長,不便記憶,我們參考web的架構,應該還要取一個別名alias。就這樣,一個Fragment頁面的三個基本屬性就被我們抽取出來了,組成了如下的實體類。在這個實體類中,頁面傳遞的引數為json形式的String字串物件,在需要使用的時候我們通過該json構造出bundle。頁面名變數mName是整個程式唯一標示該頁面的引數,其值唯一,但是其對應的class全類名可以不唯一,也就是說從name到class的對映可以一對多。

public class CorePage implements Serializable {
    private static final long serialVersionUID = 3736359137726536495L;

    private String mName;
    //頁面名
    private String mClazz;
    //頁面class
    private String mParams;
    //傳入引數,json object結構

    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 + '\'' +
                '}';
    }
}

實體類編寫好了,為了更方便的進行頁面跳轉,我們需要像Activity那樣,有一個配置檔案,裡面存著Fragment名到其全類名的對映關係。那麼這些資料存在哪呢。我們參考網路資料,一般從網路上獲取的資料有兩種格式,一種是json,一種是xml,json由於其優點,在網路傳輸中被大量使用,這裡,我們優先使用json,選定了json之後,就要選定一個json解析的框架,我們不使用android系統自帶的,我們使用阿里的fastjson,當然你也可以使用gson或者Jackson。我們的Fragment有很多,所以這個Fragment的配置檔案應該是一個json陣列。就像這個樣子

[
  {
    "name": "test1",
    "class": "cn.edu.zafu.corepage.sample.TestFragment1",
    "params": {
      "param1": "value1",
      "param2": "value2"
    }
  },
  {
    "name": "test2",
    "class": "cn.edu.zafu.corepage.sample.TestFragment2",
    "params": ""
  }
]

有了這個配置,我們就要在程式進入時讀取這個配置。我們將這個配置放在assets目錄下,當然你也可以放在其他目錄下,只有你能讀取到就行,甚至你可以放在壓縮包裡。然而,實際情況下,這個檔案不應該暴露,因為一旦暴露就存在風險。因此,大家可以採用更安全的方式存這些資料,比如把資料壓縮到壓縮包中,增加一個密碼,而讀取檔案的內容的程式碼我們移到native中取實現,畢竟java層太容易被反編譯了,而c/c++層相對來說會畢竟有難度。

這裡為了簡單,我們暫時放在assets目錄下,那麼要從assets目錄中讀取這個檔案內容就必須有這麼一個讀取該目錄下檔案的函式,該目錄在android中也算是一個比較特殊的目錄了,可以通過getAssets()函式,然後獲得一個輸入流,將檔案內容讀出,然後將對應的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;
    }

然後根據該檔案內容讀取json配置。讀取出來後需要將這些資料儲存下來。因此要有一個資料結構來儲存這個物件,存完之後還要方便取出,存取的依據應該是Fragment的表示,即前面提到的name,因此Map這個資料結構是最適合不過了。

private Map<String, CorePage> mPageMap = new HashMap<String, CorePage>();
//儲存page的map

將配置讀取出來存進該map,讀取的時候判斷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());
}

此外,除了從配置檔案中讀取,我們應該可以動態新增,對外提供這個函式。

    /**
     * 新增新頁面
     *
     * @param name 頁面名
     * @param clazz 頁面class
     * @param params 頁面引數
     * @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中得到引數的json格式
     *
     * @param params 頁面map形式引數
     * @return json格式引數
     */
    private String buildParams(Map<String, String> params) {
        if (params == null) {
            return "";
        }
        String result = JSON.toJSONString(params);
        Log.d(TAG, "params:" + result);
        return result;
    }

文章開頭已經說了,頁面跳轉的引數是json形式的字串,我們還要這麼一個函式,能夠根據json字串構造出一個bundle

/**
     * 根據page,從pageParams中獲得bundle
     *
     * @param corePage 頁面
     * @return 頁面的引數
     */
    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;
    }

以上配置讀取的一系列函式,構成了頁面管理類CorePageManager,我們對其應用單例模式。

/**
 * 跳轉頁面管理
 * User:lizhangqu(513163535@qq.com)
 * Date:2015-07-22
 * Time: 09:34
 */
public class CorePageManager {
    private volatile static CorePageManager mInstance = null;
    //單例
    private Context mContext;
    //Context上下文

    /**
     * 建構函式私有化
     */
    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函式暴露給程式入口,進行配置檔案的讀取。一般放在Application的子類的onCreate方法中即可。

到這裡為止,基本上我們一切已經就緒了。就差如何切換了。這裡,在CorePageManager類中再提供兩個核心函式,用於處理頁面切換。

下面這個函式是頁面切換的核心函式,首先根據引數從map中拿到對應的實體類,假設存在這個頁面,通過class名用反射獲得該Fragment物件,呼叫前面寫好的建立Bundle的函式得到頁面引數,並與當前函式中的入參bundle進行合併。將引數設定給fragment物件,開啟一個fragment事務,查詢id為fragment_container的fragment容器,如果該容器已經有fragment,則隱藏它,如果該函式傳遞了動畫引數,則新增頁面切換動畫,然後將反射獲得的fragment物件新增到該容器中,如果需要新增到返回棧,則呼叫addToBackStack,最後提交事務並返回該fragment物件。

整個函式很簡單,就是我們平常在activity中寫的切換fragment的程式碼

 /**
     * 頁面跳轉核心函式之一
     * 開啟一個fragemnt
     *
     * @param fragmentManager FragmentManager管理類
     * @param pageName  頁面名
     * @param bundle 引數
     * @param animations 動畫型別
     * @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;
    }

而上面這個函式中的id值在一個基礎的佈局中,之後的Fragment都會新增到該佈局中去。我們的基類Activity也將使用這個佈局,這個後續編寫BaseActivity的時候會提到

<?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>

此外,我們再提供一個核心函式。就是如果返回棧中存在了目標fragment,則將其彈出,否則新建fragment開啟。

 /**
     * 頁面跳轉核心函式之一
     * 開啟一個Fragement,如果返回棧中有則出棧,否則新建
     *
     * @param fragmentManager FragmentManager管理類
     * @param pageName 頁面別名
     * @param bundle 引數
     * @param animations 動畫
     * @return 成功跳轉到的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;

    }

細心的你可能已經注意到了頁面跳轉函式中用到了動畫,其實這個動畫是一個陣列,為了方便使用,我們將其封裝為列舉類,提供常見的幾種動畫形式。

package cn.edu.zafu.corepage.core;

/**
 * 頁面切換動畫類別
 * User:lizhangqu(513163535@qq.com)
 * Date:2015-07-22
 * Time: 09:42
 */
public enum CoreAnim {
    none, /* 沒有動畫 */
    present, /*由下到上動畫 */
    slide,/* 從左到右動畫 */
    fade;/*漸變 */
}

之後我們還要根據該列舉類獲得對應的動畫的xml檔案。

/**
     * 動畫轉化,根據列舉類返回int陣列
     *
     * @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;
    }

這裡貼出一個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" />

到了這裡,如果你都明白了,那麼後面基本上就沒有什麼難度了,因為之後的功能都是基於以上內容。

前面我們定義了一個CorePage實體類用於儲存配置檔案中實體類的資訊,而頁面切換過程中需要傳遞一些引數,比如是否新增到fragment返回棧,是否在新的activity中開啟fragment,頁面切換時的動畫,傳遞的引數等等,通樣,我們將其封裝為實體類。由於該物件可能需要通過intent傳遞,這裡我們將其實現Parcelable介面。實現該介面方法很簡單,假設使用的是android studio,使用快捷鍵alt+insert選擇Parcelable即可建立一個模板,我們將其補齊就好了。整個類如下,我們對外提供了多個過載的建構函式,其本質都是一樣的,而前面的動畫轉換函式我們將其放入這個類中。

/**
 * 頁面跳轉控制引數
 * User:lizhangqu(513163535@qq.com)
 * Date:2015-07-22
 * Time: 09:34
 */
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;
    //相關資料
    private int[] mAnim = null;
    //動畫型別
    private boolean mAddToBackStack = true;
    //是否新增到棧中
    private boolean mNewActivity = false;
    //是否起新的Activity
    private int requestCode = -1;

    //fragment跳轉
    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);
    }

    /**
     * 動畫轉化,根據列舉類返回int陣列
     *
     * @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);
    }

}

該類中的部分屬性有一些預設值,比如是否新增到返回棧,是否起新Activity,我們預設在當前activity中開啟fragment,並且新增到返回棧。有了這個類,之後的頁面切換都通過該實體類進行傳參就可以了。

然後,我們定義一個介面,讓基類activity實現該介面,用於切換時的一些常用操作。fragment中呼叫宿主activity中該介面的方法即可。

/**
 * 頁面跳轉介面,用於控制頁面跳轉或啟動新的activity
 * User:lizhangqu(513163535@qq.com)
 * Date:2015-07-22
 * Time: 09:34
 */
public interface CoreSwitcher {
    /**
     * 返回到前一個頁面(只有一個fragment時會關閉Activityt)
     */
    void popPage();

    /**
     * fragmentTag 是否在當前頂上activity上的最頂上的fragment
     *
     * @param fragmentTag
     * @return
     */
    boolean isFragmentTop(String fragmentTag);

    /**
     * 是否查詢到某個page
     *
     * @param pageName
     * @return
     */
    boolean findPage(final String pageName);

    /**
     * 跳轉到某一個頁面。
     *
     * @param bean
     * @return
     */
    Fragment gotoPage(CoreSwitchBean bean);

    /**
     * 開啟一個新的頁面
     *
     * @param bean
     * @return
     */
    Fragment openPage(CoreSwitchBean bean);

    /**
     * 移除當前Acitivity不需要的fragment
     *
     * @param fragmentLists
     */
    void removeUnlessFragment(List<String> fragmentLists);

    /**
     * 頁面跳轉,支援跨Activity進行傳遞資料
     *
     * @param page
     * @param fragment
     * @return
     */
    public Fragment openPageForResult(final CoreSwitchBean page, final BaseFragment fragment);

}

到了這裡,似乎已經初具模型了,接下來,我們實現該介面。為了保證在子執行緒中也能呼叫這些方法,我們需要一個主執行緒的handler來幫我們完成一部分工作。假設我們已經獲得了這個handler。具體細節看下面的程式碼實現吧,仔細閱讀以下不難理解的。

    private static List<WeakReference<BaseActivity>> mActivities = new ArrayList<WeakReference<BaseActivity>>();
    //所有activity的引用
    private Handler mHandler = null;
    //執行緒安全的handler
    private WeakReference<BaseActivity> mCurrentInstance = null;
    //當前activity的引用
    /**
     * 彈出頁面
     */
    @Override
    public void popPage() {
        popOrFinishActivity();
        //如果只有一個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重新整理資料,在onFragmentDataReset中回撥
     * @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;
    }

    /**
     * 當前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);//預設為空實現,用於舒心返回時的頁面資料,重寫改方法完成資料重新整理
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * 根據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());
        }
    }
/**
     * 根據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呼叫
     * @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發起的由fragment處理,否則預設處理
     * @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);
    }

除此之外,提供一些函式的過載便於呼叫以及一些工具函式。

    /**
     * 僅用於接受應用退出廣播,程式退出時有機會做一些必要的清理工作
     */
    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;
    }

    /**
     * 廣播退出時清理activity列表
     */
    public static void unInit() {
        if (mActivities != null) {
            mActivities.clear();
        }
    }

    /**
     * 獲得當前活動頁面名
     * @return
     */
    protected String getPageName() {
        BaseFragment frg = getActiveFragment();
        if (frg != null) {
            return frg.getPageName();
        }
        return "";
    }

    /**
     * 開啟fragment,並設定是否新開activity,設定是否新增到返回棧
     *
     * @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,並設定是否新開activity,設定是否新增到返回棧
     *
     * @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,並設定是否新增到返回棧
     *
     * @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,並設定是否新增到返回棧
     *
     * @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);
    }

    /**
     * 如果當前activity中只有一個activity,則關閉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;
        }
    }

    /**
     * 獲得當前活動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;
    }

    /**
     * 列印,除錯用
     */
    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());
                }
            }
        }
    }

    /**
     * 結束activity,設定是否顯示動畫
     *
     * @param activity
     * @param showAnimation
     */
    private void finishActivity(BaseActivity activity, boolean showAnimation) {
        if (activity != null) {
            activity.finish();
        }
        if (showAnimation) {
            //動畫
            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方法中完成一些初始化工作。

/**
 * 頁面跳轉都通過BaseActivity 巢狀Fragment來實現,動態替換fragment只需要指定相應的引數。 避免Activity 需要再manifest中註冊的問題。
 * 1.管理應用中所有BaseActivity 例項。 2.管理BaseActivity 例項和fragment的跳轉
 * User:lizhangqu(513163535@qq.com)
 * Date:2015-07-22
 * Time: 09:32
 */
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;//記錄首個,用於頁面切換
    //所有activity的引用
    private Handler mHandler = null;
    private WeakReference<BaseActivity> mCurrentInstance = null;
    //當前activity的引用
    private BaseFragment mFragmentForResult = null;
    //forResult 的fragment
    private int mFragmentRequestCode = -1;
    //請求碼,必須大於等於0
    /**
     * 僅用於接受應用退出廣播,程式退出時有機會做一些必要的清理工作
     */
    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);
            //恢復資料
            //需要用註解SaveWithActivity
        }
        mHandler = new Handler(getMainLooper());
        //獲得主執行緒handler
        mCurrentInstance = new WeakReference<BaseActivity>(this);
        //當前activity弱引用
        mActivities.add(mCurrentInstance);
        //當前activity增加到activity列表中
        printAllActivities();
        //列印所有activity情況

        init(mNewIntent);
        //處理新開activity跳轉
        IntentFilter filter = new IntentFilter();
        filter.addAction(Config.ACTION_EXIT_APP);
        filter.addCategory(Intent.CATEGORY_DEFAULT);
        BaseApplication.getLocalBroadcastManager().registerReceiver(mExitReceiver, filter);
        //註冊本地廣播,接收程式退出廣播
    }
}

接下來就是處理基類BaseFragment的問題了,這裡貼出該類所有程式碼,具體請參考註釋。

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介面,用於傳遞返回結果
    private OnFragmentFinishListener mFragmentFinishListener;

    /**
     * 設定該介面用於返回結果
     * @param listener OnFragmentFinishListener物件
     */
    public void setFragmentFinishListener(OnFragmentFinishListener listener) {
        this.mFragmentFinishListener = listener;
    }

    /**
     * 設定openPageForResult開啟的頁面的返回結果
     * @param resultCode 返回結果碼
     * @param intent 返回的intent物件
     */
    public void setFragmentResult(int resultCode, Intent intent) {
        if (mFragmentFinishListener != null) {
            mFragmentFinishListener.onFragmentResult(mRequestCode, resultCode, intent);
        }
    }

    /**
     * 得到requestCode
     * @return 請求碼
     */
    public int getRequestCode() {
        return this.mRequestCode;
    }

    /**
     * 設定requestCode
     * @param code 請求碼
     */
    public void setRequestCode(int code) {
        this.mRequestCode = code;
    }

    /**
     * 將Activity中onKeyDown在Fragment中實現,
     * @param keyCode
     * @param event
     * @return
     */
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return false;
    }

    /**
     * 資料設定,回撥
     * @param bundle
     */
    public void onFragmentDataReset(Bundle bundle) {

    }

    /**
     * 彈出棧頂的Fragment。如果Activity中只有一個Fragemnt時,Acitivity也退出。
     */
    public void popToBack() {
        this.popToBack(null, null);
    }

    /**
     * 如果在fragment棧中找到,則跳轉到該fragment中去,否則彈出棧頂
     * @param pageName 頁面名
     * @param bundle 引數
     */
    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) {// 加強保護,保證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;
        }

    }

    /**
     * 對應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;
        }
    }

    /**
     * 重新該方法用於獲得返回的資料
     * @param requestCode 請求碼
     * @param resultCode 返回結果碼
     * @param data 返回資料
     */
    public void onFragmentResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onFragmentResult from baseFragment:requestCode-" + requestCode + "  resultCode-" + resultCode);
    }

    /**
     * 在當前activity中開啟一個fragment,並新增到返回棧中
     * @param pageName Fragemnt 名,在page.json中配置。
     * @param bundle 頁面跳轉時傳遞的引數
     * @param coreAnim  指定的動畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動畫)
     * @return
     */
    public final Fragment openPage(String pageName, Bundle bundle, CoreAnim coreAnim) {
        return this.openPage(pageName, bundle, CoreSwitchBean.convertAnimations(coreAnim), true);
    }

    /**
     * 在當前activity中開啟一個fragment,並設定是否新增到返回棧
     * @param pageName Fragemnt 名,在page.json中配置。
     * @param bundle 頁面跳轉時傳遞的引數
     * @param anim 指定的動畫農林 none/slide(左右平移)/present(由下向上)/fade(fade 動畫)
     * @param addToBackStack 是否新增到使用者操作棧中
     * @return
     */
    public final Fragment openPage(String pageName, Bundle bundle, int[] anim, boolean addToBackStack) {
        return this.openPage(pageName, bundle, anim, addToBackStack, false);
    }

    /**
     * 開啟一個fragment並設定是否新開activity,設定是否新增返回棧
     * @param pageName Fragemnt 名,在page.json中配置。
     * @param bundle 頁面跳轉時傳遞的引數
     * @param anim 指定的動畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動畫)
     * @param addToBackStack  是否新增到使用者操作棧中
     * @param newActivity 該頁面是否新建一個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;
        }
    }

    /**
     * 在當前activity中開啟一個fragment,並新增到返回棧中
     *
     * @param pageName Fragemnt 名,在page.json中配置。
     * @param bundle   頁面跳轉時傳遞的引數
     * @param anim     指定的動畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動畫)
     * @return
     */
    public final Fragment openPage(String pageName, Bundle bundle, int[] anim) {
        return this.openPage(pageName, bundle, anim, true);
    }

    /**
     * 在當前activity中開啟一個fragment,並設定是否新增到返回棧
     *
     * @param pageName       Fragemnt 名,在page.json中配置。
     * @param bundle         頁面跳轉時傳遞的引數
     * @param coreAnim       指定的動畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動畫)
     * @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);
    }

    /**
     * 開啟一個fragment並設定是否新開activity,設定是否新增返回棧
     * @param pageName Fragemnt 名,在page.json中配置。
     * @param bundle 頁面跳轉時傳遞的引數
     * @param coreAnim 指定的動畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動畫)
     * @param addToBackStack  是否新增到使用者操作棧中
     * @param newActivity 該頁面是否新建一個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);

    }

    /**
     * 新建或跳轉到一個頁面(Fragment)。找不到pageName Fragment時,就新建Fragment。找到pageName
     * Fragment時,則彈出該Fragement到棧頂上的所有actvity和fragment
     *
     * @param pageName    Fragemnt 名,在在configure.zip 的pageContext.txt中配置。
     * @param bundle      頁面跳轉時傳遞的引數
     * @param coreAnim        指定的動畫理性 none/slide(左右平移)/present(由下向上)/fade(fade 動畫)
     * @param newActivity 該頁面是否新建一個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並請求獲得返回值
     * @param pageName
     * @param bundle
     * @param coreAnim
     * @param requestCode 請求碼
     * @return
     */
    public final Fragment openPageForResult(String pageName, Bundle bundle, CoreAnim coreAnim, int requestCode) {
        return this.openPageForResult(false, pageName, bundle, coreAnim, requestCode);
    }

    /**
     * 開啟fragment並請求獲得返回值,並設定是否在新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;
    }

    //頁面跳轉介面
    public interface OnFragmentFinishListener {
        void onFragmentResult(int requestCode, int resultCode, Intent intent);
    }

}

其實無論BaseActivity還是BaseFragment的原理都是一樣的,都是通過介面中的函式進行切換,最終都是呼叫了我們前面所說的兩個核心函式。所謂的難點,其實就是openPageForResult的函式。如果調整函式中指定了新開activity,則直接呼叫startActivityForResult函式進行跳轉,目標Activity中為BaseActivity,傳遞一些引數用於識別。

Intent intent = new Intent(this, BaseActivity.class);
intent.putExtra("SwitchBean", page);
intent.putExtra("startActivityForResult", "true");

然後再onCreate中獲得intent

 Intent mNewIntent = getIntent();
 init(mNewIntent);

呼叫了init函式,在裡面獲得傳遞的兩個引數,如果是startActivityForResult,則對fragment設定回撥函式,當我們手動設定了setFragmentResult函式後回撥就會被呼叫,即onFragmentResult函式回撥

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();
        }
    }

最後的最後,也就是整個Fragment跳轉框架的初始化了,繼承Application編寫一個應用程式類完成初始化。

/**
 *
 * User:lizhangqu(513163535@qq.com)
 * Date:2015-07-22
 * Time: 09:35
 */
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);
    }

    /**
     * 傳送本地廣播退出程式
     */
    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>

之後的一切就會變得特別簡單,呼叫即可。由於我是用android studio建的module,因此直接引用該module即可,然後提供一個程式入口Activity,該類繼承BaseActivity,將該Activity宣告在manifest檔案中,然後我們再也不用新建Activity了,後續的頁面跳轉全都使用Fragment來完成。並且可以設定動畫型別等一系列的引數。

使用非常簡單

openPage("test1",null, CoreAnim.slide);
//開啟一個頁面,不傳遞引數第二個傳null,第三個引數為動畫型別,此方法有過載方法,第四個參數列示是否新增到返回棧,第五個參數列示是否新開activity,一般情況下,只需傳遞前三個引數即可,而動畫型別,處理傳遞列舉類,還支援自定義的動畫,對應的檔案參考res/anim目錄下的相應檔案
openPageForResult("test2",bundle,CoreAnim.fade,requestCode);
//開啟一個頁面並獲得返回結果,之後呼叫setFragmentResult和popToBack設定結果
 setFragmentResult(500, intent);
 popToBack();
重寫onFragmentResult函式獲取返回結果
@Override
public void onFragmentResult(int requestCode, int resultCode, Intent data) {
}

//這個使用過程同startActivityFor的整個過程。

來一張樣例動圖。

這裡寫圖片描述

專案程式碼,肯定有bug,歡迎fork修改  https://github.com/lizhangqu/CorePage

相關文章