Skip to content

Instantly share code, notes, and snippets.

@rbenza
Last active November 13, 2021 12:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rbenza/763e10b88f9e187ab98eb351df6b97b8 to your computer and use it in GitHub Desktop.
Save rbenza/763e10b88f9e187ab98eb351df6b97b8 to your computer and use it in GitHub Desktop.
Android Lifecycle aware View extensions
import android.os.Looper
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import androidx.annotation.MainThread
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.slider.RangeSlider
import timber.log.Timber
fun TextView.onTextChanged(
lifecycleOwner: LifecycleOwner,
action: (text: CharSequence?, start: Int?, before: Int?, count: Int?) -> Unit
): TextWatcher {
val textWatcher = addTextChangedListener(onTextChanged = action)
setupListener(
lifecycleOwner = lifecycleOwner,
addListener = { addTextChangedListener(textWatcher) },
removeListener = { removeTextChangedListener(textWatcher) },
log = "onTextChanged"
)
return textWatcher
}
fun View.onFocusChanged(
lifecycleOwner: LifecycleOwner,
action: (view: View, hasFocus: Boolean) -> Unit
): View.OnFocusChangeListener {
val focusListener = View.OnFocusChangeListener { view: View, hasFocus: Boolean ->
action.invoke(view, hasFocus)
}
setupListener(
lifecycleOwner = lifecycleOwner,
addListener = { onFocusChangeListener = focusListener },
removeListener = { onFocusChangeListener = null },
log = "onFocusChanged"
)
return focusListener
}
fun RangeSlider.onSliderTouched(
lifecycleOwner: LifecycleOwner,
onStartTouch: (slider: RangeSlider) -> Unit,
onStopTouch: (slider: RangeSlider) -> Unit,
): RangeSlider.OnSliderTouchListener {
val sliderListener = object : RangeSlider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: RangeSlider) {
onStartTouch(slider)
}
override fun onStopTrackingTouch(slider: RangeSlider) {
onStopTouch(slider)
}
}
setupListener(
lifecycleOwner = lifecycleOwner,
addListener = { addOnSliderTouchListener(sliderListener) },
removeListener = { removeOnSliderTouchListener(sliderListener) },
log = "onSliderTouched"
)
return sliderListener
}
fun RangeSlider.onChanged(
lifecycleOwner: LifecycleOwner,
action: (slider: RangeSlider, value: Float?, fromUser: Boolean?) -> Unit,
): RangeSlider.OnChangeListener {
val onChangedListener = RangeSlider.OnChangeListener { slider: RangeSlider, value: Float, fromUser: Boolean ->
action(slider, value, fromUser)
}
setupListener(
lifecycleOwner = lifecycleOwner,
addListener = { addOnChangeListener(onChangedListener) },
removeListener = { removeOnChangeListener(onChangedListener) },
log = "onChanged"
)
return onChangedListener
}
fun AppBarLayout.onOffsetChanged(
lifecycleOwner: LifecycleOwner,
action: (layout: AppBarLayout, verticalOffset: Int) -> Unit
): AppBarLayout.OnOffsetChangedListener {
val offsetListener = AppBarLayout.OnOffsetChangedListener { layout: AppBarLayout, verticalOffset: Int ->
action.invoke(layout, verticalOffset)
}
setupListener(
lifecycleOwner = lifecycleOwner,
addListener = { addOnOffsetChangedListener(offsetListener) },
removeListener = { removeOnOffsetChangedListener(offsetListener) },
log = "onOffsetChanged"
)
return offsetListener
}
fun View.onTouched(
lifecycleOwner: LifecycleOwner,
action: (view: View, event: MotionEvent?) -> Boolean
): View.OnTouchListener {
val onTouchListener = View.OnTouchListener { view: View, event: MotionEvent? ->
action.invoke(view, event)
false
}
setupListener(
lifecycleOwner = lifecycleOwner,
addListener = { setOnTouchListener(onTouchListener) },
removeListener = { setOnTouchListener(null) },
log = "onTouched"
)
return onTouchListener
}
fun TextView.onEditorAction(
lifecycleOwner: LifecycleOwner,
action: (tv: TextView, actionId: Int?, event: KeyEvent?) -> Boolean
): TextView.OnEditorActionListener {
val editorListener = TextView.OnEditorActionListener { tv: TextView, actionId: Int?, event: KeyEvent? ->
action.invoke(tv, actionId, event)
}
setupListener(
lifecycleOwner = lifecycleOwner,
addListener = { setOnEditorActionListener(editorListener) },
removeListener = { setOnEditorActionListener(null) },
log = "onEditorAction"
)
return editorListener
}
@MainThread
private fun setupListener(
lifecycleOwner: LifecycleOwner,
addListener: () -> Unit,
removeListener: () -> Unit,
log: String = ""
) {
if (Looper.getMainLooper().isCurrentThread) {
val observer = object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
addListener()
Timber.tag("~~~").d("addListener CALLED for $log")
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
removeListener()
Timber.tag("~~~").d("removeListener CALLED for $log")
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
Timber.tag("~~~").d("removeObserver CALLED for $log")
lifecycleOwner.lifecycle.removeObserver(this)
}
}
lifecycleOwner.lifecycle.addObserver(observer)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment