【问题标题】:How to smoothly change drawable resource during MotionLayout transition?如何在 MotionLayout 过渡期间平滑更改可绘制资源?
【发布时间】:2020-06-17 07:19:33
【问题描述】:

我想在转换期间更改 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 中的常规过渡一样使过渡平滑?

【问题讨论】:

    标签: android android-animation android-motionlayout


    【解决方案1】:

    有一种很好的方法可以在 MotionLayout 中为两个图像之间的变化设置动画。但是,与 Fab 交互可能有点棘手。如this blog post 所示,您可以使用 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 一起使用,但这是我知道如何在两个图像之间制作动画的最佳方式。

    【讨论】:

    • 感谢您的回答,我之前没有听说过有关 ImageFilterView 的任何消息。但你是对的,fab 并不是那么明显。
    • 虽然图片与src一起使用是正确的,但问题是关于drawables
    【解决方案2】:

    尝试使用TransitionDrawable 进行动画和更改 FloatingActionButton 可平滑绘制。

     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
        }
    

    谢谢。

    【讨论】:

      猜你喜欢
      • 2022-10-13
      • 1970-01-01
      • 2018-03-26
      • 1970-01-01
      • 1970-01-01
      • 2014-10-17
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      相关资源
      最近更新 更多