Як запобігти анімуванню рядка стану та панелі навігації під час переходу анімації сцени активності?


129

По-перше, для мого фону рядка стану встановлено темно-коричневий колір, а для мого фону панелі навігації встановлено чорний колір. Я використовую тему Light light.

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

Згідно з документацією :

Щоб отримати повний ефект від переходу, потрібно ввімкнути переходи вмісту вікна як на виклик, так і на виклик. В іншому випадку активація виклику розпочне вихідний перехід, але тоді ви побачите перехід вікна (наприклад, масштаб або зникнення)

Я використовую getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);як дзвінки, так і виклики.

Подібним чином, якщо я зміню вхідний перехід на слайд, як на панелі стану, так і на панелі навігації на короткий час перехід слайда з білим тлом.

Як запобігти анімуванню рядка стану та панелі навігації під час переходу анімації сцени активності?

Відповіді:


219

Я знаю два підходи, які я можу використовувати для запобігання анімуванню панелі навігації / стану під час переходу:

Підхід №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 на льодянику? Я використовую appcompat-v7
radzio

3
Підхід №2 працює, але все ще є мерехтіння (в діяльності), коли відбувається перехід. рядок стану та панель навігації більше не мигають.
Акшат

16
@alex Це працювало майже ідеально для мене, поки ми не перейшли на нову бібліотеку підтримки з android.support.design.widget.TabLayout та android.support.design.widget.AppBarLayout - тепер мерехтіння повернулось
Noa Drach

6
android.R.id.navigationBarBackground може дати вам NPE на пристроях Samsung або HTC, оскільки вони не мають панелі навігації на екрані. Зробіть нульову перевірку, перш ніж додавати їх як спільні елементи
Хлопчик

8
Я випробовую це рішення на Nexus 6 за допомогою андроїд-нуги. Але обидва підходи для мене не працюють.
Пардіп Кр

4

Повністю запобігати переходам Activity від втручання у спільні переходи елементів:

По завершенню активності викличте getWindow (). SetExitTransition (null);

У процесі введення викличте getWindow (). SetEnterTransition (null);

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

Я підозрюю, що це може мати побічні ефекти, але не знаю точно. Це просто по-справжньому і працює.

Запобігання мигання конкретних елементів:

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


4

Щойно у мене було те саме питання, і у відповідях, здається, не вистачає критичної частини загадки. Пам’ятайте, що при спільному переході елемента все відбувається в цільовій діяльності .

Для того, щоб видалити блимаючий ефект, просто додайте до виклику дії наступне:

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

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

Це має вирішити вашу проблему!


Привіт. Хіба це не те саме, що Підхід No1 у верхній відповіді?
rlay3

@ rlay3 Не зовсім. Наскільки я можу зрозуміти, він ніколи не згадує того факту, що це потрібно встановлювати лише в діяльності призначення.
LukeWaggoner,

агов @LukeWaggoner ви могли б допомогти мені це: stackoverflow.com/questions/50189286 / ...
Blackhawk

Ви повинні виключити рядок стану та навігаційну панель як для активності, так і для абонента та для виклику.
shaithana

3

Вам потрібно поділитися ними ActivityOptions.makeSceneTransitionAnimation.

Наприклад:

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

(вибачте за psuedo; я не маю під рукою точного значення android.R.id)

Ви можете виконати відповідний перехід, поділившись думками.


Вам це вдалося? Я спробував це, і рядок стану та панель навігації все ще блимають білим під час переходу.
rlay3

Так, це спрацювало у мене. Ви впевнені, що немає іншого подання, яке може тимчасово перекривати їх? Ви пробували налаштувати простий перехід до діяльності в середовищі, що не працює, щоб ізолювати проблему?
klmprt

@klmprt Я думаю, вам також потрібно відкласти перехід введення, щоб він запрацював ... Я також не зміг перешкодити анімуванню панелей стану / навігації, просто поділившись переглядами. Думаю, вам потрібно почекати, поки вид декору вікна закінчить його макет, перш ніж дозволити переходу введення.
Alex Lockwood

@klmprt Що, але моя відповідь все ще не стосується, це те, як запобігти анімуванню кольору фону панелі дій під час переходу. Якщо обидва дії мають один і той же колір тла на панелі дій, тоді колір фону на панелі дій буде здаватися анімованим, оскільки панель дій викликаючої активності поступово згасає, а панель дій викликаної активності плавно зникає. Чи маєте ви уявлення, як це обійти проблема?
Алекс Локвуд,

@klmprt Насправді, я думаю, є ще краще рішення. Ви можете просто виключити фони навігації / рядка стану як цілі у переході вікна за замовчуванням вихід / вхід затухання. Детальніше див. У моїй оновленій відповіді.
Алекс Локвуд,

3

Наскільки я розумію, це спричинено перекриттям переходу діяльності. Для подолання цієї проблеми я використав наступні значення в onCreate()методах обох видів діяльності:

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);


0

Ось як я це зробив. Я поділяю як 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.