视频解说

视频链接 : https://www.bilibili.com/video/BV1eD4y1R73U

简介

XPage是一个十分不便的fragment页面框架!天下文治,唯快不破,XPage最大的特点就是快,进步开发的效率!

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的切换展现和数据交互,只须要一行代码即可实现所有的操作,还不须要本人手动去注册,能够一键生成。

设计思路

刚开始的时候真的很难,没有什么好的思路,最后只是简略封装了一个Activity,通过传入一些key值从而获取并加载对应的fragment,相似ARouter中Fragment发现那种。其实这样做并没有解决一个容器的问题,而且页面切换也不是很灵便,不够通用,应用起来也不是很不便。

忽然有一天我发现Github上有个开源我的项目CorePage写得十分好,完满地解决了我对一个Activity容器的问题,于是我决定认真钻研其代码,并在其根底上设计出了XPage的最后版本。

就在XPage正式投入使用的过程中,我发现还是存在不少问题的:

  • 1.对外API不够灵便,应用起来不够不便;
  • 2.每个Fragment仍须要手动注册,很麻烦;

对于API不够灵便的问题,我在之后的版本中陆续通过结构者模式设计以及Android主题属性等伎俩解决了。

而对于手动注册的问题,我正是借鉴了ARouter的思路,通过Android APT技术,从而实现了Fragment信息的主动注册。

解决痛点

  • 只须要一个Activity容器就能够实现多个页面的交互。
  • Fragment自在切换和数据交互。
  • 无需在manifest中注册一堆Activity信息,通过@Page注解一键主动注册。

集成指南

增加Gradle依赖

1.在我的项目根目录的build.gradle的 repositories 增加jitpack仓库

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

2.在dependencies增加援用

以下是版本阐明,抉择一个即可。

  • 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里的setResultIntent intent = new Intent();intent.putExtra(KEY_BACK_DATA, "==【返回的数据】==");setFragmentResult(500, intent);// 返回操作popToBack();
  • 3.重写Fragment的onFragmentResult办法来接管返回的数据,相似Activity里的onActivityResult办法。
@Overridepublic 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注解的属性表:

属性名类型默认值备注
nameString注解类的类名页面的名称、惟一标识符
paramsString[]{""}动态设置默认参数
animCoreAnimCoreAnim.slide页面转场动画
extraint-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办法须要被笼罩。
@Overrideprotected 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

@Overrideprotected 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空壳模版工程 先相熟一下集成和应用。

微信公众号

更多资讯内容,欢送扫描关注我的集体微信公众号!