Как правильно вернуться к родительской активности?

179

У меня есть 2 действия (A и B) в моем приложении для Android, и я использую намерение перейти от действия A к действию B. Использование parent_activity включено:

 <activity
        android:name=".B"
        android:label="B" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.app_name.A" />
  </activity>

Я также использую тему, которая обеспечивает кнопку UP.

Поэтому после того, как я вызвал операцию BI, можно использовать кнопку UP, чтобы вернуться к операции A. Проблема заключается в том, что приложение, похоже, снова вызывает функцию onCreate () операции A, и это не то поведение, которое мне нужно. Мне нужно, чтобы упражнение A выглядело так же, как оно выглядело до того, как я назвал упражнение B.

Есть ли способ добиться этого?

заранее спасибо

РЕДАКТИРОВАТЬ:

Я не написал никакого кода, чтобы начать действие B с действия A. Я думаю, что оно автоматически генерируется Eclipse.

Класс B выглядит так:

    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_b);
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_b, menu);
    return true;
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpFromSameTask(this);
            return true;
    }
    return super.onOptionsItemSelected(item);
}
ashiaka
источник
Опубликуйте свой
Если я вас правильно понял, вы можете использовать startActivityForResult () и вернуть resultCode или что-то еще.
Закон Гименеса
Пожалуйста, обновите помеченный правильный ответ! ПРАВИЛЬНЫЙ ответ исходит от LorenzCK, а не от пользователя ......! Пометка этого как правильного вводит в заблуждение и заставляет еще больше программистов неправильно понимать навигацию в отличие от обратной навигации!
Зордид
Ну и дела, так много неправильных ответов здесь, не могли бы вы помочь в очистке этого ...?
Zordid
@ashiaka - правильный ответ в соответствии с вашим дизайном кода обновляется.
user370305

Ответы:

334

Вы объявили активность A со стандартом launchModeв манифесте Android. Согласно документации это означает следующее:

Система всегда создает новый экземпляр действия в целевой задаче и направляет намерение к нему.

Следовательно, система вынуждена воссоздавать действие A (т.е. вызывать onCreate), даже если стек задач обрабатывается правильно.

Чтобы решить эту проблему, вам нужно изменить манифест, добавив следующий атрибут в объявление действия A:

android:launchMode="singleTop"

Примечание. Вызов finish()(как предлагалось ранее в качестве решения) работает только в том случае, если вы полностью уверены, что экземпляр действия B, который вы прерываете, живет поверх экземпляра действия A. В более сложных рабочих процессах (например, запуск действия B из уведомления) это может быть не так, и вы должны правильно запустить действие A из B.

