Возврат из действия с помощью navigateUpFromSameTask()
У меня есть два действия, A и B. Когда действие A начинается впервые, оно обращается к Intent
перешел к нему (потому что Bundle
является 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()
активности А во второй раз Bundle
параметр null
а также getExtras()
возвращается null
, поскольку onSaveInstanceState()
был вызван, я бы ожидал Bundle
параметр, который не являетсяnull
,
Я читал об этой проблеме на других веб-сайтах, попробовал предложения, но ничего не работает.
4 ответа
Если вы хотите, чтобы ваше приложение реагировало таким образом, вы должны объявить режим запуска вашей активности A следующим образом:
android:launchMode="singleTop"
в вашем AndroidManifest.xml.
В противном случае Android использует стандартный режим запуска, что означает
"Система всегда создает новый экземпляр действия в целевой задаче"
и ваша деятельность воссоздается (см. документацию Android).
С singleTop система возвращается к существующему действию (с оригинальным дополнительным), если оно находится на вершине заднего стека задачи. В этой ситуации нет необходимости реализовывать onSaveInstanceState.
saveInstanceState имеет значение null в вашем случае, потому что ваша деятельность ранее не была закрыта ОС.
Обратите внимание (спасибо, разработчик Android за указание на это):
Хотя это решение вопроса, оно не будет работать, если возвращаемое действие не находится на вершине заднего стека. Рассмотрим случай активности A, начинающейся с 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);
Это происходит потому, что NavUtils.navigateUpFromSameTask()
в основном просто звонки startActivity(intentForActivityA)
, Если действие A использует значение по умолчанию android:launchMode="standard"
затем создается новый экземпляр действия, а сохраненное состояние не используется. Есть три способа исправить это, с различными преимуществами и недостатками.
Опция 1
Переопределите getParentActivityIntent() в действии B и укажите соответствующие дополнительные функции, необходимые для действия A:
@Override
public Intent getParentActivityIntent() {
Intent intent = super.getParentActivityIntent();
intent.putSerializable("CustInfo", m_custInfo);
return intent;
}
Примечание. Если вы используете ActionBarActivity и панель инструментов из библиотеки поддержки, вам нужно вместо этого переопределить getSupportParentActivityIntent().
Мне кажется, что это наиболее элегантное решение, так как оно работает независимо от того, как пользователь перешел к действию B. Два перечисленных ниже параметра не будут работать правильно, если пользователь переходит непосредственно к действию B без выполнения действия A, например, если действие B запускается из обработчика уведомлений или URL. Я думаю, что этот сценарий является причиной того, что навигационный API "вверх" сложен и не просто действует как тупая кнопка "назад".
Недостатком этого решения является то, что вместо анимации возврата к предыдущей активности используется стандартная анимация перехода "начало новой деятельности".
Вариант 2
Перехватите кнопку вверх и обработайте ее как тупую кнопку возврата, переопределив onNavigateUp ():
@Override
public boolean onNavigateUp() {
onBackPressed();
return true;
}
Примечание. Если вы используете ActionBarActivity и панель инструментов из библиотеки поддержки, вам нужно вместо этого переопределить onSupportNavigateUp().
Это решение немного хакерское и работает только для приложений, где "вверх" и "назад" должны вести себя одинаково. Это, вероятно, редко, потому что если "вверх" и "назад" должны вести себя одинаково, то зачем использовать кнопку вверх? Одним из преимуществ этого решения является то, что используется стандартная анимация возврата к предыдущей активности.
Вариант 3
Как предложено yonojoy, установите android:launchMode="singleTop"
на деятельность А. Смотрите его ответ для деталей. Обратите внимание, что 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 может работать для некоторых простых приложений, но что, если это действие не было запущено непосредственно из его родителя? ИЛИ вы можете на законных основаниях хотеть, чтобы родитель существовал в нескольких местах в заднем стеке.
Я думаю, что это более общее решение, которое нужно добавить в базовый класс 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>
Больше информации здесь
Вы упоминаете:
В действии B, когда на панели действий нажимается кнопка "Вверх", я хочу вернуться к заданию A и вернуть его в то же состояние, в котором оно было раньше.
Если ваш контент в Деятельности А находится во Фрагменте, тогда вызовите setRetainInstance(true) в onCreate() Фрагмента.
Затем экземпляр Fragment будет сохранен во время воссоздания Activity.