Android Jetpack Navigation框架的組件化方案

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)了組件化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末袁梗,一起剝皮案震驚了整個濱河市宜鸯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遮怜,老刑警劉巖淋袖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锯梁,居然都是意外死亡即碗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門陌凳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剥懒,“玉大人,你說我怎么就攤上這事合敦∪锓剩” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵蛤肌,是天一觀的道長。 經(jīng)常有香客問我批狱,道長裸准,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任赔硫,我火速辦了婚禮炒俱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己权悟,他們只是感情好砸王,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峦阁,像睡著了一般谦铃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榔昔,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天驹闰,我揣著相機與錄音,去河邊找鬼撒会。 笑死嘹朗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诵肛。 我是一名探鬼主播屹培,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怔檩!你這毒婦竟也來了褪秀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤珠洗,失蹤者是張志新(化名)和其女友劉穎溜歪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體许蓖,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蝴猪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了膊爪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片自阱。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖米酬,靈堂內(nèi)的尸體忽然破棺而出沛豌,到底是詐尸還是另有隱情,我是刑警寧澤赃额,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布加派,位于F島的核電站,受9級特大地震影響跳芳,放射性物質(zhì)發(fā)生泄漏芍锦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一飞盆、第九天 我趴在偏房一處隱蔽的房頂上張望娄琉。 院中可真熱鬧次乓,春花似錦、人聲如沸孽水。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽女气。三九已至杏慰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間主卫,已是汗流浹背逃默。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留簇搅,地道東北人完域。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像瘩将,于是被迫代替她去往敵國和親吟税。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容