XPage頁面框架使用指南

視頻講解

視頻連接 : https://www.bilibili.com/video/BV1eD4y1R73Ujava

簡介

XPage是一個很是方便的fragment頁面框架!天下武功,惟快不破,XPage最大的特色就是快,提升開發的效率!android

XPage設計的初衷是但願能作一個通用的Activity做爲殼,Fragment做爲頁面填充展現,而且可以像Activity那樣自由的切換和數據交互。

特徵

  • 支持assets下「corepage.json」靜態配置Fragment頁面信息。
  • 支持Application中動態配置Fragment頁面信息。
  • 支持經過註解@Page的方式動態自動配置頁面信息。
  • 支持自定義Fragment頁面信息配置。
  • 支持4種默認Fragment頁面切換動畫。
  • 支持Fragment頁面間參數傳遞。
  • 支持Fragment頁面屬性保存。
  • 支持Fragment頁面的onKeyDown、onFragmentResult等生命週期
  • 支持Fragment和Fragment頁面自由跳轉以及數據交互。
  • 支持導航欄經過註解的方式自動添加及設置。
  • 支持進行內存泄露監測。
  • 支持自定義TitleBar全局主題屬性。
  • 支持自定義Fragment頁面容器。
  • 支持自定義Activity頁面容器。
  • 支持Fragment之間、activity和fragment之間的數據交互。
  • 兼容kotlin和androidx。

設計起因

當初作Android開發時每當我寫一個頁面,都須要建立一個Activity,而且還須要在manifest中註冊一堆Activity信息,這樣既不方便,並且對資源的開銷也比較大。所以當時我就設想可否創造出一個通用萬能的Activity容器,能夠全權負責Fragment的切換展現和數據交互,只須要一行代碼便可完成全部的操做,還不須要本身手動去註冊,能夠一鍵生成。git

設計思路

剛開始的時候真的很難,沒有什麼好的思路,最初只是簡單封裝了一個Activity,經過傳入一些key值從而獲取並加載對應的fragment,相似ARouter中Fragment發現那種。其實這樣作並無解決一個容器的問題,並且頁面切換也不是很靈活,不夠通用,使用起來也不是很方便。github

忽然有一天我發現Github上有個開源項目CorePage寫得很是好,完美地解決了我對一個Activity容器的問題,因而我決定仔細研究其代碼,並在其基礎上設計出了XPage的最第一版本。json

就在XPage正式投入使用的過程當中,我發現仍是存在很多問題的:微信

  • 1.對外API不夠靈活,使用起來不夠方便;
  • 2.每一個Fragment仍須要手動註冊,很麻煩;

對於API不夠靈活的問題,我在以後的版本中陸續經過構造者模式設計以及Android主題屬性等手段解決了。app

而對於手動註冊的問題,我正是借鑑了ARouter的思路,經過Android APT技術,從而實現了Fragment信息的自動註冊。框架

解決痛點

  • 只須要一個Activity容器就能夠實現多個頁面的交互。
  • Fragment自由切換和數據交互。
  • 無需在manifest中註冊一堆Activity信息,經過@Page註解一鍵自動註冊。

集成指南

添加Gradle依賴

1.在項目根目錄的build.gradle的 repositories 添加jitpack倉庫dom

allprojects {
     repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

2.在dependencies添加引用maven

如下是版本說明,選擇一個便可。

  • androidx版本:3.0.0及以上
dependencies {
  ...
  // XPage
  implementation 'com.github.xuexiangjys.XPage:xpage-lib:3.0.0'
  annotationProcessor 'com.github.xuexiangjys.XPage:xpage-compiler:3.0.0'
  // ButterKnife的sdk
  implementation 'com.jakewharton:butterknife:10.1.0'
  annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
}
  • support版本:2.3.0及如下
dependencies {
  ...
  // XPage
  implementation 'com.github.xuexiangjys.XPage:xpage-lib:2.3.0'
  annotationProcessor 'com.github.xuexiangjys.XPage:xpage-compiler:2.3.0'
  // ButterKnife的sdk
  implementation 'com.jakewharton:butterknife:8.4.0'
  annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}

【注意】若是你使用的是kotlin,請使用以下配置:

apply plugin: 'kotlin-kapt'

dependencies {
  ...
  //XPage
  implementation 'com.github.xuexiangjys.XPage:xpage-lib:3.0.0'
  kapt 'com.github.xuexiangjys.XPage:xpage-compiler:3.0.0'
  //ButterKnife的sdk
  implementation 'com.jakewharton:butterknife:10.1.0'
  kapt 'com.jakewharton:butterknife-compiler:10.1.0'
}

3.進行moduleName註冊(非必要)

defaultConfig {
    ...

    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [ moduleName : project.getName() ]
        }
    }
}

