Search code examples
android-transitionsshared-element-transition

How to animate shared element in transition by moving the element in source activity to dest position?


I have a shared element in the transition from activity A to activity B. The shared element in A, however, uses a TransitionDrawable, and sometimes I want to fire the transition of the drawable, using startTransition, as part of the transition. (The shared element in B looks like the end state of the transition.)

The shared element transition documentation states that the implementation moves the shared element in B to the screen position of the shared element in A. Then it will run the shared element enter transition.

Is it possible to move the element in A to the B element's position (letting me run startTransition via shared element callback) instead of the other way? P.S.: I know about shared element exit transitions, but I don't think that helps me, because that always runs before the activity transition starts, and I don't see a way to change that, in the docs.


Solution

  • I leveraged the shared element snapshots via SharedElementCallback. While it's fairly complicated overall, here's the portion of my code that handles taking the snapshot and recreating a TransitionDrawable using the snapshot as the bottom layer.

    override fun onSharedElementEnd(sharedElementNames: MutableList<String>,
                                    sharedElements: MutableList<View>,
                                    sharedElementSnapshots: MutableList<View>?) {
        if (sharedElements.size < 1)
            return
        val shElem = sharedElements[0]
        if (shElem !is ImageView)
            return
        mainPhoto = shElem
        if (mainPhoto!!.drawable !is TransitionDrawable) {
            val layers = arrayOf(sharedElementSnapshots!![0].background, mainPhoto!!.drawable)
            if (exiting)
                layers.reverse()
            val transDrwbl = android.graphics.drawable.TransitionDrawable(layers)
            mainPhoto!!.setImageDrawable(transDrwbl)
        } else
            needToUseReverse = true
    }
    
    /**
     * I'm copying these from v21 SharedElementCallback.java because, while the
     * new functionality in v24 (v23?) seems nice, I don't want something unexpected
     * to happen on newer versions
     */
    override fun onCaptureSharedElementSnapshot(sharedElement: View, viewToGlobalMatrix: Matrix,
                                                screenBounds: RectF): Parcelable {
        val mTempMatrix = Matrix(viewToGlobalMatrix)
        val transitionUtils = Class.forName("android.transition.TransitionUtils")
        val createViewBitmap = transitionUtils.getDeclaredMethod("createViewBitmap", View::class.java, Matrix::class.java, RectF::class.java)
        val pcbl = createViewBitmap.invoke(null, sharedElement, mTempMatrix, screenBounds) as Parcelable
        return pcbl
    }
    
    override fun onMapSharedElements(names: MutableList<String>, sharedElements: MutableMap<String, View>) {
        for (key in sharedElements.keys.toList())
            if (key != currentSharedElementName)
                sharedElements.remove(key)
    }
    
    override fun onCreateSnapshotView(context: Context, snapshot: Parcelable): View? {
        var view: View? = null
        if (snapshot is Bitmap) {
            view = View(context)
            val resources = context.resources
            view.background = BitmapDrawable(resources, snapshot)
        }
        return view
    }