Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android使用Fragment打造萬能頁面切換框架

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

編輯:關於Android編程

首先我們來回憶一下傳統用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,我們對其應用單例模式。


/**
 * 跳轉頁面管理
 */
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;

/**
 * 頁面切換動畫類別
 */
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即可創建一個模板,我們將其補齊就好了。整個類如下,我們對外提供了多個重載的構造函數,其本質都是一樣的,而前面的動畫轉換函數我們將其放入這個類中。

/**
 * 頁面跳轉控制參數
 */
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
 */
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的跳轉
 */
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編寫一個應用程序類完成初始化。

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的整個過程

效果圖:

以上就是本文的全部內容,希望對大家的學習有所幫助。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved