Как предотвратить анимацию строки состояния и панели навигации во время перехода анимации сцены действия?

127

Во-первых, фон в строке состояния установлен на темно-коричневый, а фон панели навигации по умолчанию черный. Я использую световую тему Material.

Я начинаю новое занятие, используя ActivityOptions.makeSceneTransitionAnimationпереходы по умолчанию, и замечаю, что панель состояния и панель навигации на короткое время становятся белыми, а затем возвращаются к правильным цветам.

Согласно документации :

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

Я использую getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);как вызывающие, так и вызываемые действия.

Точно так же, если я изменяю переход ввода на слайд, и на панели состояния, и на панели навигации кратковременно появляется переход между слайдами с белым фоном.

Как предотвратить анимацию строки состояния и панели навигации во время перехода анимации сцены действия?

rlay3
источник

Ответы:

217

Есть два известных мне подхода для предотвращения анимации панели навигации / состояния во время перехода:

Подход №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()метод, который я называю Activity, чтобы предотвратить "мигание" строки состояния и панели навигации во время перехода:

@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+ .

Алекс Локвуд
источник
14
Любые идеи, почему findViewById (android.R.id.navigationBarBackground) возвращает null на Lollipop? Я использую appcompat-v7
radzio
3
Подход № 2 работает, но все еще есть мерцание (в действии), когда происходит переход. строка состояния и панель навигации больше не мигают.
Акшат
16
@alex У меня это работало почти идеально, пока мы не перешли на новую библиотеку поддержки с android.support.design.widget.TabLayout и android.support.design.widget.AppBarLayout - теперь мерцание вернулось
Ноа Драч
6
android.R.id.navigationBarBackground может предоставить вам NPE на устройствах Samsung или HTC, поскольку у них нет экранной панели навигации. Выполните нулевую проверку перед добавлением их в качестве общих элементов
Мальчик
8
Я пробую это решение на nexus 6 с Android Nougat. Но оба подхода у меня не работают.
Pardeep Kr
4

Полностью предотвратите вмешательство переходов Activity в переходы общих элементов:

При завершении действия вызовите getWindow (). SetExitTransition (null);

При входящей активности вызовите getWindow (). SetEnterTransition (null);

С https://stackoverflow.com/a/34907685/967131

Я подозреваю, что это может иметь побочные эффекты, но не знаю наверняка. Это очень просто и работает.

Предотвратить мигание определенных элементов:

Я начал с ответа Алекса Локвуда и немного поэкспериментировал, чтобы попытаться заставить его работать. Ядро этого правильное, хотя мне не нужен код, который он предлагает для принимающего 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синий цвет. Это вызывает синюю вспышку при переходе, что довольно неприятно. Но вместо того, чтобы вносить изменения, которые влияют на все приложение, как это, сейчас я выберу первый, быстрый и грязный вариант.

Чад Шульц
источник
3

Вам нужно поделиться ими в ActivityOptions.makeSceneTransitionAnimation.

Например:

ActivityOptions.makeSceneTransitionAnimation(... Pair.create(activity.findViewById(android.R.id.window_status_bar), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME) 

(извините за псевдо, у меня нет точного значения android.R.id под рукой)

Вы можете запустить соответствующий переход после того, как поделитесь взглядами.

klmprt
источник
Это сработало для вас? Я попробовал это, и строка состояния и панель навигации все еще мигают белым во время перехода.
rlay3
Да, у меня это сработало. Вы уверены, что нет другого представления, которое может временно их перекрыть? Вы пробовали настроить простой переход между действиями в «изолированной» среде, чтобы изолировать проблему?
klmprt
@klmprt Я думаю, вам также нужно отложить переход при входе, чтобы он заработал ... Я также не смог предотвратить анимацию панелей состояния / навигации просто путем совместного использования представлений. Я предполагаю, что вам нужно дождаться, пока вид декора окна завершит свою компоновку, прежде чем вы позволите начаться переходу входа.
Alex Lockwood
@klmprt В моем ответе все еще не говорится о том, как предотвратить анимацию цвета фона панели действий во время перехода. Если оба Activity имеют один и тот же цвет фона панели действий, тогда цвет фона панели действий будет казаться анимированным по мере того, как панель действий вызывающего действия постепенно исчезает, а панель действий вызываемого действия мягко исчезает. У вас есть идеи, как обойти это выдавать?
Alex Lockwood
@klmprt Вообще-то, думаю, есть еще лучшее решение. Вы можете просто исключить фоны панели навигации / строки состояния в качестве целей в переходе перехода при выходе / входе в окно по умолчанию. Подробности см. В моем обновленном ответе.
Alex Lockwood
3

Насколько я понимаю, это вызвано перекрытием переходов активности. Чтобы решить эту проблему, я использовал следующие значения в onCreate()методах обоих действий:

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
Randeep
источник
3

У меня была такая же проблема, и в ответах, похоже, отсутствует важный элемент головоломки. Помните, что при переходе общего элемента все происходит в целевом действии .

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

Fade fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);

getWindow().setEnterTransition(fade);
getWindow().setExitTransition(fade);

Это должно решить вашу проблему!

LukeWaggoner
источник
Здравствуй. Разве это не то же самое, что и подход №1 в верхнем ответе?
rlay3
@ rlay3 Не совсем. Насколько я могу судить, он никогда не упоминает тот факт, что это нужно устанавливать только в целевом действии.
Люк Ваггонер
привет @LukeWaggoner, не могли бы вы мне помочь: stackoverflow.com/questions/50189286/…
blackHawk
Вы должны исключить строку состояния и панель навигации как для действия, так и для вызывающего и вызываемого.
Джованни Ди Грегорио
1

getWindow().setEnterTransition(null); на переходе Entering удалил белый оверлей для меня.

Доминик Дэвис
источник
0

Вот как я это сделал. Я разделяю как the, так Status Barи Navigation Barin 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);
}
toobsco42
источник