# ENavigation
**Repository Path**: Rickyal/ENavigation
## Basic Information
- **Project Name**: ENavigation
- **Description**: 一个使用kotlin封装的路由框架,适用于组件化开发场景,目前支持组件自动注册,拦截器,子线程跳转,跳转动画等功能。
- **Primary Language**: Kotlin
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-10-01
- **Last Updated**: 2021-10-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# ENavigation
一个使用kotlin封装的路由框架,适用于组件化开发场景,目前支持路由自动注册,拦截器,子线程跳转,跳转动画等功能。
ENavigaiton可以用来跳转Activity,内外部Scheme,系统界面等等。
### 跳转流程

# 解决场景
组件化开发时,需要跳转到指定的Activity,但是很多时候,目标Activity类又是不可见的,不能直接通过startActivity跳转,ENavigation便是解决这一场景的方案。另外,很多时候我们访问一个页面,需要先判断用户是否有访问该页面的权限,此时通常的做法就是在跳转之前做一个权限判断,但是如此这般,项目中就会多出很多重复的权限判断代码,使用ENavigation的拦截器便能很好的解决这一问题,只需要定义一个拦截器类即可。
# 依赖配置
首先需要在使用到ENavigation的模块的build.gradle文件中配置host:
```groovy
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'com.ricky.enavigation.plugin'
}
android {
compileSdk 30
...
}
kapt {
arguments {
arg("host", "app")
}
}
dependencies {
...
// 核心库
implementation project(path: ':enavigation_impl')
kapt project(path: ':enavigation_complier')
}
```
# 使用说明
## 一、初始化
使用ENavigation前请务必先初始化,建议初始化代码放在Application中,初始化方式:
```kotlin
class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
// 自动注册组件,注意需要在app模块添加自动注册插件
ENavigation.init(this)
// 手动注册组件,需要传入每个模块的build.gradle中配置的host
ENavigation.init(this,"base","app","module1","module1")
}
}
```
自动注册和手动注册选择其中一个即可,注意如果要使用自动注册,需要在app模块的build.gradle文件中apply自动注册插件:
```groovy
plugins {
id 'com.ricky.enavigation.plugin'
}
// 或者
apply plugin: 'com.ricky.enavigation.plugin'
```
如果报找不到插件,请检查项目的build.gradle中是否添加了插件依赖:
```groovy
buildscript {
...
dependencies {
classpath "com.ricky.enavigation.plugin:ENavigationPlugin:$lateast_plugin_version"
}
...
}
```
## 二、基础跳转
使用路由跳转时需要在目标Activity上添加@HostAndPathAnno注解:
```kotlin
@HostAndPathAnno("app/test")
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test1)
}
}
```
这里的***app/test1***即是TestActivity的路由地址,之后便可通过这个地址跳转到TestActivity。
注意第一个单词代表host,必须与Activity所在模块的build.gradle中配置的host一致,第二个单词可根据Activity用途随便配置。
### 同模块跳转
MainActivity跳转到TestActivity,两个Activity都在app模块。
```kotlin
ENavigation.with(activity)
.setHostAndPath(“app/test”)
.navigate()
```
### 跨模块跳转
* 目标Activity可见
MainActivity跳转到TestActivity1,TestActivity1在module1模块,app模块引用module1模块,因此TestActivity1类对MainActivity可见。
```kotlin
ENavigation.with(activity)
.setHostAndPath(“app/test1”)
.navigate()
```
* 目标Activity不可见
MainActivity跳转到TestActivity2,TestActivity2在module2模块,app模块引用base和module2模块,TestActivity2类对base模块不可见,在base模块中执行跳转。
```kotlin
ENavigation.with(activity)
.setHostAndPath(“app/test1”)
.navigate()
```
### 子线程跳转
因为ENavigation真正执行跳转是在拦截器中执行的,而拦截器又会转移到UI线程执行,所以在子线程中也能实现跳转界面。
```kotlin
Thread{
ENavigation.with(activity)
.setHostAndPath(“app/test1”)
.navigate()
}.start()
```
但是通常在子线程中是拿不到或者说不好拿activity的,此时不传入activity,也能实现跳转:
```kotlin
Thread{
ENavigation.with()
.setHostAndPath(“app/test1”)
.navigate()
}.start()
```
### Scheme跳转
直接通过setScheme方法设置跳转的scheme即可
```kotlin
// 内部scheme
ENavigation.with(activity)
.setScheme("enavigation://test")
.navigate()
// 外部scheme
ENavigation.with(activity)
.setScheme("taobao://item?id=97896794126846128")
.navigate()
```
支持内部app和外部app的scheme。
### 系统界面跳转
这里举个跳转系统相册选照片的例子
```kotlin
ENavigation.with(activity)
.setAction(Intent.ACTION_GET_CONTENT)
.setType("image/*")
.onResult { _, _, data ->
// 通过onResult回调取到返回的值,这里下面会讲到
Toast.makeText(this, "uri=${data?.data}", Toast.LENGTH_SHORT).show()
}
.navigate()
```
直接设置action和type即可,ENavigation还封装了其它诸如setData,setFlags,setCategories等方法,其作用于内部的intent,在跳转时传入startActivity方法。
### 跳转监听
ENavigation支持跳转前后的监听,方便执行相关操作。
```kotlin
ENavigation.with(activity)
.setHostAndPath(PathConfig.Module1.Test4.PATH)
.beforeAction { activity ->
// 跳转页面之前
}
.afterAction { activity ->
// 跳转页面之后
}
.navigate()
```
注意目前这两个回调都是在UI线程中执行的。
## 三、参数传递
### 参数传入
ENavigation封装了一系列设置传入参数的方法,其都作用于内部的intent,具体方法可参考系统地Intent类。这里举个跳转页面传字符串的例子。
```kotlin
ENavigation.with(activity)
.setHostAndPath("app/test")
.putString("text", "hello world")
.navigate()
```
上面向***app/test***这个路由对应的Activity传入了一个key为text,value为hello world的值。取值的方式和通过intent传值一样。
### 数据回传
可通过封装的onResult回调取到上个页面回传的值。
```kotlin
ENavigation.with(activity)
.setHostAndPath("app/test")
.onResult { requestCode, resultCode, data ->
val text = data?.getStringExtra("result")
Toast.makeText(this, "requestCode=$requestCode\nresultCode=$resultCode\n收到回传的值:$text", Toast.LENGTH_SHORT).show()
}
.navigate()
```
## 四、拦截器
拦截器作用于目标Activity,可用于目标Activity的权限限制,如某些页面需要登录后才能访问,此时就可以使用拦截器来实现这一功能。
### 全局拦截器
全局拦截器作用于所有的页面跳转,实现全局拦截器需要自定义一个类,继承自INavigationInterceptor,重写intercept方法,然后给自定义的类加上@GlobalInterceptorAnno注解。
```kotlin
@GlobalInterceptorAnno
class MyGlobalInterceptor : INavigationInterceptor {
override fun intercept(chain: INavigationInterceptor.Chain) {
if(isLogin){
// 如果不拦截,执行chain.proceed
chain.proceed(chain.request)
}
}
}
```
如果有多个全局拦截器,可以设置拦截器的优先级
```kotlin
@GlobalInterceptorAnno(priority = 1)
class MyGlobalInterceptor : INavigationInterceptor {
override fun intercept(chain: INavigationInterceptor.Chain) {
if(isLogin){
// 如果不拦截,执行chain.proceed
chain.proceed(chain.request)
}
}
}
```
priority的值越大,优先级越高。
### 注解拦截器
注解拦截器作用于单个页面跳转,同样需要自定义一个拦截器类
```kotlin
@InterceptorAnno(name = "app/test_interceptor")
class TestInterceptor1 : INavigationInterceptor {
override fun intercept(chain: INavigationInterceptor.Chain) {
Toast.makeText(chain.request.activity, "拦截器1", Toast.LENGTH_SHORT).show()
chain.proceed(chain.request)
}
}
```
同路由地址一样,拦截器也需要给一个name,第一个单词对应host,第二个对应名称。当然也可以不使用注解,直接定义
```kotlin
class TestInterceptor2 : INavigationInterceptor {
override fun intercept(chain: INavigationInterceptor.Chain) {
Toast.makeText(chain.request.activity, "拦截器2", Toast.LENGTH_SHORT).show()
chain.proceed(chain.request)
}
}
```
之后需要在目标Activity的注解上添加拦截器
```kotlin
@HostAndPathAnno(
PathConfig.Module1.Test6.PATH,
interceptors = [TestInterceptor2::class],
interceptorNames = ["app/test_interceptor1"],
)
class TestActivity6 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test6)
}
}
```
可以通过interceptors或者interceptorNames的方式为目标Activity添加拦截器。注意通过interceptors设置的拦截器类不需要添加@InterceptorAnno注解,否则就会出现拦截两次的情况。
### 动态添加拦截器
也可以在跳转时添加拦截器
```kotlin
ENavigation.with(activity)
.setHostAndPath("app/test")
.setInterceptors(TestInterceptor())
.navigate()
```
通过setInterceptors设置即可,如果需要添加多个
```kotlin
ENavigation.with(activity)
.setHostAndPath("app/test")
.setInterceptors(TestInterceptor1(), TestInterceptor2(), TestInterceptor3())
.navigate()
```
跳转时添加的拦截器类同样不需要添加@InterceptorAnno注解。
## 五、跳转动画
ENavigation封装了一系列的跳转动画,举个Activity从下面弹出的例子
```kotlin
ENavigation.with(this)
.setHostAndPath("app/test")
.bottom()
.navigate()
```
跳转动画方法说明
| 方法名 | 入场 | 出场 |
| --- | --- | --- |
| fade() | 渐显 | 渐隐 |
| top() | 上面进入 | 渐隐 |
| right() | 右边进入 | 渐隐 |
| bottom() | 底部进入 | 渐隐 |
| left() | 左边进入 | 渐隐 |
| fadeIn() | 渐显 | - |
| topIn() | 上面进入 | - |
| rightIn() | 右边进入 | - |
| bottomIn() | 底部进入 | - |
| leftIn() | 左边进入 | - |
| expandTopLeftIn() | 左上角展开 | - |
| expandTopCenterIn() | 顶部展开 | - |
| expandTopRightIn() | 右上角展开 | - |
| expandCenterLeftIn() | 左边展开 | - |
| expandCenterIn() | 中间展开 | - |
| expandCenterRightIn() | 右边展开 | - |
| expandBottomLeftIn() | 左下角展开 | - |
| expandBottomCenterIn() | 下面展开 | - |
| expandBottomRightIn() | 右下角展开 | - |
| fadeOut() | - | 渐隐 |
| topOut() | - | 顶部退出 |
| rightOut() | - | 右边退出 |
| bottomOut() | - | 下面退出 |
| leftOut() | - | 左边退出 |
| shrinkTopLeftOut() | - | 右上角退出 |
| shrinkTopCenterOut() | - | 上面退出 |
| shrinkTopRightOut() | - | 右上角退出 |
| shrinkCenterLeftOut() | - | 左边退出 |
| shrinkCenterOut() | - | 中间退出 |
| shrinkCenterRightOut() | - | 右边退出 |
| shrinkBottomLeftOut() | - | 左下角退出 |
| shrinkBottomCenterOut() | - | 下面退出 |
| shrinkBottomRightOut() | - | 右下角退出 |
当然也可以使用自定义的动画
```kotlin
ENavigation.with(this)
.setHostAndPath("app/test")
.animateIn(R.anim.custom_in)
.animateOut(R.anim.custom_out)
.navigate()
```
animateIn作用于跳转的Activity,animateOut作用于当前Activity。
## 六、异常处理
路由跳转常见的就是找不到路由的情况,再就是跳转的时候Activity已经销毁了,ENavigation封装了一些常见的异常,放在NavigationException类中
| 异常 | 说明 |
| --- | --- |
| NavigationException.ActivityDetachedException | Activity已经销毁 |
| NavigationException.NullTargetException | 路由或Scheme不存在 |
| NavigationException.NullActivityException | Activity为空,比如在Fragment中跳转 |
| NavigationException.InvalidActivityException | Activity只能为FragmentActivity |
| NavigationException.InvalidCodeException | resultCode != Activity.RESULT_OK |
通过onError回调监听异常
```kotlin
ENavigation.with(this)
.setScheme("app/test")
.onError { exception ->
when (exception) {
is NavigationException.ActivityDetachedException -> {
}
is NavigationException.NullActivityException -> {
}
is NavigationException.NullTargetException -> {
}
is NavigationException.InvalidCodeException -> {
}
is NavigationException.InvalidActivityException -> {
}
else -> {
// 其它异常
}
}
}
.navigate()
```
## License
> ```
> Copyright 2021 RickyHal
>
> Licensed under the Apache License, Version 2.0 (the "License");
> you may not use this file except in compliance with the License.
> You may obtain a copy of the License at
>
> http://www.apache.org/licenses/LICENSE-2.0
>
> Unless required by applicable law or agreed to in writing, software
> distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the License for the specific language governing permissions and
> limitations under the License.
> ```