LorenzCK
источник
11
Вот объяснение из документации по Android. Режимы «стандартный» и «одиночный» отличаются друг от друга только в одном отношении: каждый раз, когда появляется новое намерение для «стандартного» действия, создается новый экземпляр класса для ответа на это намерение. Каждый экземпляр обрабатывает одно намерение. Аналогичным образом, новый экземпляр действия «singleTop» также может быть создан для обработки нового намерения. Однако, если целевая задача уже имеет существующий экземпляр действия на вершине своего стека, этот экземпляр получит новое намерение (в вызове onNewIntent ()); новый экземпляр не создан.
kpsfoo
Обратите внимание, что в соответствии с документацией navigateUpFromSameTask(...) должно быть добавление FLAG_ACTIVITY_CLEAR_TOPк upIntent(через navigateUpTo(...)которое должно происходить то же поведение (согласно этому руководству ), но флаг никогда не устанавливается - и, следовательно, этот ответ имеет смысл
Anigif
82

Обновленный ответ: Навигация вверх

Вы должны указать, какое действие является подходящим родителем для каждого действия. Это позволяет системе упростить навигационные шаблоны, такие как Up, поскольку система может определить логическое родительское действие из файла манифеста.

Поэтому для этого вы должны объявить родительскую активность в теге Activity с атрибутом

android:parentActivityName

Подобно,

<!-- The main/home activity (it has no parent activity) -->
    <activity
        android:name="com.example.app_name.A" ...>
        ...
    </activity>
    <!-- A child of the main activity -->
    <activity
        android:name=".B"
        android:label="B"
        android:parentActivityName="com.example.app_name.A" >
        <!-- Parent activity meta-data to support 4.0 and lower -->
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.app_name.A" />
    </activity>

С родительским действием, объявленным таким образом, вы можете перейти к соответствующему родителю, как показано ниже,

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    // Respond to the action bar's Up/Home button
    case android.R.id.home:
        NavUtils.navigateUpFromSameTask(this);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Поэтому, когда вы вызываете NavUtils.navigateUpFromSameTask(this);этот метод, он завершает текущее действие и запускает (или возобновляет) соответствующее родительское действие. Если целевое родительское действие находится в заднем стеке задачи, оно переносится, как определено FLAG_ACTIVITY_CLEAR_TOP.

И чтобы отобразить кнопку вверх, вы должны объявить setDisplayHomeAsUpEnabled():

@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

Старый ответ: (без навигации вверх, по умолчанию навигация назад)

Это происходит только в том случае, если вы снова запускаете Занятие A из Занятия B.

Использование startActivity().

Вместо этого из Действия A запустите Activity B, используя startActivityForResult()переопределение onActivtyResult()в Activity A.

Теперь в Деятельности B просто finish()нажмите на кнопку Up. Итак, теперь вы обратились к Деятельности А onActivityResult()без повторного создания Деятельности А.

user370305
источник
Я не знаю, где startActivity () в действии B называется. Я опубликовал исходный код активности B ...
ashiaka
18
Вместо строки кода NavUtils.navigateUpFromSameTask(this); пишите finish().
user370305
4
Напрашивается вопрос: почему Google не упоминает ничего о finish()или startActivityForResult()в своей документации по навигации ( developer.android.com/design/patterns/navigation.html )?
wired00
это звучит как трудоемкий обходной путь. Ответ @LorenzCK кажется намного лучше.
carmen_munich
Большое спасибо за использование NavUtils.navigateUpFromSameTask (это). Большой плюс! Я искал такой метод для замены finish (), который не возвращается к родительской активности при некоторых обстоятельствах. finish () по существу возвращает к предыдущему действию, которое не обязательно является родительским действием.
Hong
22

У меня была почти такая же настройка, которая привела к тому же нежелательному поведению. Для меня это сработало: добавив следующий атрибут к активности A в Manifest.xmlмоем приложении:

android:launchMode="singleTask"

Смотрите эту статью для более подробного объяснения.

androidnewbie
источник
1
С моей точки зрения правильное решение проблемы. Это будет вести себя так же, как если бы вы вызывали finish (), если Activity существует в стеке задач. Если это не так, он будет создан и запущен.
Йохан
5
Это неправильный способ, так как каждый раз он создает ненужную новую задачу, вместо этого вы должны использовать launchMode = "singleTop".
kpsfoo
19

Хотя старый вопрос, вот еще (имхо самое чистое и лучшее) решение , как и все предыдущие ответившие не работало для меня , так как я deeplinked активности B от Widget .

public void navigateUp() {
final Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
    Log.v(logTag, "Recreate back stack");
        TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities();
  } else {
    NavUtils.navigateUpTo(this, upIntent);
  }
}

[ https://stackoverflow.com/a/31350642/570168 ]

Но также смотрите: https://speakerdeck.com/jgilfelt/this-way-up-implementing-effective-navigation-on-android

Тобиас
источник
7

Лучший способ добиться этого - использовать две вещи: call:

NavUtils.navigateUpFromSameTask(this);

Теперь, чтобы это работало, вам нужно, чтобы в файле манифеста было указано, что у действия A есть родительское действие B. Для родительского действия ничего не нужно. В версии 4 и выше вы получите красивую стрелку назад без дополнительных усилий (это можно сделать и в более низких версиях, с небольшим кодом, я опишу это ниже). Вы можете установить эти данные на вкладке manifest-> application в графическом интерфейсе (прокрутите вниз до имени родительского действия и введите его вручную)

Узел поддержки:

если вы хотите поддерживать версию ниже версии 4, вам также необходимо включить метаданные. щелкните правой кнопкой мыши на активности, добавьте-> метаданные, name = android.support.PARENT_ACTIVITY и value = your.full.activity.name

чтобы получить красивую стрелку в более низких версиях:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

обратите внимание, вам понадобится поддержка библиотеки версии 7, чтобы все это заработало, но оно того стоит!

Дональд
источник
5

То, что работало для меня, добавляло:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        finish();
    }