【注意】:若是不註冊的話,默認ModuleName爲app


頁面註冊

方法一:Application中動態註冊【推薦】

1.自動進行頁面註冊【推薦】

使用apt編譯時自動生成的頁面註冊配置類 "moduleName"+PageConfig 的getPages()進行註冊。

PageConfig.getInstance()
        .setPageConfiguration(new PageConfiguration() { //頁面註冊
            @Override
            public List<PageInfo> registerPages(Context context) {
                //自動註冊頁面,是編譯時自動生成的,build一下就出來了。若是你還沒使用@Page的話,暫時是不會生成的。
                return AppPageConfig.getInstance().getPages(); //自動註冊頁面
            }
        })
        .debug("PageLog")       //開啓調試
        .setContainActivityClazz(XPageActivity.class) //設置默認的容器Activity
        .enableWatcher(false)   //設置是否開啓內存泄露監測
        .init(this);            //初始化頁面配置

【注意】:若是你的項目中只是增長了依賴,尚未使用@Page註解XPageFragment頁面的話,在編譯時是不會自動生成註冊頁面的!!

2.手動動態進行頁面註冊

PageConfig.getInstance()
        .setPageConfiguration(new PageConfiguration() { //頁面註冊
            @Override
            public List<PageInfo> registerPages(Context context) {
                List<PageInfo> pageInfos = new ArrayList<>();
                addPageInfoAndSubPages(pageInfos, MainFragment.class);
                pageInfos.add(PageConfig.getPageInfo(DateReceiveFragment.class));
                return pageInfos;        //手動註冊頁面
            }
        })
        .debug("PageLog")       //開啓調試
        .enableWatcher(false)   //設置是否開啓內存泄露監測
        .init(this);            //初始化頁面配置

方法二:assets中靜態註冊

在assets文件夾中新建「corepage.json「,而後進行以下配置:

[
  {
    "name": "測試頁面1",
    "classPath": "com.xuexiang.xpagedemo.fragment.TestFragment1",
    "params": ""
  },
  {
    "name": "測試頁面2",
    "classPath": "com.xuexiang.xpagedemo.fragment.TestFragment2",
    "params": {
      "key1":"這是參數1的值",
      "key2":"這是參數2的值"
    }
  },
]

混淆配置

# fastjson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *; }
-keepattributes Signature

# xpage
-keep class com.xuexiang.xpage.annotation.** { *; }

基礎使用

頁面跳轉

使用XPage,Activity必需要繼承 XPageActivity,Fragment必需要繼承 XPageFragment,不然將沒法調用頁面跳轉的 openPage方法。

頁面的簡單打開和關閉

  • 使用openPage便可打開頁面,入參可爲跳轉類的類名,也能夠是類的標識(@Page標註的name字段)
// 使用類名打開
openPage(TestFragment.class);
// 使用標識打開
openPage("TestFragment");
  • 使用popToBack便可關閉頁面。
// 關閉當前頁,返回上一頁
popToBack();
// 關閉當前頁並跳轉至某個頁面
popToBack("popBackName", null);

頁面打開等待結果返回

  • 1.使用openPageForResult便可,相似Activity裏的startActivityForResult
openPageForResult(TestFragment.class, null, REQUEST_CODE);
  • 2.使用setFragmentResult來設置頁面關閉的返回值,相似Activity裏的setResult方法。
// 設置返回的數據,相似Activity裏的setResult
Intent intent = new Intent();
intent.putExtra(KEY_BACK_DATA, "==【返回的數據】==");
setFragmentResult(500, intent);
// 返回操做
popToBack();
  • 3.重寫Fragment的onFragmentResult方法來接收返回的數據,相似Activity裏的onActivityResult方法。
