Fragment OnBackPressedDispatcher 监听Activity的onBackPressed

需求场景描述

在目前的比较常见的一种场景是,在 App 的首页如果点击 back 键会 toast 提示用户再点击一次是退出 App。

例如如下代码所示:

# 首页Homectivity.java
@Override
public void onBackPressed() {
    if ((System.currentTimeMillis() - exitTime) > 2000) {
        ShowUtils.toast(R.string.exit_tost);//提示再按一次back退出App
        exitTime = System.currentTimeMillis();
    } 
}

这种场景常规情况是没有什么问题的,但是如果你 App 首页 Activity 嵌套了一个 Fragment ,这个 fragment 需要根据场景来决定是否需要处理返回键这个操作,如果需要处理的话就不将这个返回的事件交给 首页ActivityonBackPressed方法,如果不需要处理则将这个返回的事件交给 首页ActivityonBackPressed方法。

解决方案

方案1:

fragment 本身并没有提供处理 activity onBackPressed 的监听,google了一下基本都是自己实现一个监听器来处理的。

image.png

方案2:官方支持OnBackPressedDispatcher

其实 Android 是有支持在 fragment 中去处理 activity onBackPressed 的处理,那就是androidx.activity.OnBackPressedDispatcher这个类。

打开这个类,它已经有对使用方式有做了一个案例了,我们对着抄就好了。

image.png

下面把操作步骤写一些:

  • 编写 OnBackPressedCallback 监听器,这个回调主要是处理用户的返回键操作的

    注意:这里OnBackPressedCallback的构造需要传一个参数enabled,标识fragment是否要处理这个返回键事件,我们可以动态设置这个值来决定fragment是否要处理用户的返回键事件。

    # fragment.kt
    //谷歌推荐的fragment监听activity返回的事件
    private val mBackPressedCallback: OnBackPressedCallback by lazy  {
        object : OnBackPressedCallback(false) {//enabled 标记位
            override fun handleOnBackPressed() {
                Log.d(TAG,"handleOnBackPressed invoke")
            }
        }
    }
    
  • 添加这个监听器

    # fragment.kt
    fragment.requireActivity().onBackPressedDispatcher.addCallback(fragment, // LifecycleOwner
            mBackPressedCallback)
    
  • 动态的调整是否要处理这个返回键事件

    mBackPressedCallback.isEnabled = false/true
    
  • 在需要的地方移除这个事件

    一般情况下可以在生命周期结束的时候移除这个事件

    mBackPressedCallback.remove()
    

##OnBackPressedDispatcher 的实现原理

分析问题,首先得找到切入口,那就是 ComponentActivity#onBackPressed方法

# ComponentActivity.java
public void onBackPressed() {
    mOnBackPressedDispatcher.onBackPressed();
}

可以看到 mOnBackPressedDispatcher 是在 ComponentActivity 中处理的。那接下来的分享都是围绕在 ComponentActivity 这个类中的。

###mOnBackPressedDispatcher.onBackPressed()

在注释1 OnBackPressedDispatcher#onBackPressed方法首先去处理 OnBackPressedCallback 事件,如果集合mOnBackPressedCallbacks中没有数据,那么注释2执行 mFallbackOnBackPressed.run()

# OnBackPressedDispatcher.java
public void onBackPressed() {
    Iterator<OnBackPressedCallback> iterator =
            mOnBackPressedCallbacks.descendingIterator();
    while (iterator.hasNext()) {
        OnBackPressedCallback callback = iterator.next();
        if (callback.isEnabled()) {//注释3
            callback.handleOnBackPressed();//注释1
            return;
        }
    }
    if (mFallbackOnBackPressed != null) {
        mFallbackOnBackPressed.run();//注释2
    }
}

添加OnBackPressedCallback事件

这个操作就是上面添加监听器mBackPressedCallback的操作:

fragment.requireActivity().onBackPressedDispatcher.addCallback(fragment, // LifecycleOwner
        mBackPressedCallback)

所以说,如果往 onBackPressedDispatcher 添加了监听器,那么在 onBackPressed 中就会分发给这个监听器mBackPressedCallback处理。注意这里还有注释3有一个判断,表示是否要处理这个返回键事件的标记位。

兜底处理mFallbackOnBackPressed

我们想一下,如果没有任何 BackPressedCallback需要处理的话,那么这个事件是必须要回流到 父Activity#onBackPressed() 的,不然就无法处理关闭 activity 的功能了,所以这个 FallbackOnBackPressed 就是做这件事的。

FallbackOnBackPressed 的实现做了什么?

这个 FallbackOnBackPressed是传递到 OnBackPressedDispatcher的构造中的。

# OnBackPressedDispatcher.java
public OnBackPressedDispatcher(@Nullable Runnable fallbackOnBackPressed) {
    mFallbackOnBackPressed = fallbackOnBackPressed;
}

从注释2来看说这个兜底的处理就是去 ComponentActivity.super.onBackPressed()这样就可以将事件分发给父 Activity 去处理。

# OnBackPressedDispatcher.java
private final OnBackPressedDispatcher mOnBackPressedDispatcher =
        new OnBackPressedDispatcher(new Runnable() {//注释1:这里传入的就是 FallbackOnBackPressed
            @Override
            public void run() {
              	//注释2
                ComponentActivity.super.onBackPressed();
            }
        });

到这里,整个OnBackPressedDispatcher的操作思路就清晰很多了,如果不处理就交给父Activity去处理(注释2)。

那我们怎么HomeActivity判断是否要处理onBackPressed呢?

