Android i Kotlin. Prosta blokada wieloklików. (gotowy kod do użycia).

Jeśli jesteś programistą Android to mam coś dla Ciebie. W tym wpisie przedstawię Ci prostą klasę pomocniczą, dzięki której pozbędziesz się problemu, który spędza sen z powiek wielu programistom. Mowa tu o szybkim klikaniu przez użytkowników w dany element interfejsu użytkownika, co  w konsekwencji może wywołać akcję wielokrotnie np. nawigacja do jakiegoś ekranu w aplikacji.

Temat ten już poruszałem na blogu, ale rozwiązanie, które zaproponowałem, bazuje na RxKotlin. Możesz przeczytać o tym tutaj:

W jaki sposób RxKotlin i Android Data Binding pomogły mi rozwiązać problem wieloklików?

Podczas pisania aplikacji mobilnych często spotykałem się z problemem obsługi kilku szybkich kliknięć przez użytkownika. Jeżeli przycisk ten prowadził do nowego widoku to bez obsługi tej sytuacji, pojawiała się kolejka tych samych żądań. W efekcie ekran docelowy pojawiał się kilka razy. Często radziłem sobie z tym po prostu blokując przycisk po pierwszym kliknięciu. Jednak ostatnio…

Read more

Rozwiązanie

Poniżej przedstawiam Ci gotowe rozwiązanie do użycia. 

class ActionDebouncer {
    private val minimumIntervalMillis: Long = 1000
    private var previousClickTimestamp: Long = 0

    fun <T> onAction(defaultValue: T, listener: (() -> T)): T {
        val currentTimestamp = SystemClock.uptimeMillis()
        val isSuccessed =
            abs(currentTimestamp - previousClickTimestamp) > minimumIntervalMillis

        previousClickTimestamp = currentTimestamp
        if (isSuccessed)
            return listener()

        return defaultValue
    }
}

Kod jest bardzo prosty. Metoda sprawdza, czy od jej ostatniego wywołania minęła odpowiednia ilość czasu, na tej podstawie wywołuje funkcję anonimową lub nie. Dodatkowo mamy możliwość zwrócenia dowolnego typu oraz podanie jego wartości domyślnej, w przypadku niewywołania funkcji anonimowej.

W jaki sposób możesz z tego skorzystać np. w kontekście przycisków? Najłatwiej jest stworzyć swoją kontrolkę opartą np. na MaterialButton. To pozwoli Ci na zablokowanie wieloklików w całej aplikacji. Wystarczy, że będziesz wykorzystywał tak przygotowany przycisk podczas budowy widoków w swojej aplikacji Android.

class MyMaterialButton : MaterialButton {

    private val actionDebouncer = ActionDebouncer()

    constructor(context: Context) : super(context) {
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
    }

    override fun performClick(): Boolean {
        return actionDebouncer.onAction(false) {
            super.performClick()
        }
    }
}

Zwróć uwagę, że bazowe wywołanie metody performClick() znajduję się wewnątrz anonimowej funkcji. Wywoła się ona tylko wtedy kiedy od ostatniego kliknięcia minęło więcej czasu niż ten ustalony  w klasie ActionDebouncer. Kiedy akcja się nie powiedzie (użytkownik kliknął kilkukrotnie) zostanie zwrócony false (domyślna wartość). Co w kontekście przycisku oznacza, że kilknięcię się nie powiodło. 

Jak będzie wyglądało użycie tej klasy w najprostszym przypadku np. w jednym konkretnym widoku? Dokładnie tak samo. Spójrz na kod poniżej.

private val actionDebouncer = ActionDebouncer()

private fun prepareView() {
    loginButton.setOnClickListener {
        actionDebouncer.onAction(Unit) {
            //Any action
        }
    }
}
thumbs brent GIF

Wykorzystuję tę klasę w swoich projektach, dzięki temu nie muszę martwić się o to, że użytkownik mnie zaskoczy i wykona jakąś akcję dwukrotnie lub (oby nie!) więcej razy.

Pozdrawiam! 😶

Newsletter

Zapisz się do mojego newslettera, aby nie przegapić nowych postów.

Dodatkowo wyślę Ci darmowego ebooka mojego autorstwa zawierającego mnóstwo wskazówek dla programisty aplikacji mobilnych.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Wypełnij to pole
Wypełnij to pole
Proszę wprowadzić prawidłowy adres email.
You need to agree with the terms to proceed

Menu