web-dev-qa-db-ja.com

navigateUpFromSameTask()を使用してアクティビティから戻る

AとBの2つのアクティビティがあります。アクティビティ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()で2回目に、BundleパラメーターがnullandgetExtras()が返される場合です。 nullonSaveInstanceState()が呼び出されたため、Bundleパラメーターが非nullであると予想されていました。

他のWebサイトでこの問題について読んだことがありますが、提案を試みましたが、何も機能しません。

68
DavidCrow

アプリケーションがこのように反応するようにするには、アクティビティAの起動モードを次のように宣言する必要があります。

Android:launchMode="singleTop"

androidManifest.xmlで。

それ以外の場合Androidは標準の起動モードを使用します。つまり、

「システムは常にターゲットタスクにアクティビティの新しいインスタンスを作成します」

アクティビティが再作成されます( Android documentation を参照)。

SingleTopを使用すると、システムは、タスクのバックスタックの一番上にある場合、既存のアクティビティ(元の余分なものを含む)に戻ります。この状況では、onSaveInstanceStateを実装する必要はありません。

あなたのアクティビティは以前OSによってシャットダウンされていなかったため、この場合、savedInstanceStateはnullです。

Notice(ありがとう、Android thisを指す開発者

これは問題の解決策ですが、戻ってくるアクティビティがnotバックスタックの一番上にある場合は機能しません。 Cを開始しているBを開始し、Cの親アクティビティがAに設定されているアクティビティAの場合を考えます。CからNavigateUpFromSameTask()を呼び出すと、アクティビティは再作成されます。スタック。

この場合、代わりに次のコードを使用できます。

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

これは、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;
}

注:ActionBarActivityとサポートライブラリのツールバーを使用している場合は、代わりに getSupportParentActivityIntent() をオーバーライドする必要があります。

ユーザーがアクティビティBにナビゲートした方法に関係なく機能するため、これは最もエレガントなソリューションだと思います。たとえば、アクティビティA Bは、通知またはURLハンドラーから起動されます。このシナリオが、「アップ」ナビゲーションAPIが複雑であり、単純に戻るボタンとして機能しない理由だと思います。

このソリューションの欠点の1つは、前のアクティビティに戻るアニメーションではなく、標準の新しいアクティビティを開始するトランジションアニメーションが使用されることです。

オプション2

上ボタンをインターセプトし、 onNavigateUp() をオーバーライドすることにより、それをダムバックボタンとして扱います。

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

注:ActionBarActivityとサポートライブラリのツールバーを使用している場合、代わりに onSupportNavigateUp() をオーバーライドする必要があります。

このソリューションは少しハック的で、「アップ」と「バック」が同じように動作するアプリケーションでのみ機能します。 「上」と「戻る」が同じように振る舞うのであれば、なぜ上ボタンがあるのでしょうか?このソリューションの利点の1つは、前のアクティビティに戻る標準のアニメーションが使用されることです。

オプション3

Yonojoyが示唆するように、アクティビティAにAndroid:launchMode="singleTop"を設定します。詳細については、 his answer を参照してください。 singleTopはすべてのアプリケーションに適しているわけではないことに注意してください。あなたはそれを試してみるか、そして/または ドキュメント を読んで自分で決める必要があります。

19
Mark Doliner

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にすることは、いくつかの単純なアプリケーションで機能する場合がありますが、このアクティビティが親から直接起動されなかった場合はどうなりますか? ORバックスタックの複数の場所に親が存在することを合法的に望むかもしれません。

4
Jared Kells

これはより基本的なソリューションであり、基本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からのものである場合は、「meta-data」タグを削除します。そのため、代わりにマニフェストに次のようにします。

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

詳細情報 here

0