Переход активности общих элементов на android 5

Я хотел настроить переход общих элементов при переходе от одного Activity к другому.

У первого Activity есть RecyclerView с элементами. При щелчке по элементу этот элемент должен анимироваться в соответствии с новым действием.

Итак, я установил android: transitionName = "item" в обоих последних представлениях активности, а также в представлениях элементов recycler-view.

Я также использую этот код при переходе к следующему действию:

this.startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, itemView, "boomrang_item").toBundle());

При щелчке по элементу происходит правильный переход и отображается новый вид. Это действительно здорово. Однако, когда я нажимаю кнопку «Назад». Иногда это работает нормально, но в большинстве случаев моя активность вылетает из-за следующей трассировки стека:

   java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.ViewGroup.transformMatrixToGlobal(android.graphics.Matrix)' on a null object reference
            at android.view.GhostView.calculateMatrix(GhostView.java:95)
            at android.app.ActivityTransitionCoordinator$GhostViewListeners.onPreDraw(ActivityTransitionCoordinator.java:845)
            at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1956)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
            at android.view.Choreographer.doCallbacks(Choreographer.java:580)
            at android.view.Choreographer.doFrame(Choreographer.java:550)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Что я делаю неправильно? Похоже на баг в андроид 5


comment
не могли бы вы добавить весь соответствующий код, чтобы было легче понять, в чем вы ошибаетесь?   -  person gaara87    schedule 29.10.2014
comment
Попробуйте установить transitionName только для общих представлений элементов.   -  person Alex Lockwood    schedule 03.11.2014
comment
У меня такая же проблема, когда я открываю новое действие, поворачиваю экран и нажимаю кнопку возврата. Какие-нибудь решения?   -  person jclova    schedule 27.01.2015
comment
Вы когда-нибудь придумали, как исправить? У меня такое же исключение.   -  person casolorz    schedule 29.03.2015
comment
@mntgoat посмотри мой ответ, не лучший способ, но, вероятно, поможет   -  person hidro    schedule 06.04.2015
comment
У меня такая же проблема, но я заметил странное поведение. Ошибка иногда появляется только на первых трех элементах в моем RecyclerView (когда я возвращаюсь из активности деталей). Я еще не нашел способа решить эту проблему.   -  person Dmytro Karataiev    schedule 23.08.2016
comment
Я только что заполнил проблему на Android с сообщением об этой ошибке: Issuesetracker.google.com/issues/37943857   -  person Brais Gabin    schedule 03.05.2017


Ответы (12)


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

Мой обходной путь - удалить возвратный переход (во втором действии), если экран был повернут перед возвратом, но я уверен, что должен быть лучший способ справиться с этим:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    mOrientationChanged = !mOrientationChanged;
}

@Override
public void supportFinishAfterTransition() {
    if (mOrientationChanged) {
        /**
         * if orientation changed, finishing activity with shared element
         * transition may cause NPE if the original element is not visible in the returned
         * activity due to new orientation, we just finish without transition here
         */
        finish();
    } else {
        super.supportFinishAfterTransition();
    }
}
person hidro    schedule 06.04.2015

Если вы используете Proguard, попробуйте добавить это в свой файл правил. У меня была такая же проблема, и, похоже, она работает?

-keep public class android.app.ActivityTransitionCoordinator

person Sababado    schedule 13.11.2014

Попробуйте удалить любые XML-теги слияния, которые могут быть у вас в представлении последнего действия. Я заметил, что переход к представлению, содержащему тег слияния, в котором переходный элемент является прямым потомком тега слияния, вызовет эту ошибку, но если я заменю тег слияния другим контейнером, таким как CardView, анимация работает нормально. Также убедитесь, что существует соотношение 1: 1 между transitionNames в представлениях.

ОБНОВЛЕНИЕ: я столкнулся с этой проблемой еще раз, когда выполнял переход активности, нажимая кнопку «Назад», чтобы вернуться к исходной активности, а затем снова пытаясь выполнить переход. Я обращался к прямому родительскому элементу «компонента перехода» (A RelativeLayout) по идентификатору с помощью вызова findViewById (), а затем вызывал removeAllViews (). В итоге я изменил код для вызова removeAllViews () для более крупного предка, чем родительский, а также удалил тег из элемента, который должен был заменить «компонент перехода» после загрузки страницы. Это облегчило мою проблему.

person DanielGaffey    schedule 13.03.2015

Убедитесь, что представление, к которому вы переходите во втором действии, не является корневым макетом. Вы можете просто обернуть его в FrameLayout с прозрачным windowBackground.

person Tinashe    schedule 24.11.2016
comment
Это было решение! - person Аксенов Владимир; 11.09.2017

У меня была такая же проблема, для меня она была вызвана тем, что recyclerview выполнял обновления после / во время первого перехода на выход. Я думаю, что представление общего элемента тогда иногда перерабатывалось, что означало, что оно больше не было доступно для анимации перехода, отсюда и сбой (обычно при переходе возврата, но иногда при переходе выхода). Я решил это, заблокировав обновления, если действие приостановлено (использовался флаг isRunning) - обратите внимание, что он приостанавливался, но не останавливался, поскольку он все еще был виден в фоновом режиме. Кроме того, я заблокировал процесс обновления, если переход был запущен. Мне хватило этого, чтобы послушать этот обратный вызов:

