使用navigateUpFromSameTask()从活动返回

时间:2013-01-22 15:47:05

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

我有两个活动,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
        ...
}

第一次通过时效果很好。 <{1}}控件和EditText已正确填写。

现在,当单击列表中的项目时,将启动活动B以显示详细信息:

ListView

在活动B开始之前,框架调用A覆盖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);

onSaveInstanceState()

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

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

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

这就是问题,当活动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()参数为Bundle null返回{ {1}}。自调用getExtras()以来,我原本期望null参数为非onSaveInstanceState()

我在其他网站上看过这个问题,已经尝试了这些建议,但没有任何效果。

5 个答案:

答案 0 :(得分:129)

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

android:launchMode="singleTop"

在AndroidManifest.xml中。

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

  

“系统始终在中创建活动的新实例   目标任务“

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

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

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

注意(感谢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);

答案 1 :(得分:17)

这是因为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;
}

注意:如果您正在使用ActionBarActivity和支持库中的工具栏,那么您需要覆盖getSupportParentActivityIntent(),而不是。

我认为这是最优雅的解决方案,因为无论用户如何导航到活动B,它都能正常工作。如果用户直接导航到活动B而不通过活动A,则下面列出的两个选项无法正常工作例如,如果从通知或URL处理程序启动活动B.我认为这种情况是出现这种情况的原因。导航API很复杂,并不像一个愚蠢的后退按钮。

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

选项2

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

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

注意:如果您正在使用ActionBarActivity和支持库中的工具栏,那么您需要覆盖onSupportNavigateUp(),而不是。

这个解决方案有点hacky,只适用于那些&#34; up&#34;和&#34;返回&#34;应该表现一样。这可能很少见,因为如果&#34; up&#34;和&#34;返回&#34;应该表现相同然后为什么打扰按钮?此解决方案的一个优点是使用标准的返回先前活动动画。

选项3

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

答案 2 :(得分:4)

而不是调用NavUtils.navigateUpFromSameTask

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

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

这与NavUtils.navigateUpFromSameTask的行为完全相同,但允许您为intent添加一些额外的属性。您可以查看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可能适用于某些简单的应用程序,但是如果此活动不是直接从它的父项启动的呢?或者您可能合法地希望父级存在于后台堆栈中的多个位置。

答案 3 :(得分: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>

更多信息here

答案 4 :(得分:-1)

你提到:

  

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

如果活动A上的内容位于片段中,则在片段的onCreate()中调用setRetainInstance(true)。

然后,Fragment实例将在Activity娱乐中保留。