Android 5 पर साझा तत्व गतिविधि संक्रमण




android-animation android-recyclerview (8)

जब मैं एक गतिविधि से दूसरे में जा रहा हूं तो एक साझा तत्व संक्रमण को सेटअप करना चाहता था।

पहली गतिविधि में आइटम के साथ एक पुनर्नवीनीकरण दृश्य होता है। जब किसी आइटम पर क्लिक किया जाता है तो उस आइटम को नई गतिविधि के लिए चेतन करना चाहिए।

तो मैंने एक एंड्रॉइड सेट किया है: दोनों अंतिम गतिविधि के विचारों पर ट्रांस्फ़ॉर्मनाम = "आइटम", जैसा कि रिसाइक्लर-व्यू आइटम विचारों के रूप में स्वागत है।

अगली गतिविधि में जाने पर मैं इस कोड का उपयोग कर रहा हूं:

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 में बग की तरह दिखता है


इसका कारण वास्तव में काफी सरल है: जब आप मूल गतिविधि या फ्रैगमेंट में वापस नेविगेट करते हैं, तो दृश्य अभी तक नहीं है (कई कारणों से हो सकता है)।
इसलिए, आप क्या करना चाहते हैं, जब तक दृश्य उपलब्ध नहीं है, तब तक ट्रांज़िशन को स्थगित करना है

मेरा काम मेरे फ्रैगमेंट में निम्नलिखित कार्य को ऑनक्रिएट () में कॉल करना है (लेकिन गतिविधि में भी काम करता है):

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;
        }
    });
}

एक ही मुद्दा मिला है, और यह पृष्ठभूमि में पुनर्नवीनीकरण दृश्य अपडेट के कारण होता है, पुनरावृत्ति दृश्य जब NotItemChanged (int सूचकांक) को पुनः देखेगा, तो शेयर दृश्य पुनर्नवीनीकरण किया गया था और वापस आने पर यह दुर्घटनाग्रस्त हो गया।

मेरा समाधान कॉल recyclerView.setItemAnimator(null); , और यह पुनर्नवीनीकरण दृश्य को फिर से देखने से रोक देगा।


क्या मैं के साथ आया था 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());
    }
}

जैसा कि @ फैबियो रोचा ने कहा, सुनिश्चित करें कि itemView से लिया ViewHolder

आप ViewHolder माध्यम से स्थिति से प्राप्त कर सकते हैं

mRecyclerView.findViewHolderForAdapterPosition(position);

मेरे पास यह एक ही मुद्दा था, मेरे लिए यह पहली बार बाहर निकलने के संक्रमण के दौरान / उसके बाद के अपडेट को पुनरावृत्ति करने के कारण हो रहा था। मुझे लगता है कि साझा तत्व दृश्य कभी-कभी पुनर्नवीनीकरण हो रहा था, जिसका अर्थ है कि यह अब संक्रमण एनीमेशन के लिए उपलब्ध नहीं होगा, इसलिए दुर्घटना (सामान्य रूप से वापसी संक्रमण पर लेकिन कभी-कभी निकास संक्रमण पर)। यदि गतिविधि रोक दी जाती है (एक isRunning ध्वज का उपयोग किया जाता है) - तो मैंने इसे अपडेट करके अवरुद्ध कर दिया - ध्यान दें कि यह रोक रहा था लेकिन रोक नहीं रहा था क्योंकि यह अभी भी पृष्ठभूमि में दिखाई दे रहा था। यदि संक्रमण चल रहा था, तो इसके अतिरिक्त मैंने अद्यतन प्रक्रिया को अवरुद्ध कर दिया है। मुझे इस कॉलबैक को सुनने के लिए पर्याप्त मिला:

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

एक अंतिम उपाय के रूप में, हालांकि मुझे यकीन नहीं है कि अगर इससे कोई फर्क पड़ता है, तो मैंने onTransitionStart / onTransitionEnd में recyclerView.setLayoutFrozen(true) / recyclerView.setLayoutFrozen(false) भी किया।


मैं एक ही मुद्दे का सामना करता हूं, और ध्यान दें कि दुर्घटना तब होती है जब मूल साझा तत्व अब पिछली स्क्रीन पर दिखाई नहीं देता है जब आप वापस जाते हैं (शायद यह चित्र में स्क्रीन पर अंतिम तत्व है, लेकिन एक बार परिदृश्य पर स्विच करने के बाद यह अब दिखाई नहीं देता है) , और इस प्रकार परिवर्तन साझा तत्व को वापस लाने के लिए कहीं नहीं है।

मेरा वर्कअराउंड रिटर्न ट्रांज़िशन (दूसरी गतिविधि में) को हटाने के लिए है यदि स्क्रीन को वापस जाने से पहले घुमाया गया है, लेकिन मुझे यकीन है कि इसे संभालने का एक बेहतर तरीका होना चाहिए:

@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();
    }
}

यदि आप Proguard का उपयोग कर रहे हैं तो इसे अपने नियम फ़ाइल में जोड़ने का प्रयास करें। मैं एक ही मुद्दा था और यह काम करने के लिए प्रकट होता है?

-keep public class android.app.ActivityTransitionCoordinator


सुनिश्चित करें कि आप जिस "वस्तु दृश्य" को संक्रमण में पास कर रहे हैं वह दृश्य है जिसे क्लिक किया गया है (आपके onClick () कॉलबैक पर प्राप्त हुआ)





activity-transition