Просмотр видимости потери состояния при возобновлении активности с ранее запущенным Переход активности
Объяснение:
Проблема заключается в следующем:
При запуске нового действия C и возвращении к активности B (или принудительной onPause в B), представления с измененными состояниями видимости появляются снова, без каких-либо прикосновений к представлениям по коду или другим.
Следующее видео объясняет проблему на снимках: https://youtu.be/oqCZo5CSkQk
При отсутствии перехода все работает так, как ожидалось. У кого-нибудь есть идея, как предотвратить потерю зрения представления при возобновлении Activity? Я неправильно использую ActivityOptionsCompat
?
Я использую библиотеки поддержки:
'com.android.support:support-v4:27.1.1'
и 'com.android.support:appcompat-v7:27.1.1'
Но проблема также возникает для более старых версий и для разных производителей телефонов (Pixel, Samsung и т.д.).
Вот код для воспроизведения проблем:
Макеты
Деятельность A:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:gravity="center"
android:text="Activity A | this starts the transition to another activity"/>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/imageToAnimate"
android:layout_width="20dp"
android:layout_height="20dp"
app:srcCompat="@android:drawable/star_big_on"/>
<Button
android:id="@+id/start_next_activity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="start Another Activity"/>
</LinearLayout>
Активность B:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:gravity="center"
android:text="Activity B | with progressbar"/>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/imageToAnimate"
android:layout_width="100dp"
android:layout_height="100dp"
android:transitionName="toAnimate"
app:srcCompat="@android:drawable/star_big_on"/>
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:visibility="gone"/>
<TextView
android:id="@+id/dismiss_text"
style="@style/TextAppearance.AppCompat.Subhead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:text="Also a text to dismiss"/>
<Button
android:id="@+id/show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SHOW"/>
<Button
android:id="@+id/hide_gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SET GONE"/>
<Button
android:id="@+id/hide_invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SET INVISIBLE"/>
<Button
android:id="@+id/start_activity_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="START activity"/>
</LinearLayout>
Деятельность C:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Activity C"
android:textSize="40sp"/>
</LinearLayout>
Исходный код операции
Деятельность A:
public class DebugActivityA extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
findViewById(R.id.start_next_activity).setOnClickListener(v -> startWithTransition());
}
private void startWithTransition() {
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(DebugActivityA.this,
findViewById(R.id.imageToAnimate),
"toAnimate");
startActivity(new Intent(DebugActivityA.this, DebugActivityB.class), options.toBundle());
}
}
Активность B:
public class DebugActivityB extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
View progressbar = findViewById(R.id.progress);
View dismissText = findViewById(R.id.dismiss_text);
progressbar.setVisibility(View.VISIBLE);
findViewById(R.id.show).setOnClickListener(v -> {
progressbar.setVisibility(View.VISIBLE);
dismissText.setVisibility(View.VISIBLE);
});
findViewById(R.id.hide_gone).setOnClickListener(v -> {
progressbar.setVisibility(View.GONE);
dismissText.setVisibility(View.GONE);
});
findViewById(R.id.hide_invisible).setOnClickListener(v -> {
progressbar.setVisibility(View.INVISIBLE);
dismissText.setVisibility(View.INVISIBLE);
});
findViewById(R.id.start_activity_c).setOnClickListener(this::startOtherActivity);
}
private void startOtherActivity(View view) {
startActivity(new Intent(this, DebugActivityC.class));
}
}
Деятельность C:
public class DebugActivityC extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_c);
}
}
Ответы
Ответ 1
Вы можете использовать метод onSaveInstanceState
для сохранения состояния пользовательского интерфейса при выходе из него, а затем использовать onRestoreInstanceState
чтобы восстановить его обратно в это состояние. Добавьте onSaveInstanceState
в действие B:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putInt("progressbarVisibility", progressbar.getVisibility());
savedInstanceState.putInt("dismissTextVisibility", dismissText.getVisibility());
}
Затем добавьте модификацию метода onCreate
B:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multi_play);
View progressbar = findViewById(R.id.progress);
View dismissText = findViewById(R.id.dismiss_text);
if (savedInstanceState != null){
dismissText.setVisibility(savedInstanceState.getInt("dismissTextVisibility", View.VISIBLE));
progressbar.setVisibility(savedInstanceState.getInt("progressbarVisibility", View.VISIBLE));
}
// The rest of your code.
}
Ответ 2
Для этого может быть создана работа.
Вместо того, чтобы изменять видимость просмотров в onCreate
, который вызывается только при создании Activity
, попробуйте манипулировать видимостью представлений в onResume
на основе флагов в самой Activity B
Используйте SharedPreference
или даже на основе простых логических флагов
Теперь, когда Activity B
вернется к видимости, одно и то же состояние будет повторно применено на основе самих этих флагов из onResume
.
Если вы используете SharedPreference
, обновите новое значение, нажав новую Activity C
В onResume
проверьте значение и измените целевые представления. Очистить флаг, как только операция Activity B
будет удалена из backstack ie (onDestroy
of Activity B
)
Ответ 3
Это может быть или не быть проблемой с общей передачей, я придумал подход ниже, чтобы исправить проблему видимости. Почти провел день!
Позвольте мне поделиться своим наблюдением здесь.
Такая проблема не будет существовать, если вы измените состояние видимости общих просмотров перехода.
Если у вас есть такое требование, когда вы хотите изменить видимость некоторых представлений при использовании совместного перехода, сделайте их как общий вид.
ViewCompat.setTransitionName(<view>, <uniqueString>);
Применяя измененные файлы, я проверял и решает нашу проблему.
ActivityA
public class ActivityA extends AppCompatActivity {
private View startNextActivity;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
startNextActivity = findViewById(R.id.start_next_activity);
startNextActivity.setOnClickListener(v -> startWithTransition());
}
private void startWithTransition() {
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(this,
new Pair<View, String>(startNextActivity, ActivityB.VIEW_NAME_IMAGEVIEW)
new Pair<View, String>(startNextActivity, ActivityB.VIEW_NAME_PROGRESSBAR),
new Pair<View, String>(startNextActivity, ActivityB.VIEW_NAME_DETAIL_TEXTVIEW));
startActivity(new Intent(ActivityA.this, ActivityB.class), options.toBundle());
}
}
ActivityB
public class ActivityB extends AppCompatActivity {
public static final String VIEW_NAME_PROGRESSBAR = "progressbar";
public static final String VIEW_NAME_IMAGEVIEW = "toAnimate";
public static final String VIEW_NAME_DETAIL_TEXTVIEW = "detailTextView";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
View progressbar = findViewById(R.id.progress);
View dismissText = findViewById(R.id.dismiss_text);
View animateView = findViewById(R.id.imageToAnimate);
// Key lines to fix our issue
ViewCompat.setTransitionName(animateView, VIEW_NAME_IMAGEVIEW);
ViewCompat.setTransitionName(progressbar, VIEW_NAME_PROGRESSBAR);
ViewCompat.setTransitionName(dismissText, VIEW_NAME_DETAIL_TEXTVIEW);
progressbar.setVisibility(View.VISIBLE);
findViewById(R.id.show).setOnClickListener(v -> {
progressbar.setVisibility(View.VISIBLE);
dismissText.setVisibility(View.VISIBLE);
});
findViewById(R.id.hide_gone).setOnClickListener(v -> {
progressbar.setVisibility(View.GONE);
dismissText.setVisibility(View.GONE);
});
findViewById(R.id.hide_invisible).setOnClickListener(v -> {
progressbar.setVisibility(View.INVISIBLE);
dismissText.setVisibility(View.INVISIBLE);
});
findViewById(R.id.start_activity_c).setOnClickListener(this::startOtherActivity);
}
private void startOtherActivity(View view) {
startActivity(new Intent(this, ActivityC.class));
}
}
Надеюсь, это может помочь вам как-то.
Хорошего дня !!
Ответ 4
Да. Теперь вы можете использовать onSaveInstanceState и onRestoreInstanceState.
Вы можете сохранить состояние visblity в состоянии onSaveInstanceState и восстановить onRestoreInstanceState. Активность всегда вызывает onSaveInstanceState & onRestoreInstanceState, которые работают отлично.
Счастливое кодирование !!!
Ответ 5
Когда вы запускаете операцию C и возвращаете, XML активности B перерисовывается, поэтому используются значения по умолчанию VIEW.Visible. Затем просмотры снова появляются (появляются).
Вам нужно как-то сохранить состояние представления. Пытаться:
- savedInstanceState или
- SharedPreferences