Простой Ripple эффект своими руками для Android

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Любой Android разработчик работал с кнопками, поэтому видел ripple эффект и всю его красоту.

Иногда хочется реализовать что-нибудь кастомное нежели стандартные вещи, которые уже предоставляются компонентами Material Design.

Поэтому я решил написать наследник AppCompatImageView и сделать для него свой ripple эффект с минимальным количеством кода.

Сразу выкладываю код:

private class Watcher  {
    private val observers = mutableListOf<() -> Unit>()

    fun tellAbout() = observers.forEach { it.invoke() }
    fun replace(observer: () -> Unit) {
        if (observers.isNotEmpty()) {
            observers.clear()
        }
        observers.add(observer)
    }
}

private class Delay {
    fun waitForSomeInterval() {
        val interval: Long = 30000
        val start = System.nanoTime()
        var end: Long = 0
        do {
            end = System.nanoTime()
        } while (start + interval >= end)

    }
}

class RippleImageButton @JvmOverloads constructor(
    ctx: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatImageView(ctx, attrs, defStyleAttr) {

    private var radius: Float = 0f
    private var initial: Float = 0f
    private val step = 0.09f

    private var pointX = 0f
    private var pointY = 0f

    private var rippleColor = Color.argb(60, 33, 33, 150)

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


    init {
        isClickable = true
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        radius = w * 2f
    }

    private val watcher = Watcher()
    private var isRunning = false

    private val uiHandler = Handler(Looper.getMainLooper())
    private val threadPool = Executors.newSingleThreadExecutor()
    private val submits = mutableListOf<Future<*>>()

    private val rippleRunnable = Runnable {
        initial = 0f
        if (isRunning) {
            return@Runnable
        }
        isRunning = true
        while (isRunning) {
            initial += step
            if (initial > radius) {
                break
            }
            uiHandler.post { invalidate() }
            Delay().waitForSomeInterval()
        }
        uiHandler.post {
            isRunning = false
            watcher.tellAbout()
            invalidate()
        }
    }

    private fun stopLast() {
        isRunning = false
        submits.forEach { it.cancel(true) }
    }


    override fun onTouchEvent(event: MotionEvent): Boolean {

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                pointX = event.x
                pointY = event.y

                watcher.replace { }
                stopLast()

                uiHandler.postDelayed({
                    submits.add(threadPool.submit(rippleRunnable))
                }, 1L)

            }
            MotionEvent.ACTION_UP -> {
                if (isRunning) {
                    watcher.replace {
                        initial = 0f
                        invalidate()
                    }
                } else {
                    initial = 0f
                    invalidate()
                }
            }
        }
        return true
    }
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawCircle(pointX, pointY, initial, paint)

    }

}

Прошу прощения, что без пояснений, добавлю если кто-нибудь не разберется :)

Основная проблема заключается в создании маленькой длительности и возможности отменять предыдущий ripple эффект при повторном нажатии.

RippleImageButton далеко не является идеальным и не соотвествует Material Design стандартам, я лишь хотел показать возможность такого варианта.

Всем хорошего кода :)

Источник: https://habr.com/ru/post/577680/


Интересные статьи

Интересные статьи

Пользователи сообщили, что Google, похоже, автоматически установила приложение для уведомлений о заражении COVID-19 в Массачусетсе на некоторые Android-смартфоны. Система...
Предыстория Когда-то у меня возникла необходимость проверять наличие неотправленных сообщений в «1С-Битрикс: Управление сайтом» (далее Битрикс) и получать уведомления об этом. Пробле...
Ваши пользователи жалуются на то, что приложение очень быстро сажает заряд телефона? Запущенный фоновый сервис внезапно останавливается? Сообщения от FCM не доходят до пользователя? Ч...
Всем привет! Два месяца назад я и мой знакомый (для краткости, назовем его Илья) запустили свой стартап. Пффф… Скажите вы. Каждый день кто-то что-то запускает. Кто-то запускает в...
Когда вы устанавливаете Vivaldi Beta для Android, вы получаете модифицированную нашей командой разработчиков версию Chromium, аналогичную десктопной, обладающую теми же функциями, что вы прив...