自定义视图中的涟漪效果

3

我目前正在创建一个Android视图,在用户点击它时,我将在点击的坐标周围显示一种涟漪效果。

但是我不确定如何实现。我的第一个想法是使缓存失效,并每次使圆圈变大,但是这样做似乎不合适也不高效。

如果有人之前遇到过同样的问题并且愿意分享一些如何实现的技巧,那将不胜感激。


1
通过设置可选项背景,即在xml中的“?android:attr/selectableItemBackground”,您可以在视图或布局上引起涟漪效应。在编程方面,可以在此解决方案中进行设置 https://dev59.com/i1oT5IYBdhLWcg3w8jFm ,同时您可能需要将其设置为可点击和可聚焦的状态。 - nik
嗨, 我认为这个解决方案对我没有帮助,因为它会在整个视图上应用涟漪效果(就像在按钮和列表项等上一样)。我想在第一个触摸视图的手指周围形成直径约为+-1厘米的圆圈(基本上是一个大彩色矩形)。 - Matthieu Meunier
2个回答

3

我终于找到了一个解决方案。虽然不是完美的,但现在它能够工作。

这是我编写的代码。基本上当我需要它时,我将布尔值更改为“true”,因此我的onDraw函数知道它必须执行drawFingerPrint函数。

另一方面,drawFingerPrint函数只是在每次迭代之间绘制一个越来越大的圆形,直到达到所需的直径。

private fun drawFingerPrint(canvas: Canvas) {
        canvas.drawCircle(pointerX, pointerY, radius, paint)

        if(radius<= 100F){
            radius+=10F
            invalidate()
        }
        else{
            radius = 0F
            drawAroundFinger = false
            invalidate()
        }
    }

我希望这个内容对其他人有所帮助!
马修

1

如@Matthieu所提到的,您需要在画布上绘制圆形并使视图失效。这里我提供一个更完整的示例。
所以我们有一个开放类RippleView

open class RippleView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    private var rippleX: Float? = null
    private var rippleY: Float? = null
    private var rippleRadius: Float? = null
    var maxRippleRadius: Float = 100f // Retrieve from resources
    var rippleColor: Int = 0x88888888.toInt()

    private val ripplePaint = Paint().apply {
        color = rippleColor
    }

    private val animationExpand = object : Runnable {
        override fun run() {
            rippleRadius?.let { radius ->
                if (radius < maxRippleRadius) {
                    rippleRadius = radius + maxRippleRadius * 0.1f
                    invalidate()
                    postDelayed(this, 10L)
                }
            }
        }
    }

    private val animationFade = object : Runnable {
        override fun run() {
            ripplePaint.color.let { color ->
                if (color.alpha > 10) {
                    ripplePaint.color = color.adjustAlpha(0.9f)
                    invalidate()
                    postDelayed(this, 10L)
                } else {
                    rippleX = null
                    rippleY = null
                    rippleRadius = null
                    invalidate()
                }
            }

        }
    }

    fun startRipple(x: Float, y: Float) {
        rippleX = x
        rippleY = y
        rippleRadius = maxRippleRadius * 0.15f
        ripplePaint.color = rippleColor

        animationExpand.run()
    }

    fun stopRipple() {
        if (rippleRadius != null) {
            animationFade.run()
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val x = rippleX ?: return
        val y = rippleY ?: return
        val r = rippleRadius ?: return

        canvas.drawCircle(x, y, r, ripplePaint)
    }
}

fun Int.adjustAlpha(factor: Float): Int =
    (this.ushr(24) * factor).roundToInt() shl 24 or (0x00FFFFFF and this)

inline val Int.alpha: Int
    get() = (this shr 24) and 0xFF

现在您可以使用RippleView扩展任何自定义视图,并在获取ACTION_DOWN时使用startRipple方法,以及在获取ACTION_UP时使用stopRipple方法:
class ExampleView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : RippleView(context, attrs), View.OnTouchListener {

    init {
        setOnTouchListener(this)
    }

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
               if (isInsideArea(event.x, event.y)) {
                   startRipple(event.x, event.y)
               }
            }

            MotionEvent.ACTION_UP -> {
                stopRipple()
            }
        }

        return false
    }

    private fun isInsideArea(x: Float, y: Float): Boolean {
        TODO("Not yet implemented")
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接