Jetpack Navigation框架用了一段時間了,感覺還不錯辽慕,公司的項目用的就是Navigation框架并且做了模塊化京腥,模塊之間的通信交互,我個人感覺實現(xiàn)的不夠優(yōu)雅o溅蛉,因為當(dāng)時項目比較急這塊也沒過多的設(shè)計架構(gòu)公浪。我在空閑時間仿照阿里的ARouter針對Navigation框架做了個路由庫,順便熟悉下annotation的用法船侧。
先說用法:
在項目的build.gradle文件添加 maven {url "https://jitpack.io"}
repositories {
google()
jcenter()
maven {url "https://jitpack.io"}
}
在module的build.gradle文件添加依賴
apply plugin: 'kotlin-kapt'
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [
ROUTER_MODULE_NAME : project.getName(),
//這個navigation表的id, 如<navigation android:id="@+id/nav_graph_module_one">
ROUTER_MODULE_GRAPH_NAME: "nav_graph_module_one"
]
}
}
}
dependencies {
kapt 'com.github.fengpeihao.router:router-compiler:1.0.1'
implementation 'com.github.fengpeihao.router:router-core:1.0.1'
}
給所有在navigation表中的Fragment添加Route注解
/**
* destinationText是Fragment在表中的id
* 這邊我把destinationText 抽出來放在了RouteConstant中
* <fragment
* android:id="@+id/mainModuleHomeFragment"
* android:name="com.cfxc.router.MainModuleHomeFragment"/>
*/
@Route(destinationText = RouteConstant.MAIN_MODULE_HOME_FRAGMENT)
class MainModuleHomeFragment : Fragment()
------------------------------分割線---------------------------------
object RouteConstant {
const val MAIN_MODULE_HOME_FRAGMENT = "mainModuleHomeFragment"
const val MAIN_MODULE_FIRST_FRAGMENT = "mainModuleFirstFragment"
const val MAIN_MODULE_SECOND_FRAGMENT = "mainModuleSecondFragment"
const val MAIN_MODULE_THIRD_FRAGMENT = "mainModuleThirdFragment"
const val LOGIN_FRAGMENT = "loginFragment"
const val PREREQUISITE_FRAGMENT = "prerequisiteFragment"
//module first
const val MODULE_ONE_FIRST_FRAGMENT = "moduleOneFirstFragment"
const val MODULE_ONE_SECOND_FRAGMENT = "moduleOneSecondFragment"
//module second
const val MODULE_TWO_FIRST_FRAGMENT = "moduleTwoFirstFragment"
//provider
const val USER_DATA_PROVIDER = "user_data_provider"
}
頁面跳轉(zhuǎn)
//不帶參跳轉(zhuǎn)
Router.getInstance().build(RouteConstant.MAIN_MODULE_FIRST_FRAGMENT)
.navigation(findNavController())
//帶設(shè)置pop up to
Router.getInstance().build(destination)
.navigation(findNavController(),RouteConstant.LOGIN_FRAGMENT,true)
//帶bundle跳轉(zhuǎn)
Router.getInstance().build(RouteConstant.MODULE_TWO_FIRST_FRAGMENT)
.with(bundleOf(BundleKeyConstant.KEY_CONTENT to "module one says 'Hello'"))
.navigation(findNavController())
//帶跳轉(zhuǎn)回調(diào)
Router.getInstance().build(RouteConstant.MODULE_ONE_SECOND_FRAGMENT)
.navigation(findNavController(), object : NavigationCallback {
override fun onLost(postcard: Postcard?) {
//沒發(fā)現(xiàn)路由頁面
}
override fun onArrival(postcard: Postcard?) {
//跳轉(zhuǎn)成功
}
override fun onInterrupt(postcard: Postcard?) {
//跳轉(zhuǎn)被攔截
}
})
注意: 頁面跳轉(zhuǎn)的時候一定要傳NavController
攔截器的使用
//攔截器可以定義多個欠气,攔截器是根據(jù) priority的值從大到小依次執(zhí)行
@Interceptor(priority = 2, name = "loginInterceptor")
class LoginInterceptor : IInterceptor {
val TAG = "LoginInterceptor"
override fun process(postcard: Postcard, callback: InterceptorCallback) {
if (checkNeedLogin(postcard.destinationText)) {
postcard.prerequisiteDestinationGraph = "nav_graph"
postcard.prerequisiteDestination = RouteConstant.LOGIN_FRAGMENT
}
callback.onContinue(postcard)
}
override fun init(context: Context?) {
Log.e(TAG, "LoginInterceptor init")
}
}
模塊間數(shù)據(jù)共享Provider的使用
先在common library定義共享的provider接口
interface IUserDataProvider:IProvider {
fun getUserName():String
}
然后在要共享數(shù)據(jù)的module中實現(xiàn)這個接口
@Route(destinationText = RouteConstant.USER_DATA_PROVIDER)
class UserDataProvider: IUserDataProvider {
override fun getUserName():String{
return "Tom"
}
}
在其他模塊就可以這樣用
val userDataProvider = Router.getInstance().build(RouteConstant.USER_DATA_PROVIDER)
.navigation() as IUserDataProvider
Toast.makeText(requireContext(), userDataProvider.getUserName(), Toast.LENGTH_SHORT).show()
對了,從main Module跳轉(zhuǎn)到其他module頁面時要先跳轉(zhuǎn)到對應(yīng)的navigation graph镜撩,所以其他module的navigation graph的startDestination Fragment要做一個該module的跳轉(zhuǎn)分發(fā)器
class ModuleOneNavigatorFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
//Constants.KEY_DESTINATION_ID 是Router內(nèi)部定義的bundle Key预柒,用來傳遞跳轉(zhuǎn)的destinationId
//這里傳遞過來的就是在前面頁面要跳轉(zhuǎn)到的頁面的destinationId
val destinationId = it.getInt(Constants.KEY_DESTINATION_ID, R.id.moduleOneFirstFragment)
findNavController().navigate(
destinationId, it,
NavOptions.Builder().setPopUpTo(R.id.moduleOneNavigatorFragment, true)
.setEnterAnim(R.anim.slide_in).setExitAnim(R.anim.slide_out).build()
)
} ?: run {
findNavController().navigate(
R.id.moduleOneFirstFragment, null,
NavOptions.Builder().setPopUpTo(R.id.moduleOneNavigatorFragment, true)
.setEnterAnim(R.anim.slide_in).setExitAnim(R.anim.slide_out).build()
)
}
}
}
附送下demo地址: https://github.com/fengpeihao/Router ,簡單實現(xiàn)了組件化