使用 navigateUpFromSameTask() 从活动返回



我有两个活动,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);

在 Acivity 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参数是非null的。

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

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

android:launchMode="singleTop"

在你的 AndroidManifest.xml 中。

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

"系统始终在 目标任务"

系统会重新创建您的活动记录(请参阅 Android 文档)。

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

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

注意(感谢安卓开发者指出这一点)

虽然这是问题的解决方案,但如果返回的活动不在后退堆栈的顶部,它将不起作用。考虑活动 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);
发生这种情况

是因为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,它都可以工作。如果用户直接导航到活动 B 而不通过活动 A,例如,如果活动 B 从通知或 URL 处理程序启动,则下面列出的两个选项将无法正常工作。我认为这种情况是"向上"导航 API 很复杂并且不仅仅充当哑后退按钮的原因。

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

选项 2

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

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

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

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

选项 3

按照约诺乔伊的建议,android:launchMode="singleTop"活动A。详见他的回答。请注意,singleTop并不适合所有应用程序。您必须尝试一下和/或花一些时间阅读文档以自己决定。

而不是调用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可能适用于某些简单的应用程序,但如果此活动不是直接从其父级启动的呢?或者,您可能合法地希望父级存在于后退堆栈中的多个位置。

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

@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>

更多信息在这里

你提到:

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

如果您在活动 A 上的内容驻留在片段中,请在片段的 onCreate() 中调用 setRetainInstance(true)。

然后,片段实例将在活动重新创建中保留。

相关内容

  • 没有找到相关文章

最新更新