Android Jetpack之Fragment里監(jiān)聽返回鍵的最佳寫法

個人原創(chuàng),轉(zhuǎn)載請注明出處:http://m.itdecent.cn/p/aec2cde0a5f0

Jetpack組件Navigation為Fragment添加了自動的返回棧管理,非常便于處理多個Fragment的相互跳轉(zhuǎn)。但Fragment默認沒有onBackPressed()方法,在按下返回鍵時無法處理除Fragment出棧以外的其他操作。下面給出幾種Fragment里監(jiān)聽返回鍵代碼,順便探討下該事件的最佳寫法。

1. 給onBackPressedDispatcher添加回調(diào)

requireActivity().onBackPressedDispatcher
                .addCallback(viewLifecycleOwner, object: OnBackPressedCallback(true) {
                    override fun handleOnBackPressed() {
                        //Handle back event from any fragment 
                    }
                })

這個方法的最大弊病是不能在里面調(diào)用Activity的onBackPressed()(會產(chǎn)生循環(huán)),當Fragment不需要處理返回操作時不能向上傳遞到Activity,必須在Fragment里處理包括Activity在內(nèi)的所有返回鍵操作,這對于Fragment的編寫者來說很不友好,于是很快被我放棄了。我們希望Activity和Fragment各自的邏輯代碼能分離開來。

2. 給rootView設(shè)置一個OnKeyListener來監(jiān)聽key事件

mRootView.setFocusable(true);
mRootView.setFocusableInTouchMode(true);
mRootView.setOnKeyListener { v, keyCode, event ->
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        if (condition) {
            ...
        }else {
            activity.onBackPressed()
        }
        true
    }
    false
}

這段代碼基本上能滿足大部分要求,Activity和Fragment分別處理各自的返回操作,但任然有兩個小問題。一是要在Fragment里獲取到RootView,二是如果Activity和Fragment都需要對返回鍵做出反應(yīng),那么必須是優(yōu)先觸發(fā)Fragment的返回操作,而Activity不知道返回操作什么時候會被Fragment攔截?。ó斎灰部梢栽贔ragment里增加對Activity里元素的監(jiān)聽,但那樣就違背了二者操作邏輯相互分離的初衷)

3. 給Fragment編寫各自的onBackPressed()方法并在Activity的onBackPressed()里調(diào)用

乍一看,這樣每次都要在Activity的onBackPressed()里判斷當前是哪一個Fragment,并且還要判斷該Fragment是否需要處理返回事件,這不是給Activity添麻煩么?其實不然,通過將給BaseFragment基類(如果沒有就寫一個)添加onBackPressed()方法這些都可以交給各Fragment自己完成。來看BaseFragment:

abstract class BaseFragment : Fragment() {

    ...

    protected val activity by lazy { getActivity() as MainActivity } 

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        ...
        activity.currentFragment = this //currentFragment是Activity里定義的變量,用來持有當前Fragment
    }

    /**fragment攔截返回鍵返回true,不攔截返回false*/
    abstract fun onBackPressed(): Boolean
}

子類Fragment:

class ChildFragment : BaseFragment() {
    
    ...

    protected val activity by lazy { getActivity() as MainActivity } 

    override fun onBackPressed(): Boolean {
        if (condition) {
            ...
            return true //返回true表示消耗掉返回事件,Activity不再處理
        }else {
            return false //返回false不處理返回事件,交給Activity處理
        }            
    }
}

Activity:

class MainActivity : AppCompatActivity() {

    lateinit var  currentFragment : BaseFragment

    ...

    override fun onBackPressed() {
        when { 
            currentFragment.onBackPressed() -> {} /*Fragment處理了返回操作返回true,不再執(zhí)行其他代碼
                                                  Fragment不處理返回false,此時Activity里的其他代碼發(fā)揮作用*/
            condition -> { ... }                            
            else -> super.onBackPressed()
        }
    }
}

這里用到了Java的動態(tài)綁定,通過在BaseFragment里獲取到當前Fragment的實例以及子Fragment重寫B(tài)aseFragment的onBackpressed()方法,使得Activity能自動調(diào)用當前Fragment的onBackpressed()方法。這種寫法我認為是最優(yōu)的,實現(xiàn)了Activity與Fragment返回邏輯代碼的完全分離,Activity不需要知道Fragment具體干了什么,只需要知道結(jié)果(是否消耗掉事件),并且能通過調(diào)整condition的順序來調(diào)整Activity與Fragment返回操作的優(yōu)先級。唯一的缺點可能就是多了點代碼量。

如果你有更好的寫法,歡迎留言指出!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容