Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting the most out of Android KTX

Getting the most out of Android KTX

Video: https://www.youtube.com/watch?v=B8ei4rHAHEA

Ever wish you could just use a lambda as an onClickListener? Or change the View visibility by modifying a simple "isVisible" boolean? Or how about using a doOnLayout{} method to run a block of code after view layout?

These are just a few examples of how Android KTX extensions help solve common problems. They improve the existing Jetpack and Android platform APIs so you can consume them from Kotlin in a concise and idiomatic way.

This talk will discuss what KTX extensions are and explore the most useful extensions that you can use in your daily workflow.

Jeroen Mols

April 20, 2020
Tweet

More Decks by Jeroen Mols

Other Decks in Programming

Transcript

  1. @MOLSJEROEN ANDROID CAN SOMETIMES BE TEDIOUS view.addOnLayoutChangeListener( object : View.OnLayoutChangeListener

    { override fun onLayoutChange( view: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) { view.removeOnLayoutChangeListener(this) doOnWidthAndHeightKnown() } })
  2. @MOLSJEROEN ANDROID CAN SOMETIMES BE TEDIOUS view.addOnLayoutChangeListener( object : View.OnLayoutChangeListener

    { override fun onLayoutChange( view: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) { view.removeOnLayoutChangeListener(this) doOnWidthAndHeightKnown() } })
  3. @MOLSJEROEN ANDROID CAN SOMETIMES BE TEDIOUS view.addOnLayoutChangeListener( object : View.OnLayoutChangeListener

    { override fun onLayoutChange( view: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) { view.removeOnLayoutChangeListener(this) doOnWidthAndHeightKnown() } })
  4. @MOLSJEROEN ANDROID CAN SOMETIMES BE TEDIOUS view.addOnLayoutChangeListener( object : View.OnLayoutChangeListener

    { override fun onLayoutChange( view: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) { view.removeOnLayoutChangeListener(this) doOnWidthAndHeightKnown() } })
  5. @MOLSJEROEN KTX UNDER THE HOOD inline fun View.doOnLayout( crossinline action:

    (view: View) -> Unit ) { if (ViewCompat.isLaidOut(this) && !isLayoutRequested) { action(this) } else { doOnNextLayout { action(it) } } }
  6. @MOLSJEROEN KTX UNDER THE HOOD inline fun View.doOnLayout( crossinline action:

    (view: View) -> Unit ) { if (ViewCompat.isLaidOut(this) && !isLayoutRequested) { action(this) } else { doOnNextLayout { action(it) } } }
  7. @MOLSJEROEN KTX UNDER THE HOOD inline fun View.doOnLayout( crossinline action:

    (view: View) -> Unit ) { if (ViewCompat.isLaidOut(this) && !isLayoutRequested) { action(this) } else { doOnNextLayout { action(it) } } }
  8. @MOLSJEROEN KTX UNDER THE HOOD inline fun View.doOnLayout( crossinline action:

    (view: View) -> Unit ) { if (ViewCompat.isLaidOut(this) && !isLayoutRequested) { action(this) } else { doOnNextLayout { action(it) } } }
  9. @MOLSJEROEN KTX UNDER THE HOOD inline fun View.doOnNextLayout( crossinline action:

    (view: View) -> Unit ) { addOnLayoutChangeListener(object : View.OnLayoutChangeListener { override fun onLayoutChange( view: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int ) { view.removeOnLayoutChangeListener(this) action(view) } }) }
  10. @MOLSJEROEN HOW TO USE KTX repositories { google() } dependencies

    { implementation "androidx.core:core:1.2.0" }
  11. @MOLSJEROEN HOW TO USE KTX repositories { google() } dependencies

    { implementation "androidx.core:core:1.2.0" }
  12. @MOLSJEROEN HOW TO USE KTX repositories { google() } dependencies

    { implementation “androidx.core:core-ktx:1.2.0” }
  13. @MOLSJEROEN ANIMATIONS animator.addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator?)

    = doSomething(animation!!) override fun onAnimationRepeat(animation: Animator?) = doSomething(animation!!) override fun onAnimationEnd(animation: Animator?) = doSomething(animation!!) override fun onAnimationCancel(animation: Animator?) = doSomething(animation!!) })
  14. @MOLSJEROEN ANIMATIONS val animator = ObjectAnimator.ofFloat(view, ALPHA, 0f, 1f) animator.doOnStart

    { doSomething(it) } animator.doOnRepeat { doSomething(it) } animator.doOnCancel { doSomething(it) } animator.doOnEnd { doSomething(it) }
  15. @MOLSJEROEN SYSTEM SERVICES // requires android M val layoutInflater =

    context .getSystemService(LayoutInflater::class.java)
  16. @MOLSJEROEN SYSTEM SERVICES // requires android M val layoutInflater =

    context .getSystemService(LayoutInflater::class.java) // any Android version context.getSystemService<LayoutInflater>()
  17. @MOLSJEROEN COLORS val red = Color.parseColor("#ff0000") val colorDrawable = ColorDrawable(red)

    val red = "#ff0000".toColorInt() val colorDrawable = red.toDrawable()
  18. @MOLSJEROEN VIEW view.doOnLayout { } view.doOnNextLayout { } view.doOnPreDraw {

    } view.doOnAttach { } view.doOnDetach { } view.marginStart view.marginEnd view.marginLeft view.marginRight view.marginTop view.marginBottom
  19. @MOLSJEROEN VIEW view.updateLayoutParams<ConstraintLayout.LayoutParams> { width = 200 // strong typed

    access height = 200 } view.updatePadding(right = 10) // only assign one val bitmap = view.drawToBitmap()
  20. @MOLSJEROEN VIEW VISIBILITY if (view.visibility == View.VISIBLE) { // do

    something } view.visibility = View.VISIBLE if (view.isVisible) { // do something } view.isVisible = false // View.GONE
  21. @MOLSJEROEN TEXTVIEW editText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?)

    = doWork() override fun beforeTextChanged(s: CharSequence?, start: Int,…) = doWork() override fun onTextChanged(s: CharSequence?, start: Int,…) = doWork() })
  22. @MOLSJEROEN TEXTVIEW editText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?)

    = doWork() override fun beforeTextChanged(s: CharSequence?, start: Int,…) = doWork() override fun onTextChanged(s: CharSequence?, start: Int,…) = doWork() }) editText.doOnTextChanged { text, start, count, after -> doWork() } editText.doAfterTextChanged { doWork() } editText.doBeforeTextChanged { text, start, count, after -> doWork() }
  23. @MOLSJEROEN VIEWMODELS - ACTIVITY // scoped to activity val viewmodel

    = ViewModelProviders.of(this) .get(MyViewModel::class.java)
  24. @MOLSJEROEN VIEWMODELS - ACTIVITY // scoped to activity val viewmodel

    = ViewModelProviders.of(this) .get(MyViewModel::class.java) // scoped to activity val viewModel by viewModels<MyViewModel>()
  25. @MOLSJEROEN VIEWMODELS - FRAGMENT // scoped to fragment val viewModel

    = ViewModelProviders.of(this) .get(MyViewModel::class.java) // scoped to activity val viewModel = ViewModelProviders.of(requireActivity()) .get(MyViewModel::class.java)
  26. @MOLSJEROEN VIEWMODELS - FRAGMENT // scoped to fragment val viewModel

    = ViewModelProviders.of(this) .get(MyViewModel::class.java) // scoped to activity val viewModel = ViewModelProviders.of(requireActivity()) .get(MyViewModel::class.java) // scoped to fragment val viewModel by viewModels<MyViewModel>() // scoped to activity val activityViewModel by activityViewModels<MyViewModel>()
  27. @MOLSJEROEN AND MANY MORE Benchmark Graphics Spannable SparseArrays Ranges Menu

    Lifecycle Livedata & flow Navigation Paging Preferences Room Slices Sqlite Workmanager Playcore … bit.ly/ktx-extensions
  28. @MOLSJEROEN LIMIT DECIMALS IN EDITTEXT fun EditText.limitDecimalsTo(numDecimals: Int) { filters

    += InputFilter { s, start, end, d, dstart, dend -> val newText = s.subSequence(start, end).toString() val result = StringBuilder(d) .replace(dstart, dend, newText).toString() val regex = “[0-9]*((\\.[0-9]{0,$numDecimals})?)||(\\.)?" .toRegex() if (regex.matches(result)) null else "" } } editText.limitDecimalsTo(2)
  29. @MOLSJEROEN LIMIT DECIMALS IN EDITTEXT fun EditText.limitDecimalsTo(numDecimals: Int) { filters

    += InputFilter { s, start, end, d, dstart, dend -> val newText = s.subSequence(start, end).toString() val result = StringBuilder(d) .replace(dstart, dend, newText).toString() val regex = “[0-9]*((\\.[0-9]{0,$numDecimals})?)||(\\.)?" .toRegex() if (regex.matches(result)) null else "" } } editText.limitDecimalsTo(2)
  30. @MOLSJEROEN LIMIT DECIMALS IN EDITTEXT fun EditText.limitDecimalsTo(numDecimals: Int) { filters

    += InputFilter { s, start, end, d, dstart, dend -> val newText = s.subSequence(start, end).toString() val result = StringBuilder(d) .replace(dstart, dend, newText).toString() val regex = “[0-9]*((\\.[0-9]{0,$numDecimals})?)||(\\.)?" .toRegex() if (regex.matches(result)) null else "" } } editText.limitDecimalsTo(2)
  31. @MOLSJEROEN TOAST fun Fragment.toast(@StringRes message: Int) = Toast.makeText(context, message, LENGTH_SHORT).show()

    fun Fragment.toast(message: String) = Toast.makeText(context, message, LENGTH_SHORT).show() toast(R.id.mymessage) toast("Hello there")
  32. @MOLSJEROEN TOAST fun Fragment.toast(@StringRes message: Int) = Toast.makeText(context, message, LENGTH_SHORT).show()

    fun Fragment.toast(message: String) = Toast.makeText(context, message, LENGTH_SHORT).show() toast(R.id.mymessage) toast("Hello there")
  33. @MOLSJEROEN FLOAT fun Float.isDecimalNumber() = this % 1 > 0.0001

    fun Float.decimalsOrRound(digits: Int) = if (isDecimalNumber()) { String.format("%.${digits}f", this) } else { String.format("%.0f", this) } 12.3f.isDecimalNumber() 12.3001f.decimalsOrRound(2)
  34. @MOLSJEROEN FLOAT fun Float.isDecimalNumber() = this % 1 > 0.0001

    fun Float.decimalsOrRound(digits: Int) = if (isDecimalNumber()) { String.format("%.${digits}f", this) } else { String.format("%.0f", this) } 12.3f.isDecimalNumber() 12.3001f.decimalsOrRound(2)
  35. @MOLSJEROEN TABSELECTED LISTENER fun TabLayout.doOnTabSelected(listener: (text: String) -> Unit) {

    clearOnTabSelectedListeners() addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabReselected(tab: TabLayout.Tab?) = Unit override fun onTabUnselected(tab: TabLayout.Tab?) = Unit override fun onTabSelected(tab: TabLayout.Tab?) = listener.invoke(tab!!.text.toString()) }) } tabLayout.doOnTabSelected { doSomething() }
  36. @MOLSJEROEN TABSELECTED LISTENER fun TabLayout.doOnTabSelected(listener: (text: String) -> Unit) {

    clearOnTabSelectedListeners() addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabReselected(tab: TabLayout.Tab?) = Unit override fun onTabUnselected(tab: TabLayout.Tab?) = Unit override fun onTabSelected(tab: TabLayout.Tab?) = listener.invoke(tab!!.text.toString()) }) } tabLayout.doOnTabSelected { doSomething() }