Transition sharedElementExitTransition = getWindow().getSharedElementExitTransition();
        if (sharedElementExitTransition != null) {
            sharedElementExitTransition.addListener(.....);
        }

В качестве последней меры, хотя я не уверен, что это имело значение, я также сделал recyclerView.setLayoutFrozen(true) / recyclerView.setLayoutFrozen(false) в onTransitionStart / onTransitionEnd.

person hmac    schedule 28.02.2017

Убедитесь, что "itemView", который вы передаете при переходе, - это просмотр, по которому щелкнули (полученный вашим обратным вызовом onClick ())

person Fabio Rocha    schedule 26.02.2015

Я столкнулся с той же проблемой, на самом деле я использовал firebase, и у меня есть список информации, и когда пользователь нажимает, он вызывает detailActivity с sharedAnimation в этом действии, я обновлял его, как видно с помощью firebase, поэтому событие firebase обновляет элемент списка, как видно, в этом В случае, если эта проблема возникает из-за того, что переработчик считает, что макет экрана выполнялся.

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

onPause () Я заморозил макет, а onResume () установил его как false;

 @Override
public void onPause() {
    super.onPause();
    mRecycler.setLayoutFrozen(true);
}

@Override
public void onResume() {
    super.onResume();
    mRecycler.setLayoutFrozen(false);
}

И это работает.

person Abdul Rizwan    schedule 10.07.2017
comment
Была ли эта проблема с использованием RecyclerView (обновления запускались в остановленном состоянии). Замораживание макета помогло. - person Demigod; 26.03.2019
comment
Спасибо, похоже, в моем случае, но setLayoutFrozen () теперь устарел, вместо этого я использовал suppressLayout (). - person Hack06; 08.08.2019

Я придумал, как избежать возврата к Activity с помощью RecyclerView или изменения обратного перехода с помощью чего-то еще.

Отключить все возвратные переходы:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void finishAfterTransition() {
    finish();
}

Или, если вы хотите отключить возврат возврата только общих элементов и иметь возможность установить свой собственный возвратный переход:

// Track if finishAfterTransition() was called
private boolean mFinishingAfterTransition;

@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mFinishingAfterTransition = false;
}

public boolean isFinishingAfterTransition() {
    return mFinishingAfterTransition;
}

@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void finishAfterTransition() {
    mFinishingAfterTransition = true;
    super.finishAfterTransition();
}

public void clearSharedElementsOnReturn() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        TransitionUtilsLollipop.clearSharedElementsOnReturn(this);
    }
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static final class TransitionUtilsLollipop {

    private TransitionUtilsLollipop() {
        throw new UnsupportedOperationException();
    }

    static void clearSharedElementsOnReturn(@NonNull final BaseActivity activity) {
        activity.setEnterSharedElementCallback(new SharedElementCallback() {

            @Override
            public void onMapSharedElements(final List<String> names,
                    final Map<String, View> sharedElements) {
                super.onMapSharedElements(names, sharedElements);
                if (activity.isFinishingAfterTransition()) {
                    names.clear();
                    sharedElements.clear();
                }
            }
        });
    }

Благодаря тому, что это реализовано в базовом действии, вы можете легко использовать его в onCreate ()

@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    clearSharedElementsOnReturn(this);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // set your own transition
        getWindow().setReturnTransition(new VerticalGateTransition());
    }
}
person Yaroslav Mytkalyk    schedule 02.12.2016

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

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

View view = this.getCurrentFocus();
if (view != null) {  
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
supportFinishAfterTransition();
person Hammy    schedule 21.12.2016

Как сказал @Fabio Rocha, убедитесь, что itemView извлекается из ViewHolder.

Вы можете получить ViewHolder по позиции через

mRecyclerView.findViewHolderForAdapterPosition(position);
person Chaos    schedule 02.03.2017

Причина этого на самом деле довольно проста: когда вы переходите обратно к родительскому Activity или Fragment, View еще не существует (может быть по многим причинам).
Итак, вы хотите отложить переход ввода до тех пор, пока не станет доступным представление.

Моя работа - вызвать следующую функцию в onCreate () в моем фрагменте (но работает и в Activity):

private void checkBeforeTransition() {
    // Postpone the transition until the window's decor view has
    // finished its layout.
    getActivity().supportPostponeEnterTransition();

    final View decor = getActivity().getWindow().getDecorView();
    decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            decor.getViewTreeObserver().removeOnPreDrawListener(this);
            getActivity().supportStartPostponedEnterTransition();
            return true;
        }
    });
}
person Andy Strife    schedule 22.05.2017

Получил ту же проблему, и это вызвано обновлением представления ресайклера в фоновом режиме, представление ресайклера воссоздает представление при notifyItemChanged (int index), поэтому представление общего ресурса было переработано, и при возврате произошел сбой.

Мое решение - call recyclerView.setItemAnimator(null);, и оно предотвратит воссоздание представления ресайклера.

person Glorin    schedule 31.10.2018