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

Во-первых, мой фон строки состояния установлен темно-коричневым, а фон панели навигации по умолчанию черный. Я использую тему 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);

С /questions/20166726/migayuschij-ekran-pri-perehode-izobrazheniya-mezhdu-dejstviyami/20166739#20166739

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

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

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

Но сейчас время навигации , так что грустно знать об этом до сих пор.

Другие вопросы по тегам