How to smoothly change drawable resource during MotionLayout transition?

纵饮孤独 提交于 2020-07-07 12:25:32

问题


I want to change image in fab button during transition, but i haven't found how to do it with xml because CustomAttribute tag supports only drawable colors as values. My solution is to set TransitionAdapter to MotionLayout and change drawable in onTransitionChange function.

motionLayout.setTransitionListener(object : TransitionAdapter() {
            var fromStart = true
            var wasChanged = false

            override fun onTransitionChange(
                motionLayout: MotionLayout?,
                startId: Int,
                endId: Int,
                progress: Float
            ) {
                if (!wasChanged) {
                    if (fromStart && progress >= 0.5f) {
                        fab.setImageResource(R.drawable.ic_done_black_24dp)
                        wasChanged = true
                    }
                    if (!fromStart && progress <= 0.5f) {
                        fab.setImageResource(R.drawable.ic_add_black_24dp)
                        wasChanged = true
                    }
                }
            }

            override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
                wasChanged = false
                fromStart = !fromStart
            }

        })

But in this case image changes immediately. Is there any way to make the transition smooth like in regular transition in MotionLayout?


回答1:


There is a great way to animate the change between two images in MotionLayout. However, it may be a little tricky with the Fab interaction. As shown in this blog post, you can use an ImageFilterView and set the source and alternate source, then crossfade between them. Grabbing code directly from the blog post linked above, here's an example of what an ImageFilterView looks like in your MotionLayout

<android.support.constraint.utils.ImageFilterView
        android:id="@+id/image"
        android:background="@color/colorAccent"
        android:src="@drawable/roard"
        app:altSrc="@drawable/hoford"
        android:layout_width="64dp"
        android:layout_height="64dp"/>

Where android:src is your first image that you want and the app:altSrc is the second image that you want to crossfade to. Below, also grabbed directly from the blog post, is what the constraints will look like in your motionScene.

<ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/image"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginStart="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="crossfade"
                motion:customFloatValue="0" />
        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/image"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginEnd="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="crossfade"
                motion:customFloatValue="1" />
        </Constraint>
    </ConstraintSet>

Where you set the crossfade value as a custom attribute. Not sure how this will play with the Fab but this is the best way I know how to animate between two images.




回答2:


Try using TransitionDrawable to animate and change FloatingActionButton drawable smoothly.

 private fun setMotionListener() {
        val transitionDrawable = TransitionDrawable(
            arrayOf(
                bitmapDrawableFromVector(this, R.drawable.ic_add),
                bitmapDrawableFromVector(this, R.drawable.ic_remove)
            )
        )
        transitionDrawable.isCrossFadeEnabled = true
        credits.setImageDrawable(transitionDrawable)

        motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
            override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
            }

            override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
            }

            override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
            }

            override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
                transitionDrawable.apply {
                    if (p0?.currentState == p0?.endState)
                        startTransition(0)
                    else
                        reverseTransition(200)
                }
            }
        })
    }

Be aware that VectorDrawables won't be cross-faded in theTransitionDrawable, You'll need to convert them into BitmapDrawables first. See the below code -

 private fun bitmapDrawableFromVector(
        context: Context,
        drawableId: Int
    ) = BitmapDrawable(context.resources, getBitmapFromVectorDrawable(context, drawableId))

    private fun getBitmapFromVectorDrawable(context: Context, drawableId: Int): Bitmap? {
        val drawable = ContextCompat.getDrawable(context, drawableId)
        val bitmap = Bitmap.createBitmap(
            drawable!!.intrinsicWidth,
            drawable.intrinsicHeight,
            Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, canvas.width, canvas.height)
        drawable.draw(canvas)
        return bitmap
    }

Thanks.



来源:https://stackoverflow.com/questions/60532150/how-to-smoothly-change-drawable-resource-during-motionlayout-transition

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!