чтобы TheRelevantActivity.javaи сейчас он работает как положено

и да, не забудьте добавить:

getSupportActionbar.setDisplayHomeAsUpEnabled(true);в onCreate()методе

Хаммад Насир
источник
4

Я пытался android:launchMode="singleTask", но это не помогло. Работал для меня, используяandroid:launchMode="singleInstance"

Pnemonic
источник
Добавление android: launchMode = "singleInstance" к родительской активности решило проблему для меня
эмир
4

Добавление к ответу @ LorenCK, изменение

NavUtils.navigateUpFromSameTask(this);

к приведенному ниже коду, если ваша деятельность может быть инициирована из другой деятельности, и это может стать частью задачи, запущенной другим приложением

Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
    TaskStackBuilder.create(this)
            .addNextIntentWithParentStack(upIntent)
            .startActivities();
} else {
    NavUtils.navigateUpTo(this, upIntent);
}

Это запустит новую задачу и запустит родительскую активность вашей деятельности, которую вы можете определить в манифесте, как показано ниже в версии Min SDK <= 15

<meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.app_name.A" />

Или, parentActivityNameесли его> 15

Sourabh
источник
Примечание: объявляйте parentActivityNameдля SDK> 15, иначе это даст вам случайные сбои
Сураб
3

У меня была похожая проблема с использованием Android 5.0 с плохим именем родительского действия

<activity
        android:name=".DisplayMessageActivity"
        android:label="@string/title_activity_display_message"
        android:parentActivityName=".MainActivity" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.myfirstapp.MainActivity" />
    </activity>

Я удалил com.example.myfirstapp из имени родительского действия, и он работал правильно

Патрик Бергтольд
источник
2

Добавьте к своей деятельности информацию манифеста с атрибутом

android:launchMode="singleTask"

работает хорошо для меня

Правеш Кумар
источник
1
Поток Comlete подобен этому @Override public boolean onOptionsItemSelected (MenuItem item) {switch (item.getItemId ()) {case android.R.id.home: NavUtils.navigateUpFromSameTask (this); вернуть истину; } return super.onOptionsItemSelected (item); }
Правеш Кумар
Добавьте в свой манифест <Activity android: name = ". MainActivity" android: label = "@ string / title_activity_main"> </ activity> <Activity android: name = ". CreateEventActivity" android: label = "@ string / title_activity_create_event" android : launchMode = "singleTask" android: parentActivityName = ". MainActivity"> <метаданные android: name = "android.support.PARENT_ACTIVITY" android: value = ". MainActivity" /> </ activity>
Правеш Кумар,
2

В классе Java: -

    toolbar = (Toolbar) findViewById(R.id.apptool_bar);
    setSupportActionBar(toolbar);

    getSupportActionBar().setTitle("Snapdeal");

    getSupportActionBar().setHomeButtonEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

В Манифесте: -

<activity
            android:name=".SubActivity"
            android:label="@string/title_activity_sub"
            android:theme="@style/AppTheme" >
            <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"></meta-data>
    </activity>

Это поможет тебе

Кастури Шраванкумар
источник
1
    @Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
    case android.R.id.home:
        finish();
        return true;
}
return super.onOptionsItemSelected(item);

как бэк пресс

Дэн Алботяну
источник
1

попробуй это:

Intent intent;
@Override
public void onCreate(Bundle savedInstanceState) {
    intent = getIntent();   
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_b);
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_b, menu);
    return true;
}  

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpTo(this,intent);
            return true;
    }
    return super.onOptionsItemSelected(item);
}
Луис Валадез
источник
0

Вход в мой манифест и добавление android:launchMode="singleTop"к действию помог мне.

Это определенно решило мою проблему, потому что я не хотел, чтобы Android создавал новый экземпляр предыдущего действия после нажатия Upкнопки на панели инструментов - вместо этого я хотел использовать существующий экземпляр предыдущего действия при переходе вверх по иерархии навигации.

Ссылка: android: launchMode

mumush
источник