# 主页 HomeActivity.java
@Override
public void onBackPressed() {
    //是否要处理onBackPressed
    if(getOnBackPressedDispatcher().hasEnabledCallbacks()) {
        super.onBackPressed();
    }else{
        //双击弹toast退出app的处理
	      ExitAppUtils.exitApp(this)
    }
}

因为如果给OnBackPressedDispatcher添加了监听器,那么数据的处理的结果肯定就不会交给当前HomeActivity的onBackPressed()了,那么这里原本的双击弹toast提示退出App的逻辑就无法触发了。

所以这里只要判断 hasEnabledCallbacks才去处理这super.onBackPressed(),否则事件还是交给当前的 HomeActivity 去处理。

记录于 2021年4月1日愚人节

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 从Fragment跳转到Activity可以通过以下步骤实现: 1. 在Fragment中获取Activity的引用: ``` Activity activity = getActivity(); ``` 2. 创建Intent对象并设置要跳转的Activity: ``` Intent intent = new Intent(activity, TargetActivity.class); ``` 3. 调用startActivity方法启动目标Activity: ``` startActivity(intent); ``` 完整代码示例: ``` public class MyFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_my, container, false); Button btnJump = view.findViewById(R.id.btn_jump); btnJump.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 获取Activity的引用 Activity activity = getActivity(); if (activity != null) { // 创建Intent对象并设置要跳转的Activity Intent intent = new Intent(activity, TargetActivity.class); // 启动目标Activity startActivity(intent); } } }); return view; } } ``` ### 回答2: 在实际开发中,我们经常需要在Fragment中进行跳转,如点击Fragment中的按钮会跳转到某个Activity页面。以下是实现从Fragment跳转到Activity的一种方法。 1. 创建Activity 首先,我们需要创建一个新的Activity。可以在Android Studio中使用“New -> Activity -> Empty Activity”来创建一个空的Activity。在Activity的布局文件中可以放置需要展示的控件,如TextView、ImageView等。 2. 在Fragment中注册点击事件 在Fragment中找到需要点击进行跳转的View(如Button),并注册点击事件。在点击事件中使用Intent来跳转Activity。代码示例如下: ```java Button btnToActivity = view.findViewById(R.id.btn_to_activity); btnToActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getActivity(), YourActivityName.class); startActivity(intent); } }); ``` 在Intent的构造函数中,第一个参数为当前Fragment所属的Activity对象,第二个参数为需要跳转的Activity.class。可以通过修改YourActivityName来指定需要跳转的Activity。 3. 在AndroidManifest.xml中注册Activity 当我们创建一个新的Activity时,需要在AndroidManifest.xml中进行注册才能在其他组件中使用。打开AndroidManifest.xml文件,找到“application”标签内,创建一个新的“activity”标签,添加我们新创建的Activity信息。代码示例如下: ```xml <activity android:name=".YourActivityName"/> ``` 其中,YourActivityName为你新创建的Activity名称。 4. 运行应用 完成以上三个步骤后,我们可以运行应用并点击Button进行跳转。如果出现问题,可以检查是否在AndroidManifest.xml中正确注册了Activity,或者是否在代码中正确引用了Activity名称。 总结 以上就是实现从Fragment跳转到Activity的简单步骤。在实际开发中,我们可能需要传递一些数据到目标Activity,可以在Intent中使用putExtra()方法来实现。例如: ```java Intent intent = new Intent(getActivity(), YourActivityName.class); intent.putExtra("key", value); startActivity(intent); ``` 在目标Activity中可以接收传递的数据,例如: ```java getIntent().getStringExtra("key"); ``` 希望以上内容对你有所帮助。 ### 回答3: 在Android应用程序中,FragmentActivity是很重要的两个概念。Fragment可以看作是Activity的一部分,它可以在Activity中添加和移除,并且可以重用。而Activity是一个单独的UI界面,与用户交互,完成特定的任务。 在某些情况下,我们需要从Fragment跳转到另一个Activity页面,这个过程可以通过以下几步完成: 1. 首先,在Fragment中的适当位置,添加一个Intent对象,用于启动目标Activity。 ``` Intent intent = new Intent(getActivity(), TargetActivity.class); startActivity(intent); ``` 这将创建一个新的Intent对象,并通过调用Activity的startActivity()方法来启动目标Activity。 2. 接下来,在目标Activity的布局文件中创建一个新的布局,以便在其中包含所需的UI元素。然后,在Activity中执行所需的操作。例如,如果您需要从Fragment中提取一些数据,并在另一个Activity中显示它们,您可以使用getIntent()方法来获取传递的Intent对象,并从中提取数据。 ``` Bundle extras = getIntent().getExtras(); if (extras != null) { String value = extras.getString("key"); // do something with your value } ``` 这段代码将从传递的Intent对象中获取值,并将其存储在一个字符串变量中,您可以在其中执行任何操作。 3. 最后,在新的Activity中完成任务后,您可以使用finish()方法关闭该Activity,并返回到父ActivityFragment。在某些情况下,您可能需要返回一些数据给父ActivityFragment,例如,用户在目标Activity中进行了一些更改,并且需要在父ActivityFragment中进行相应的更改。 ``` Intent intent = new Intent(); intent.putExtra("key", "value"); setResult(RESULT_OK, intent); finish(); ``` 这段代码将在关闭Activity之前,将需要返回的数据存储在Intent对象中,并使用当前Activity的setResult()方法将其设置为RESULT_OK的值。这将告诉父ActivityFragment,在目标Activity已完成任务时可以返回结果,并且您可以在之后的代码中使用返回的数据执行操作。 通过这些步骤,您可以在Android应用程序中从Fragment中跳转到其他Activity。这使得您可以为应用程序创建复杂的UI,以便在不同的屏幕大小和方向上提供最佳的用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值