@Override
public void onFragmentResult(int requestCode, int resultCode, Intent data) {
    super.onFragmentResult(requestCode, resultCode, data);
    if (data != null) {
        Bundle extras = data.getExtras();
        XToastUtils.toast("requestCode:" + requestCode + " resultCode:" + resultCode + " data:" + extras.getString(TestFragment.KEY_BACK_DATA));
    }
}

數據傳遞

  • 使用openPage打開頁面時,可傳入Bundle做爲參數。
// 設置須要傳遞的參數
Bundle params = new Bundle();
params.putBoolean(DateReceiveFragment.KEY_IS_NEED_BACK, false);
int id = (int) (Math.random() * 100);
params.putString(DateReceiveFragment.KEY_EVENT_NAME, "事件" + id);
params.putString(DateReceiveFragment.KEY_EVENT_DATA, "事件" + id + "攜帶的數據");
// 把參數傳入
openPage(DateReceiveFragment.class, params);
  • 數據接收

數據接收和普通Fragment接收數據同樣,使用getArguments獲取傳入的數據。

Bundle arguments = getArguments();
String eventName = arguments.getString(DateReceiveFragment.KEY_EVENT_NAME);
String eventData = arguments.getString(DateReceiveFragment.KEY_EVENT_DATA);

頁面轉場動畫

頁面轉場動畫能夠動態設置,也能夠靜態設置。

靜態設置

在咱們使用@Page進行頁面註冊的時候,咱們能夠靜態設置轉場動畫、默認參數、拓展字段等。

Page註解的屬性表:

屬性名 類型 默認值 備註
name String 註解類的類名 頁面的名稱、惟一標識符
params String[] {""} 靜態設置默認參數
anim CoreAnim CoreAnim.slide 頁面轉場動畫
extra int -1 拓展字段

動態設置

使用openPage打開頁面時,可傳入CoreAnim枚舉設置頁面轉場動畫。

switch(position) {
    case 0:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.none);//沒有動畫
        break;
    case 1:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.present);//由下到上動畫
        break;
    case 2:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.slide);//從左到右動畫
        break;
    case 3:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.fade);//漸變
        break;
    case 4:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.zoom);//放大
        break;
    default:
        break;
}

設置自定義轉場動畫

//自定義動畫
openPage(TestFragment.PAGE_NAME, null, new int[]{
        // OpenEnterAnimation, 頁面打開進場動畫
        R.anim.custom_open_enter,
        // OpenExitAnimation, 頁面打開退場動畫
        R.anim.custom_open_exit,

        // CloseEnterAnimation, 頁面關閉進場動畫
        R.anim.custom_close_enter,
        // CloseExitAnimation, 頁面關閉退場動畫
        R.anim.custom_close_exit
});

進階使用

使用PageOption進行頁面操做【推薦】

使用PageOption.to進行頁面選項構建。

  • setAnim: 設置頁面轉場動畫
  • setRequestCode: 設置頁面打開的請求碼,用於返回結果
  • setAddToBackStack: 設置是否加入堆棧
  • setNewActivity: 設置是否使用新的Activity打開
  • setContainActivityClazz:設置新打開Activity的容器
  • putBoolean、putString、putAll等:設置傳遞的參數
  • open:打開頁面進行跳轉
PageOption.to(TestFragment.class) //跳轉的fragment
    .setAnim(CoreAnim.zoom) //頁面轉場動畫
    .setRequestCode(100) //請求碼,用於返回結果
    .setAddToBackStack(true) //是否加入堆棧
    .setNewActivity(true, ContainActivity.class) //是否使用新的Activity打開
    .putBoolean(DateReceiveFragment.KEY_IS_NEED_BACK, true) //傳遞的參數
    .open(this); //打開頁面進行跳轉

自定義TitleBar樣式

