【问题标题】:Returning from an activity using navigateUpFromSameTask()使用 navigateUpFromSameTask() 从活动返回
【发布时间】:2013-01-05 21:55:33
【问题描述】:

我有两个活动,A 和 B。当活动 A 首次启动时,它会访问传递给它的 Intent(因为 Bundlenull,因为它应该是第一次通过),并且相应地显示信息:

CustInfo m_custInfo;
...
protected void onCreate(Bundle savedInstanceState)
{
    ...
    Bundle bundle = (savedInstanceState == null) ? getIntent().getExtras() : savedInstanceState;
    m_custInfo = (CustInfo) m_bundle.getSerializable("CustInfo");
    if (m_custInfo != null
        ...
}

这在第一次通过时效果很好。 EditText 控件和ListView 填写正确。

现在,当点击列表中的一个项目时,活动 B 开始显示详细信息:

m_custInfo = m_arrCustomers.get(pos);

Intent intent = new Intent(A.this, B.class);
intent.putExtra("CustInfo", m_custInfo); // CustInfo is serializable
// printing this intent, it shows to have extras and no flags

startActivityForResult(intent, 1);

在活动 B 启动之前,框架调用 A 的覆盖 onSaveInstanceState()

protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);

    outState.putSerializable("CustInfo", m_custInfo);
}

在活动B中,当在操作栏中按下向上按钮时,我想返回活动A并使其处于与之前相同的状态:

public boolean onOptionsItemSelected(MenuItem item)
{
    if (item.getItemId() == android.R.id.home)
    {
        Intent intent = NavUtils.getParentActivityIntent(this);
        // printing this intent, it shows to have flags but no extras

        NavUtils.navigateUpFromSameTask(this); // tried finish() here but that created an even bigger mess
        return true;
    }
    ...
}

问题就出在这里,当第二次在活动A的onCreate()中,Bundle参数是null并且getExtras()返回null。由于调用了onSaveInstanceState(),我预计Bundle 参数不是null

我在其他网站上阅读过有关此问题的信息,尝试了建议,但没有任何效果。

【问题讨论】:

  • finish() 是使用 Activity B 的方式。是的,如果您保存在 onSaveInstanceState() 中,onCreate 应该与它捆绑在一起。三重检查您的代码,因为其中一定存在错误。您也可以考虑发布更完整的代码。
  • jsmith -- 我试图保持代码简短但相关。我还能添加什么有帮助的内容?
  • 3 年后,我们遇到了同样的问题

标签: android android-activity android-actionbar up-button


【解决方案1】:

如果您希望您的应用程序以这种方式反应,您应该将您的活动 A 的启动模式声明为:

android:launchMode="singleTop"

在您的 AndroidManifest.xml 中。

否则android使用标准启动模式,也就是说

"系统总是在 目标任务”

您的活动被重新创建(请参阅Android documentation)。

使用singleTop,如果它位于任务的后堆栈的顶部,系统将返回到您现有的活动(带有原始额外活动)。在这种情况下不需要实现 onSaveInstanceState。

在您的情况下,savedInstanceState 为 null,因为您的活动之前没有被操作系统关闭。

通知(感谢android开发者指向this

虽然这是对问题的一种解决方案,但如果返回的活动 不是 在返回堆栈的顶部,它将不起作用。 考虑活动A启动B的情况,也就是启动C,C的父活动配置为A。 如果您从 C 调用 NavigateUpFromSameTask(),将重新创建活动,因为 A 不在堆栈顶部。

在这种情况下,可以改用这段代码:

Intent intent = NavUtils.getParentActivityIntent(this); 
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP); 
NavUtils.navigateUpTo(this, intent);

【讨论】:

【解决方案2】:

这是因为NavUtils.navigateUpFromSameTask() 基本上只是调用startActivity(intentForActivityA)。如果活动 A 使用默认的android:launchMode="standard",则创建活动的新实例并且不使用保存的状态。解决此问题的方法有 3 种,各有优缺点。

选项 1

覆盖活动 B 中的 getParentActivityIntent() 并指定活动 A 所需的适当附加功能:

@Override
public Intent getParentActivityIntent() {
    Intent intent = super.getParentActivityIntent();
    intent.putSerializable("CustInfo", m_custInfo);
    return intent;
}

注意:如果您使用的是 AppCompatActivity 和 androidx 或支持库中的工具栏,那么您需要覆盖 getSupportParentActivityIntent()

我觉得这是最优雅的解决方案,因为无论用户如何导航到活动 B,它都可以正常工作。如果用户直接导航到活动 B 而不通过活动 A,下面列出的两个选项将无法正常工作,因为例如,如果活动 B 从通知或 URL 处理程序启动。我认为这种情况是“向上”导航 API 复杂且不能简单地充当愚蠢的后退按钮的原因。

