Last active
November 13, 2021 12:02
-
-
Save rbenza/763e10b88f9e187ab98eb351df6b97b8 to your computer and use it in GitHub Desktop.
Android Lifecycle aware View extensions
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.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