使用navigationUpFromSameTask()从活动返回


69

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

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;
    }
    ...
}

这里存在一个问题,当onCreate()第二次进入活动A时,Bundle参数为null getExtras()返回null。自从onSaveInstanceState()被调用以来,我希望Bundle参数为non- null

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


finish()是进行活动B的方法。是的,如果保存在onSaveInstanceState()中,则onCreate应该与其捆绑在一起。请仔细检查您的代码,因为其中某处必须有错误。您也可以考虑发布更完整的代码。
jsmith

1
jsmith-我试图使代码简短但相关。我还能添加些什么呢?
DavidCrow

1
3年后,我们
遇到

Answers:


133

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

android:launchMode="singleTop"

在您的AndroidManifest.xml中。

否则android使用标准启动模式,这意味着

“系统总是在目标任务中创建活动的新实例”

并重新创建您的活动(请参阅Android文档)。

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

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

通知(感谢,用于指向Android开发

尽管这是一个解决问题的方法,但是如果返回的活动不在后栈顶部,则将无法使用。考虑活动A始于B的情况,该活动B始于C,并且C的父活动被配置为A。如果NavigateUpFromSameTask()从C调用,则会重新创建该活动,因为A不在堆栈顶部。

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

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

3
这迫使您必须始终以这种方式启动活动。最好只在您需要的特定时间执行此操作,如下所示:stackoverflow.com/a/20306670/878126
android开发人员

@androiddeveloper感谢您指出这一点,我已经相应地编辑了答案
yonojoy 2013年


您的答案的第二部分解决了我的问题,因为我需要一个以上的活动实例来进行导航。这应该是公认的答案!
马库斯

20

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

选项1

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

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

注意:如果您使用的是AppCompatActivity和androidx或支持库中的工具栏,则需要重写getSupportParentActivityIntent()

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

这种解决方案的一个缺点是,使用了标准的“新活动开始”过渡动画,而不是使用“返回到上一个活动”动画。

选项2

通过覆盖onNavigateUp()来拦截向上按钮并将其视为后退按钮:

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

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

该解决方案有点笨拙,仅适用于“向上”和“向后”行为相同的应用程序。这可能很罕见,因为如果“向上”和“向后”的行为相同,那么为什么还要打个向上按钮呢?该解决方案的一个优势是使用了标准的返回到上一个活动的动画。

选项3

正如yonojoy所建议的那样,设置android:launchMode="singleTop"活动A。有关详细信息,请参见他的答案。请注意,这singleTop并不适合所有应用程序。您必须尝试一下和/或花一些时间阅读文档以自行决定。


我认为这是正确的答案。谢谢,我不希望我的父项活动始终为SingleTop,我只希望UP传递某些状态。
Jared Kells 2014年

1
爱解决方案,以调用onBackPressed。非常适合我,谢谢!
batoutofhell 2015年

选项1.如果A-扩展Fragment,B-扩展AppCompatActivity,如何提取数据?我已经重写了getParentActivityIntent() in B,但是如何在A中使用它呢?
user25 2013年

user25:您是否在问A如何getParentActivityIntent()从B返回的Intent中提取数据?您应该能够getIntent()在片段A的父活动上使用该方法。本页讨论片段如何访问意图:stackoverflow.com/questions/11387740/… 请注意,如果您使用的是android:launchMode =“ singleTop”,则活动托管片段A可能需要覆盖onNewIntent()并调用this.setIntent ()。
Mark Doliner

选项2不应该使用。有用的演示,很好地解释了它。Speakerdeck.com/jgilfelt/…–
TheLibrarian

4

而不是打电话 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);
}

将父活动设置为SINGLE_TOP可能适用于某些简单的应用程序,但是如果不是直接从父活动启动该活动怎么办?或者,您可能合法地希望父级存在于后堆栈的多个位置。


0

我认为这是一个更通用的解决方案,可以添加到您的基本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 android:parentActivityName="pathToParentActivity" ...>
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="pathToParentActivity"/>
    </activity>

并且,如果您的minSdk来自API 16,请删除“元数据”标记,以便在清单中添加以下标记:

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

更多信息在这里


-1

您提到:

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

如果您在Activity A上的内容位于一个Fragment中,请在Fragment的onCreate()中调用setRetainInstance(true)。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.