能夠設置XPageTitleBarStyle主題樣式來自定義標題欄的默認樣式。

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/xpage_default_actionbar_color</item>
    <item name="colorPrimaryDark">@color/xpage_default_actionbar_color</item>
    <item name="colorAccent">@color/xpage_default_actionbar_color</item>

    <!--標題欄的背景圖片,優先使用背景圖片,沒有背景圖片才使用背景顏色,可選-->
    <item name="xpage_actionbar_background">@null</item>
    <!--標題欄的背景顏色-->
    <item name="xpage_actionbar_color">@color/xpage_default_actionbar_color</item>
    <!--是否支持沉浸式標題欄, 默認false-->
    <item name="xpage_actionbar_immersive">false</item>
    <!--標題欄返回箭頭, 默認R.drawable.xpage_ic_navigation_back_white-->
    <item name="xpage_actionbar_navigation_back">@drawable/xpage_ic_navigation_back_white</item>
    <!--標題欄的高度,默認52dp-->
    <item name="xpage_actionbar_height">60dp</item>
    <!--標題欄標題文字的大小,默認18sp-->
    <item name="xpage_actionbar_title_text_size">21sp</item>
    <!--標題欄副標題文字的大小,默認12sp-->
    <item name="xpage_actionbar_sub_text_size">14sp</item>
    <!--標題欄動做文字的大小,默認15sp-->
    <item name="xpage_actionbar_action_text_size">18sp</item>
    <!--標題欄動做圖片的padding,默認5dp-->
    <item name="xpage_actionbar_action_padding">6dp</item>
    <!--標題欄兩側文字的padding,默認14dp-->
    <item name="xpage_actionbar_side_text_padding">16dp</item>
    
    <item name="XPageTitleBarStyle">@style/XPageTitleBar.Custom</item>
</style>

<style name="XPageTitleBar.Custom">
    <item name="tb_immersive">false</item>
    <item name="tb_centerGravity">center</item>
</style>

利用XPage來寫程序的Tab主頁

詳細可參見BottomNavigationViewFragment

就像正常使用ViewPager加載Fragment那樣。可是這裏須要注意的兩點是:

  • 因爲使用ViewPager進行加載,而非XPage,所以Fragment的initTitleBar方法須要被覆蓋。
@Override
protected TitleBar initTitleBar() {
    //不使用@Page標註的必定要注意覆蓋這個方法
    return null;
}
  • 因爲爲了新開頁面不影響Tab主頁當前容器的狀態,須要在打開新頁面的使用設置使用新容器。
PageOption.to(TestFragment.class)
        //新建一個容器,以不影響當前容器
        .setNewActivity(true)
        .open(this);

複雜Activity界面容器的自定義

詳細可參見ComplexActivity

1.自定義頁面容器的佈局,在佈局中必定要包含idfragment_container

<FrameLayout
    android:id="@id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="400dp">
</FrameLayout>

2.在XPageActivity中設置頁面容器的佈局ID

@Override
protected int getLayoutId() {
    return R.layout.activity_complex;
}

3.使用changePage方法切換Fragment。

changePage(TestFragment.PAGE_NAME, null, CoreAnim.none);

【注意】在切換Fragment的時候,fragment並不會走onResume和onPause生命週期,建議使用onHiddenChanged代替。

4.使用getPage方法獲取指定的Fragment,就能夠獲取該fragment頁面中的數據。

TabAFragment tabAFragment = getPage(TabAFragment.class);
if (tabAFragment != null) {
    ToastUtils.toast(tabAFragment.getData());
} else {
    ToastUtils.toast("頁面還未加載!");
}

常見問題

1.問:我使用的是自動註冊,爲何我剛接入的時候,一直報錯找不到AppPageConfig?

答:首先須要明確的是,AppPageConfig是須要編譯以後纔會出現的,若是你沒有編譯的話,是確定沒有的。若是你編譯了仍是找不到,你能夠根據以下步驟依次進行排查:

  • 排查當前項目中是否有Fragment被@Page註解了,若是沒有的話,即便編譯了也是不會生成AppPageConfig文件的。
  • 排查是否進行了moduleName註冊,由於自動生成的註冊類是根據"moduleName"+PageConfig的規則進行自動生成的,若是沒有配置moduleName的話,默認纔是app,這樣自動生成的註冊類纔是AppPageConfig。若是你配置了moduleName,並且模塊的名稱也不是app,那麼自動生成的註冊類確定不是AppPageConfig
  • 查看編譯時是否有其餘報錯,若是在編譯的過程當中就報錯了,那麼做爲apt這種編譯時自動生成的註冊類也是沒法生成的。
  • 若是以上都沒能解決你的問題,那麼考慮八成是你哪裏集成出錯了,因此須要你回頭從新閱讀集成指南,不能放過每個細節。若是仍是不行,考慮直接使用簡化版的Android空殼模版工程 先熟悉一下集成和使用。

微信公衆號

更多資訊內容,歡迎掃描關注個人我的微信公衆號!
在這裏插入圖片描述
相關文章
相關標籤/搜索