Как плавно изменить доступный для рисования ресурс во время перехода MotionLayout?

Я хочу изменить изображение в кнопке fab во время перехода, но я не нашел, как это сделать с помощью xml, потому что тег CustomAttribute поддерживает только рисованные цвета в качестве значений. Мое решение - установить для TransitionAdapter значение MotionLayout и изменить возможность рисования в функции onTransitionChange.

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
            }

        })

Но в этом случае изображение сразу меняется. Есть ли способ сделать переход плавным, как при обычном переходе в MotionLayout?


person Shmuser    schedule 04.03.2020    source источник


Ответы (2)


Есть отличный способ анимировать смену двух изображений в MotionLayout. Однако с взаимодействием Fab это может быть немного сложно. Как показано в этом сообщении в блоге, вы можете использовать ImageFilterView и установите источник и альтернативный источник, а затем выполните перекрестное затухание между ними. Взяв код прямо из сообщения блога, указанного выше, вот пример того, как ImageFilterView выглядит в вашем 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"/>

Где android:src - ваше первое изображение, которое вы хотите, а app:altSrc - второе изображение, к которому вы хотите перейти. Ниже, также взято непосредственно из сообщения в блоге, показано, как ограничения будут выглядеть в вашем 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>

Где вы устанавливаете значение кроссфейда как настраиваемый атрибут. Не уверен, как это будет работать с Fab, но это лучший способ анимировать между двумя изображениями.

person kjanderson2    schedule 04.03.2020
comment
Спасибо за ответ, раньше я ничего не слышал об ImageFilterView. Но вы правы, с fab все не так очевидно. - person Shmuser; 07.03.2020
comment
в то время как изображения можно использовать с src, но вопрос касается чертежей - person vahid; 25.01.2021

Попробуйте использовать TransitionDrawable для анимации и плавного изменения FloatingActionButton drawable.

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

Имейте в виду, что VectorDrawables не будет затухать в theTransitionDrawable, вам нужно сначала преобразовать их в BitmapDrawables. См. Код ниже -

 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
    }

Спасибо.

person Anoop M Maddasseri    schedule 18.06.2020