共计 9936 个字符,预计需要花费 25 分钟才能阅读完成。
视频解说
视频链接 : 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 里的 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 空壳模版工程 先相熟一下集成和应用。
微信公众号
更多资讯内容,欢送扫描关注我的集体微信公众号!