Last active
October 12, 2021 12:21
-
-
Save jsh-me/bede73802fa624f4196889bb23d43f38 to your computer and use it in GitHub Desktop.
Implement SingleClick and LongClick using onTouchListener
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.annotation.SuppressLint | |
import android.view.MotionEvent | |
import android.view.View | |
import io.reactivex.android.schedulers.AndroidSchedulers | |
import io.reactivex.disposables.CompositeDisposable | |
import io.reactivex.rxkotlin.plusAssign | |
import io.reactivex.schedulers.Schedulers | |
import io.reactivex.subjects.BehaviorSubject | |
import java.util.* | |
import javax.inject.Inject | |
import javax.inject.Singleton | |
@Singleton | |
class ViewTouchEventDetector @Inject constructor() { | |
private var elapsedSecond = 0 | |
private lateinit var targetView: View | |
private lateinit var touchTimer: Timer | |
private val compositeDisposable = CompositeDisposable() | |
private val singleClickDetector = BehaviorSubject.create<Unit>() | |
private val longClickDetector = BehaviorSubject.create<Unit>() | |
private val touchUpEventDetector = BehaviorSubject.create<Unit>() | |
@SuppressLint("ClickableViewAccessibility") | |
fun bindTargetViewEvent( | |
view: View, | |
) { | |
targetView = view | |
targetView.setOnTouchListener { _, event -> | |
when (event.action) { | |
MotionEvent.ACTION_DOWN -> { | |
startTimer() | |
true | |
} | |
MotionEvent.ACTION_UP, | |
MotionEvent.ACTION_CANCEL, | |
-> { | |
cancelTimer() | |
true | |
} | |
else -> true | |
} | |
} | |
} | |
fun setViewClickEvent( | |
touchUpListener: () -> Unit, | |
singleClickListener: () -> Unit, | |
longClickListener: () -> Unit, | |
) { | |
clickEventDetector( | |
singleClickListener, | |
longClickListener, | |
touchUpListener | |
) | |
} | |
fun clearClickEvent() { | |
targetView.setOnTouchListener(null) | |
} | |
private fun clickEventDetector(singleClickListener: () -> Unit, longClickListener: () -> Unit, touchUpListener: () -> Unit) { | |
initState { | |
touchUpListener() | |
} | |
compositeDisposable += singleClickDetector | |
.subscribeOn(Schedulers.computation()) | |
.observeOn(AndroidSchedulers.mainThread()) | |
.subscribe { | |
singleClickListener() | |
} | |
compositeDisposable += longClickDetector | |
.subscribeOn(Schedulers.computation()) | |
.observeOn(AndroidSchedulers.mainThread()) | |
.subscribe { | |
longClickListener() | |
} | |
compositeDisposable += Observable.zip( | |
longClickDetector, | |
touchUpEventDetector | |
) { _, _ -> } | |
.subscribeOn(Schedulers.computation()) | |
.observeOn(AndroidSchedulers.mainThread()) | |
.subscribe { | |
touchUpListener() | |
} | |
} | |
private fun initState(init: () -> Unit) = init() | |
private fun startTimer() { | |
touchTimer = kotlin.concurrent.timer(period = MILLIS_OF_SECOND) { | |
elapsedSecond++ | |
checkUserLongPressed() | |
} | |
} | |
private fun checkUserLongPressed() { | |
if (elapsedSecond >= LONG_PRESSED_TIME) { | |
longClickDetector.onNext(Unit) | |
} | |
} | |
private fun cancelTimer() { | |
touchTimer.cancel() | |
touchUpEventDetector.onNext(Unit) | |
checkUserSinglePressed() | |
elapsedSecond = 0 | |
} | |
private fun checkUserSinglePressed() { | |
if (elapsedSecond < LONG_PRESSED_TIME) { | |
singleClickDetector.onNext(Unit) | |
} | |
} | |
fun clear() { | |
compositeDisposable.clear() | |
} | |
companion object { | |
/** Long Press 판단 기준 시간 */ | |
private const val LONG_PRESSED_TIME = 2L | |
private const val MILLIS_OF_SECOND = 1000L | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment