Как предотвратить анимацию строки состояния и панели навигации при переходе анимации сцены активности?
Во-первых, мой фон строки состояния установлен темно-коричневым, а фон панели навигации по умолчанию черный. Я использую тему Material light.
Я начинаю новую деятельность, используя ActivityOptions.makeSceneTransitionAnimation
с переходами по умолчанию, и я замечаю, что строки состояния и навигации на короткое время переходят в белый, а затем обратно в правильные цвета.
Согласно документации:
Чтобы получить полный эффект перехода, необходимо включить переходы содержимого окна как для вызывающей, так и для вызываемой деятельности. В противном случае вызывающее действие начнет выходной переход, но затем вы увидите переход окна (например, масштабирование или затухание).
я использую getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
как на призыве, так и на вызываемой деятельности.
Точно так же, если я изменю переход ввода на слайд, у панели состояния и навигации на короткое время будет переход слайда с белым фоном.
Как предотвратить анимацию строки состояния и панели навигации при переходе анимации сцены активности?
9 ответов
Есть два подхода, которые вы можете использовать для предотвращения анимации навигации / строки состояния во время перехода:
Подход № 1: Исключить строку состояния и панель навигации из окна по умолчанию для выхода / перехода плавного перехода
Причина, по которой навигация / строка состояния исчезают и исчезают во время перехода, заключается в том, что по умолчанию все не разделяемые представления (включая фоны навигации / строки состояния) будут постепенно исчезать / появляться в вызывающих / вызываемых операциях соответственно после начала перехода., Однако вы можете легко обойти это, исключив фон навигации / строки состояния из окна / выхода по умолчанию окна. Fade
переход. Просто добавьте следующий код в ваши действия onCreate()
методы:
Transition fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
getWindow().setExitTransition(fade);
getWindow().setEnterTransition(fade);
Этот переход также может быть объявлен в теме действия с использованием XML (т.е. в вашем собственном res/transition/window_fade.xml
файл):
<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:android="http://schemas.android.com/apk/res/android">
<targets>
<target android:excludeId="@android:id/statusBarBackground"/>
<target android:excludeId="@android:id/navigationBarBackground"/>
</targets>
</fade>
Подход № 2. Добавление строки состояния и панели навигации в качестве общих элементов.
Этот подход основан на ответе klmprt, который почти сработал для меня... хотя мне все еще нужно было сделать пару изменений.
В своем вызове Activity я использовал следующий код для запуска Activity:
View statusBar = findViewById(android.R.id.statusBarBackground);
View navigationBar = findViewById(android.R.id.navigationBarBackground);
List<Pair<View, String>> pairs = new ArrayList<>();
if (statusBar != null) {
pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
}
if (navigationBar != null) {
pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
}
pairs.add(Pair.create(mSharedElement, mSharedElement.getTransitionName()));
Bundle options = ActivityOptions.makeSceneTransitionAnimation(activity,
pairs.toArray(new Pair[pairs.size()])).toBundle();
startActivity(new Intent(context, NextActivity.class), options);
Пока что это, по сути, то же самое, что предложил klmprt в своем ответе. Тем не менее, мне также нужно было добавить следующий код в моей называемой деятельности onCreate()
метод для предотвращения "мигания" строки состояния и панели навигации во время перехода:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
// Postpone the transition until the window's decor view has
// finished its layout.
postponeEnterTransition();
final View decor = getWindow().getDecorView();
decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
decor.getViewTreeObserver().removeOnPreDrawListener(this);
startPostponedEnterTransition();
return true;
}
});
}
Добавление строки состояния и фонов панели навигации в качестве общих элементов заставит их рисовать поверх стандартного перехода выхода / ввода с исчезновением по умолчанию, что означает, что они не исчезнут во время перехода. Более подробное обсуждение этого подхода можно найти в этом посте Google+.
У меня была та же самая проблема, и ответы, кажется, пропускают критическую часть головоломки. Помните, что при переходе через общий элемент все происходит в операции назначения.
Чтобы удалить эффект мигания, просто добавьте следующее к вызываемому действию:
Fade fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
getWindow().setEnterTransition(fade);
getWindow().setExitTransition(fade);
Это должно решить вашу проблему!
Полностью не допускайте, чтобы переходы Activity мешали переходам с общим элементом:
При выходе из функции вызовите getWindow(). SetExitTransition(null);
При входящем действии вызовите getWindow(). SetEnterTransition(null);
Я подозреваю, что это может иметь побочные эффекты, но не знаю точно. Это очень просто и работает, хотя.
Предотвратить мигание определенных элементов:
Я начал с ответа Алекса Локвуда и провел немало экспериментов, чтобы попытаться заставить его работать. Суть его правильная, хотя мне не нужен был код, который он предлагает для получения Activity, но я столкнулся с некоторыми проблемами, вызвав его во фрагменте (вместо Activity) и установив панель инструментов в качестве панели действий.
О, что за фрагмент? Я видел много комментариев о том, что попытки получить ссылки на строку состояния и панель навигации были нулевыми. То же самое произошло и со мной, пока я не понял, что не найду их в макете фрагмента... они были выше этого уровня. Следовательно, код ниже, чтобы получить представление декора из Activity и искать его. Тогда я нашел их без проблем.
В итоге я разработал этот полезный метод:
public static Bundle transitionOptions(Activity activity, int transitionViewResId, int transitionNameResId) {
if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
return null;
}
View decorView = activity.getWindow().getDecorView();
View statusBar = decorView.findViewById(android.R.id.statusBarBackground);
View navigationBar = decorView.findViewById(android.R.id.navigationBarBackground);
View appBarLayout = decorView.findViewById(**R.id.appbarlayout**);
View transitionView = decorView.findViewById(transitionViewResId);
String transitionName = activity.getString(transitionNameResId);
List<Pair<View, String>> pairs = new ArrayList<>();
pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
if (appBarLayout != null) {
pairs.add(Pair.create(appBarLayout, activity.getString(**R.string.transition_appbarlayout**)));
}
pairs.add(Pair.create(transitionView, transitionName));
//noinspection unchecked - we're not worried about the "unchecked" conversion of List<Pair> to Pair[] here
return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs.toArray(new Pair[pairs.size()]))
.toBundle();
}
Обратите внимание на R.string.transition_appbarlayout и R.id.appbarlayout. Эти идентификаторы являются произвольными, если они соответствуют тому, что использует ваш код. В моем XML я настраиваю пользовательскую панель действий следующим образом (отредактировано до основы):
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout
android:id="**@+id/appbarlayout**"
android:transitionName="**@string/transition_appbarlayout**">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"/>
</android.support.design.widget.AppBarLayout>
Если вы не используете подобную панель инструментов, эту часть можно удалить из служебного метода.
Тогда вы бы назвали это в своем фрагменте так:
startActivity(intent, UIUtils.transitionOptions(getActivity(),
R.id.**my_view**,
R.string.**transition_my_view**));
Используя любые значения, которые вы хотите, при условии, что это соответствует вашему XML.
Это предотвращает мигание строки состояния, панели инструментов и панели навигации (кнопки назад / домой / последние приложения) во время перехода. Остальная часть перехода Активности нормальна.
В моем случае тема нашего приложения имеет android:windowBackground
синего цвета. Это вызывает синюю вспышку при переходе, что довольно неприятно. Но вместо того, чтобы вносить изменения, которые так влияют на все приложение, сейчас я выберу первый, быстрый и грязный вариант.
Вы должны поделиться ими в ActivityOptions.makeSceneTransitionAnimation.
Например:
ActivityOptions.makeSceneTransitionAnimation(... Pair.create(activity.findViewById(android.R.id.window_status_bar), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME)
(извините за псевдо; у меня нет точного значения android.R.id под рукой)
Вы можете запустить соответствующий переход после обмена мнениями.
Насколько я понимаю, это связано с перекрытием перехода деятельности. Чтобы преодолеть эту проблему, я использовал следующие значения в onCreate()
методы обоих видов деятельности:
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
getWindow().setEnterTransition(null);
на входном переходе убрал для меня белое наложение.
Вот как я это сделал. Я разделяю оба Status Bar
и Navigation Bar
в SharedElementTransition
вместе с ImageView
:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
View imageView = view.findViewById(R.id.iv);
Resources resources = view.getResources();
imageView.setTransitionName(resources.getString(R.string.transition_image_thumbnail));
Pair<View, String> p1 = Pair.create(imageView, resources.getString(R.string.transition_image_thumbnail));
Window window = getActivity().getWindow();
View navigationBar = getActivity().findViewById(android.R.id.navigationBarBackground);
View statusBar = getActivity().findViewById(android.R.id.statusBarBackground);
Pair<View, String> p2 = Pair.create(statusBar, statusBar.getTransitionName());
Pair<View, String> p3 = Pair.create(navigationBar, navigationBar.getTransitionName());
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
p1, p2, p3);
ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
} else {
startActivity(intent);
}
В случае анимации
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromYDelta="100%p"
android:toYDelta="0">
</translate> <!--Define duration here-->
</set>
Я закончил тем, что сделал прозрачную строку состояния в
@colors
<color name="transparent">#00000000</color>
для примера темы, подобной следующей в
@styles
<style name="Theme.AppCompat.Translucent" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="android:background">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:statusBarColor">@color/transparent</item>
</style>
Если кто использует androidx и найдет
fade.excludeTarget(android.R.id.navigationBarBackground, true);
не работает, это просто потому, что это окно не может найти представление с этим идентификатором, вы должны заменить его на
this.excludeTarget(androidx.appcompat.R.id.action_bar_container,true)
.
Но сейчас время навигации , так что грустно знать об этом до сих пор.