此解决方案的一个缺点是使用标准的开始新活动过渡动画,而不是返回到先前活动的动画。

选项 2

通过覆盖onNavigateUp(),拦截向上按钮并将其视为愚蠢的返回按钮:

@Override
public boolean onNavigateUp() {
    onBackPressed();
    return true;
}

注意:如果您使用的是 AppCompatActivity 和来自 androidx 或支持库的工具栏,那么您需要改写 onSupportNavigateUp()

这个解决方案有点老套,只适用于“up”和“back”应该表现相同的应用程序。这可能很少见,因为如果“向上”和“返回”应该表现相同,那么为什么还要麻烦有一个向上按钮呢?此解决方案的一个优点是使用了标准的返回上一活动动画。

选项 3

按照 yonojoy 的建议,在活动 A 上设置 android:launchMode="singleTop"。有关详细信息,请参阅 his answer。请注意,singleTop 并不适用于所有应用程序。您必须尝试一下和/或花一些时间阅读the documentation 来自行决定。

【讨论】:

  • 我认为这是正确的答案。谢谢我不希望我的父活动始终是 SingleTop,我只是希望 UP 传递一些状态。
  • 喜欢调用 onBackPressed 的解决方案。它非常适合我,谢谢!
  • 选项 1. 如果 A - 扩展 Fragment,B - 扩展 AppCompatActivity ,我如何提取数据?我已经覆盖了getParentActivityIntent() in B,但是如何在A中使用呢?
  • user25:您是在问 A 如何从 B 的 getParentActivityIntent() 返回的 Intent 中提取数据?您应该能够在片段 A 的父 Activity 上使用 getIntent() 方法。本页讨论片段如何访问意图:stackoverflow.com/questions/11387740/… 请注意,如果您使用的是 android:launchMode="singleTop" 则 Activity 托管片段 A 可能需要覆盖 onNewIntent() 并调用 this.setIntent()。
  • 不应使用选项 2。有用的演示文稿很好地解释了它。 speakerdeck.com/jgilfelt/…
【解决方案3】:

而不是调用NavUtils.navigateUpFromSameTask

您可以在导航之前获取父意图并对其进行修改。例如:

Intent intent = NavUtils.getParentActivityIntent(this);
intent.putExtra("CustInfo", m_custInfo); // CustInfo is serializable
NavUtils.navigateUpTo(this, intent);

这将与NavUtils.navigateUpFromSameTask 完全相同,但允许您向意图添加一些额外的属性。你可以看看NavUtils.navigateUpFromSameTask的代码,很简单。

public static void navigateUpFromSameTask(Activity sourceActivity) {
    Intent upIntent = getParentActivityIntent(sourceActivity);

    if (upIntent == null) {
        throw new IllegalArgumentException("Activity " +
                sourceActivity.getClass().getSimpleName() +
                " does not have a parent activity name specified." +
                " (Did you forget to add the android.support.PARENT_ACTIVITY <meta-data> " +
                " element in your manifest?)");
    }

    navigateUpTo(sourceActivity, upIntent);
}

使父 Activity SINGLE_TOP 可能适用于一些简单的应用程序,但如果该 Activity 不是直接从其父 Activity 启动的呢?或者您可能合法地希望父级存在于后堆栈中的多个位置。

【讨论】:

    【解决方案4】:

    我认为这是一个更通用的解决方案,要添加到您的基本 Activity 类中:

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            final Intent upIntent = NavUtils.getParentActivityIntent(this);
            if (upIntent == null)
                onBackPressed();
            else
                //optionally add this flag to the intent: Intent.FLAG_ACTIVITY_SINGLE_TOP
                NavUtils.navigateUpTo(this, upIntent);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    

    对于您希望导航到某个特定父 Activity 的每个 Activity,请在清单上使用它:

        <activity android:parentActivityName="pathToParentActivity" ...>
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="pathToParentActivity"/>
        </activity>
    

    而且,如果您的 minSdk 来自 API 16,请删除“元数据”标签,这样您就可以在清单中使用它:

        <activity android:parentActivityName="pathToParentActivity" ...>
        </activity>
    

    更多信息here

    【讨论】:

      【解决方案5】:

      你提到:

      在活动B中,当在操作栏中按下向上按钮时,我想返回活动A并使其处于与之前相同的状态。

      如果您在 Activity A 上的内容驻留在 Fragment 中,则在 Fragment 的 onCreate() 中调用 setRetainInstance(true)。

      然后 Fragment 实例将在 Activity 重新创建期间保留。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-26
        • 2018-06-02
        相关资源
        最近更新 更多