首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Fragment响应onBackPressed的三个方案

Fragment响应onBackPressed的三个方案

作者头像
韦东锏
发布2021-09-29 15:17:12
1.6K0
发布2021-09-29 15:17:12
举报
文章被收录于专栏:Android码农Android码农

经常会碰到Fragment需要响应activity的onBackPressed事件,对比下三个不同方案

方案1 - 也是我之前用过的

activity在收到onBackPressed事件后,问下对应的fragment,你要不要拦截啊,你要是拦截,我就不管了,你不要拦截我就自己处理了,代码如下

一个FragmentA,有个public的方法,定义自己是否拦截

class FragmentA : Fragment() {

    /**
     * @return true代表响应back键点击,false代表不响应
     */
    fun handleBackPressed(): Boolean {
        if ("满足条件") {
            return true
        } else {
            return false
        }
    }
    
}

然后activity在收到onBackPressed事件后,调用下fragmentA的判断方法,如果fragment有处理,就交给fragment处理,没有的话,就调用super.onBackPressed方法,关闭activity

class ActivityA : AppCompatActivity() {
    override fun onBackPressed() {
        if (!fragmentA.handleBackPressed()) {
            super.onBackPressed()
        }
    }
}

这个策略的问题也是很明显

  1. 不符合单一职责原则。activity必现直接持有fragment的引用,如果有多个fragment,需要对每个fragment都持有一个引用,而且每次新增一个fragment,都需要activity单独做处理
  2. 不能通用处理,一个新的activityB,有类似的场景,还是需要专门处理

方案2 - 采用系统方案

AndroidX库内部,也提供了一个现成的方案,代码如下

class BaseFragment : Fragment() {
    override fun onAttach(context: Context) {
        super.onAttach(context)
        requireActivity().onBackPressedDispatcher.addCallback(this,
            object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                    //这里处理拦截的逻辑
                }

            })
    }
}

系统有一个OnBackPressedDispatcher类,可以直接基于这个类实现onBackPressed的拦截,但是系统这个实现,有个难以接受的地方,最终使我抛弃使用了

先看下系统内部的实现代码

    @MainThread
    public void onBackPressed() {
        Iterator<OnBackPressedCallback> iterator =
                mOnBackPressedCallbacks.descendingIterator();
        while (iterator.hasNext()) {
            OnBackPressedCallback callback = iterator.next();
            if (callback.isEnabled()) {
                callback.handleOnBackPressed();
                //只要callback的enable是true,就算什么都不做,也会强制拦截
                return;
            }
        }
        if (mFallbackOnBackPressed != null) {
            mFallbackOnBackPressed.run();
        }
    }

内部会判断callback.isEnabled(),如果是true,就强制拦截,false就什么都不做,这样设计有什么问题呢?

完全不符合实际场景

真实场景一般是fragment走到特定逻辑了,就需要拦截,没有走到就不拦截,或者随着不同的业务,会动态不断变化,而Android X的设计是,必现提前告诉它们,要不要拦截

  1. 在需要拦截的时候,设置为anable为true,在不需要拦截的时候,要马上设置为flase
  2. 场景复杂下,需要不断的调用true跟flase,来回切换

不知道设计这个逻辑的人是怎么想的

策略3 - 自行实现

首先,定义一个拦截的接口

/**
 * 监听activity的onBackPress事件
 */
interface BackPressedListener {
    /**
     * @return true代表响应back键点击,false代表不响应
     */
    fun handleBackPressed(): Boolean
}

基类fragment实现这个接口

/**
 * 全局通用的基类fragment
 */
abstract class BaseFragment : Fragment(), BackPressedListener {
    override fun handleBackPressed(): Boolean {
        //默认不响应
        return false
    }
}

fragmentA需要响应,就override这个方法

class FragmentA : BaseFragment(){
    override fun handleBackPressed(): Boolean {
        //处理自己的逻辑
        return true
    }
}

最后在基类activity实现逻辑打通

class BaseActivity : AppCompatActivity() {
    override fun onBackPressed() {
        if (!interceptBackPressed()) {
            super.onBackPressed()
        }
    }

    /**
     * 拦截事件
     */
    private fun interceptBackPressed(): Boolean {
        supportFragmentManager.fragments.forEach {
            if (it is BackPressedListener) {
                if (it.handleBackPressed()) {
                    return true
                }
            }
        }
        return false
    }
}

这个方案总结

  1. 业务侧的实现很简单,只需要override一个方法就可以了
  2. 前提是项目都继承同个BaseActivity跟BaseFragment(一般的项目其实都是这样吧)
  3. 不支持fragment内嵌的fragment的back键响应,不过可以拓展
  4. 如果后期Android X实现有调整,可以无缝切换到新的方案上,具体实现层的fragment不用任何改动

综合以上三个,最终选择了方案3,另外希望Android X的实现方案可以早日调整

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android码农 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 方案1 - 也是我之前用过的
  • 方案2 - 采用系统方案
  • 策略3 - 自行实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档