android - Android 中的汉堡菜单图标交叉动画

标签 android animation

我正在尝试用十字图标替换菜单图标,但我不知道比替换 ImageView 中的源更好的解决方案,而且我找不到转换图像的完成库。

Menu icon Cross icon

任何帮助将不胜感激。

最佳答案

Android 有一个可绘制的汉堡包和箭头之间的动画:android.support.v7.graphics.drawable.DrawerArrowDrawable 该可绘制对象使用非常通用的 Canvas 绘制方法。如果您有一些空闲时间并准备好进行一些乏味的工作,那么您可以通过查看此示例来制作几乎所有内容的动画。

例如,这里是“hamburger”来交叉绘制:

/**
 * Simple animating drawable between the "hamburger" icon and cross icon
 *
 * Based on [android.support.v7.graphics.drawable.DrawerArrowDrawable]
 */
class HamburgerCrossDrawable(
        /** Width and height of the drawable (the drawable is always square) */
        private val size: Int,
        /** Thickness of each individual line */
        private val barThickness: Float,
        /** The space between bars when they are parallel */
        private val barGap: Float
) : Drawable() {

    private val paint = Paint()
    private val thick2 = barThickness / 2.0f

    init {
        paint.style = Paint.Style.STROKE
        paint.strokeJoin = Paint.Join.MITER
        paint.strokeCap = Paint.Cap.BUTT
        paint.isAntiAlias = true

        paint.strokeWidth = barThickness
    }

    override fun draw(canvas: Canvas) {
        if (progress < 0.5) {
            drawHamburger(canvas)
        } else {
            drawCross(canvas)
        }
    }

    private fun drawHamburger(canvas: Canvas) {
        val bounds = bounds
        val centerY = bounds.exactCenterY()
        val left = bounds.left.toFloat() + thick2
        val right = bounds.right.toFloat() - thick2

        // Draw middle line
        canvas.drawLine(
                left, centerY,
                right, centerY,
                paint)

        // Calculate Y offset to top and bottom lines
        val offsetY = barGap * (2 * (0.5f - progress))

        // Draw top & bottom lines
        canvas.drawLine(
                left, centerY - offsetY,
                right, centerY - offsetY,
                paint)
        canvas.drawLine(
                left, centerY + offsetY,
                right, centerY + offsetY,
                paint)
    }

    private fun drawCross(canvas: Canvas) {
        val bounds = bounds
        val centerX = bounds.exactCenterX()
        val centerY = bounds.exactCenterY()
        val crossHeight = barGap * 2 + barThickness * 3
        val crossHeight2 = crossHeight / 2

        // Calculate current cross position
        val distanceY = crossHeight2 * (2 * (progress - 0.5f))
        val top = centerY - distanceY
        val bottom = centerY + distanceY
        val left = centerX - crossHeight2
        val right = centerX + crossHeight2

        // Draw cross
        canvas.drawLine(
                left, top,
                right, bottom,
                paint)
        canvas.drawLine(
                left, bottom,
                right, top,
                paint)
    }

    override fun setAlpha(alpha: Int) {
        if (alpha != paint.alpha) {
            paint.alpha = alpha
            invalidateSelf()
        }
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
        paint.colorFilter = colorFilter
        invalidateSelf()
    }

    override fun getIntrinsicWidth(): Int {
        return size
    }

    override fun getIntrinsicHeight(): Int {
        return size
    }

    override fun getOpacity(): Int {
        return PixelFormat.TRANSLUCENT
    }

    /**
     * Drawable color
     * Can be animated
     */
    var color: Int = 0xFFFFFFFF.toInt()
        set(value) {
            field = value
            paint.color = value
            invalidateSelf()
        }

    /**
     * Animate this property to transition from hamburger to cross
     * 0 = hamburger
     * 1 = cross
     */
    var progress: Float = 0.0f
        set(value) {
            field = value.coerceIn(0.0f, 1.0f)
            invalidateSelf()
        }

}

您可以像任何其他可绘制对象一样使用此可绘制对象,例如通过将 ImageView src 设置为此可绘制对象:

imageView = AppCompatImageView(context)
addView(imageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
hamburgerDrawable = HamburgerCrossDrawable(
        size = dpToPx(20).toInt(),
        barThickness = dpToPx(2),
        barGap = dpToPx(5)
)
hamburgerDrawable.color = hamburgerColor
imageView.setImageDrawable(hamburgerDrawable)

要使可绘制对象实际上从汉堡包变为十字形并返回,您需要更改HamburgerCrossDrawable.progress(0代表汉堡包,1代表十字形):

val animator = ValueAnimator.ofFloat(0.0f, 1.0f)
animator.interpolator = AccelerateDecelerateInterpolator()
animator.duration = 300
animator.addUpdateListener {
    val progress = it.animatedValue as Float
    val color = interpolateColor(hamburgerColor, crossColor, progress)
    hamburgerDrawable.color = color
    hamburgerDrawable.progress = progress
}
animator.start()

关于android - Android 中的汉堡菜单图标交叉动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35716854/

相关文章:

javascript - jquery animate jerky - 是否有更好的缩放图像库?

java - 无法使用 Intent 和 Parcelable 传输对象

android - 谷歌地图每秒更新一次当前位置

android - 如何杀死android中所有正在运行的应用程序?

android - 使用 asynctask 进行多次下载

java - 如何获取 TranslateAnimation 中的位置?

ios - 需要在 iOS 应用程序上挥舞旗帜效果

android - 如何编写应用程序将 Android 设备连接到主机 USB GPS 设备

css - Sass 顺序动画

html - 使用多个 span 的 CSS 打字动画