Slide 1

Slide 1 text

Android , DataBinding ! , @fornewid @SOUP

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

View

Slide 4

Slide 4 text

val loadingView : View = findViewById(...)

Slide 5

Slide 5 text

// Kotlin loadingView .visibility = View .VISIBLE // Java loadingView.setVisibility(View.VISIBLE);

Slide 6

Slide 6 text

// Kotlin loadingView .visibility = if (isLoading) View .VISIBLE else View.GONE // Java loadingView.setVisibility( isLoading ? View.VISIBLE : View.GONE);

Slide 7

Slide 7 text

loadingView .is Visible = isLoading

Slide 8

Slide 8 text

Slide 9

Slide 9 text

Slide 10

Slide 10 text

Slide 11

Slide 11 text

KTX / DataBinding ݾ಴

Slide 12

Slide 12 text

KTX / DataBinding ݾ಴ View Layer ✂

Slide 13

Slide 13 text

Android

Slide 14

Slide 14 text

Android KTX Android Jetpack Kotlin extensions Link: https://developer.android.com/kotlin/ktx • Named parameters • Parameter default values • Coroutines • Extension functions • Extension properties • Lambdas

Slide 15

Slide 15 text

Core KTX: View implementation 'androidx.core:core-ktx:1.1.0' // 1.2.0-rc01 loadingView.visibility = if (isLoading) View.VISIBLE else View.GONE // with ktx loadingView.isVisible = isLoading

Slide 16

Slide 16 text

Core KTX: View inline var View.isVisible: Boolean get() = visibility == View.VISIBLE set(value) { visibility = if (value) View.VISIBLE else View.GONE } inline var View.isInvisible: Boolean inline var View.isGone: Boolean // LayoutParams // Margin // Padding // and more ...

Slide 17

Slide 17 text

Core KTX: Text implementation 'androidx.core:core-ktx:1.1.0' // 1.2.0-rc01 boldstring boldstring

Slide 18

Slide 18 text

Core KTX: Text val htmlString = "bold string" textView.text = Html.fromHtml(htmlString) // with compat textView.text = HtmlCompat.fromHtml(htmlString, FROM_HTML_MODE_LEGACY) // with ktx textView.text = htmlString.parseAsHtml()

Slide 19

Slide 19 text

Core KTX: Text inline fun String.parseAsHtml( flags: Int = FROM_HTML_MODE_LEGACY, imageGetter: ImageGetter? = null, tagHandler: TagHandler? = null ): Spanned = HtmlCompat.fromHtml(this, flags, imageGetter, tagHandler)

Slide 20

Slide 20 text

Core KTX: Text val sb = StringBuilder() .append(1) .append("string") // kotlin function val sb = buildString { append(1) append("string") } 1string

Slide 21

Slide 21 text

Core KTX: Text implementation 'androidx.core:core-ktx:1.1.0' // 1.2.0-rc01 val ssb = SpannableStringBuilder().apply { append("bold") setSpan(StyleSpan(Typeface.BOLD), 0, 4, SPAN_INCLUSIVE_EXCLUSIVE) append("string") } // with ktx val ssb = buildSpannedString { bold { append("bold") } append("string") } boldstring

Slide 22

Slide 22 text

Core KTX: Text inline fun buildSpannedString( builderAction: SpannableStringBuilder.() -> Unit): SpannedString { val builder = SpannableStringBuilder() builder.builderAction() return SpannedString(builder) } ... inline fun SpannableStringBuilder.bold( builderAction: SpannableStringBuilder.() -> Unit ) = inSpans(StyleSpan(BOLD), builderAction = builderAction)

Slide 23

Slide 23 text

Core KTX: Text buildSpannedString { click(onClick = { /* click! */ }) { color(Color.BLUE) { bold { append("clickable") } } } append("string") } clickablestring Custom

Slide 24

Slide 24 text

Core KTX: Text inline fun SpannableStringBuilder.click( crossinline onClick: (View) -> Unit, builderAction: SpannableStringBuilder.() -> Unit ) = inSpans( object : ClickableSpan() { override fun onClick(widget: View) = onClick(widget) }, builderAction = builderAction ) Custom

Slide 25

Slide 25 text

Core KTX: OS implementation 'androidx.core:core-ktx:1.1.0' // 1.2.0-rc01 View (Activity / Fragment) Bundle

Slide 26

Slide 26 text

Core KTX: OS fun newInstance(id: Int, title: String): DetailFragment { return DetailFragment().apply { arguments = Bundle().apply { putInt("EXTRA_ID", id) putCharSequence("EXTRA_TITLE", title) } } } // with ktx arguments = bundleOf( "EXTRA_ID" to id, "EXTRA_TITLE" to title )

Slide 27

Slide 27 text

Core KTX: OS fun bundleOf(vararg pairs: Pair) = Bundle(pairs.size).apply { for ((key, value) in pairs) { when (value) { is Int -> putInt(key, value) is CharSequence -> putCharSequence(key, value) is Parcelable -> putParcelable(key, value) ... } } }

Slide 28

Slide 28 text

Collection KTX implementation 'androidx.collection:collection-ktx:1.1.0' ArrayMap ArrayList HashSet HashMap ArraySet

Slide 29

Slide 29 text

Collection KTX // Java Collections val array = arrayOf(1, 2, 3) val list = listOf(1, 2, 3) val set = setOf("one", "two", "three") val map = mapOf(1 to "one", 2 to "two") // Android Collections with ktx val arraySet: ArraySet = arraySetOf("one", "two", "three") val arrayMap: ArrayMap = arrayMapOf(1 to "one", 2 to "two")

Slide 30

Slide 30 text

Collection KTX inline fun arrayMapOf(): ArrayMap = ArrayMap() fun arrayMapOf(vararg pairs: Pair): ArrayMap { val map = ArrayMap(pairs.size) for (pair in pairs) { map[pair.first] = pair.second } return map } ...

Slide 31

Slide 31 text

Activity, Fragment, Navigation KTX AAC ViewModel View (Activity / Fragment) implementation 'androidx.activity:activity-ktx:1.0.0' // 1.1.0-rc02 'androidx.fragment:fragment-ktx:1.1.0' // 1.2.0-rc02 'androidx.navigation:navigation-fragment-ktx:2.1.0' // 2.2.0-rc02 'androidx.navigation:navigation-ui-ktx:2.1.0' // 2.2.0-rc02

Slide 32

Slide 32 text

Activity, Fragment, Navigation KTX private lateinit var viewModel: HomeViewModel override fun onCreateView(...): View? { viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java) } // with lazy private val viewModel: HomeViewModel by lazy { ViewModelProviders.of(this).get(HomeViewModel::class.java) } // with ktx private val viewModel: HomeViewModel by viewModels()

Slide 33

Slide 33 text

Activity, Fragment, Navigation KTX // Activity private val viewModel: HomeViewModel by viewModels() // Fragment (scope: activity) private val viewModel: HomeViewModel by activityViewModels() // Fragment (scope: fragment) private val viewModel: HomeViewModel by viewModels() // Fragment (scope: navigation graph) private val viewModel: HomeViewModel by navGraphViewModels(R.id.home_graph)

Slide 34

Slide 34 text

Link: https://developer.android.com/kotlin/ktx/extensions-list Android KTX List

Slide 35

Slide 35 text

Firebase KTX Link: https:// rebase.google.com/support/release-notes/android // build.gradle implementation 'com.google.firebase:firebase-common-ktx:19.3.0' implementation 'com.google.firebase:firebase-firestore-ktx:21.3.0' implementation 'com.google.firebase:firebase-functions-ktx:19.0.1' implementation 'com.google.firebase:firebase-storage-ktx:19.1.0' implementation 'com.google.firebase:firebase-inappmessaging-ktx:19.0.2' implementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.2' implementation 'com.google.firebase:firebase-database-ktx:19.2.0'

Slide 36

Slide 36 text

Play Core KTX Link: https://developer.android.com/reference/com/google/android/play/core/release-notes // build.gradle implementation 'com.google.android.play:core-ktx:1.6.4' val AppUpdateInfo.isFlexibleUpdateAllowed: Boolean val AppUpdateInfo.isImmediateUpdateAllowed: Boolean suspend fun AppUpdateManager.requestAppUpdateInfo(): AppUpdateInfo fun AppUpdateManager.requestUpdateFlow(): Flow ...

Slide 37

Slide 37 text

DataBinding

Slide 38

Slide 38 text

DataBinding UI Link: https://developer.android.com/topic/libraries/data-binding • Goodbye findViewById() • BindingAdapter • Two-way data binding // app/build.gradle android { dataBinding { enabled = true } }

Slide 39

Slide 39 text

DataBinding: example val loadingView = findViewById(R.id.loading_view) loadingView.visibility = if (isLoading) View.VISIBLE else View.GONE

Slide 40

Slide 40 text

DataBinding: example

Slide 41

Slide 41 text

DataBinding: visibility ViewModel View UI Model

Slide 42

Slide 42 text

DataBinding: visibility // HomeViewModel.kt private val _isLoading = MutableLiveData() val isLoading: LiveData get() = _isLoading init { _isLoading.value = true // load data _isLoading.value = false } // HomeActivity.kt private val viewModel: HomeViewModel by viewModels() val binding: HomeActivityBinding = ... viewModel.isLoading.observe(this, Observer { binding.loadingView.isVisible = it })

Slide 43

Slide 43 text

DataBinding: visibility // HomeViewModel.kt private val _isLoading = MutableLiveData() val isLoading: LiveData get() = _isLoading init { _isLoading.value = true // load data _isLoading.value = false } // HomeActivity.kt private val viewModel: HomeViewModel by viewModels() val binding: HomeActivityBinding = ... binding.viewModel = viewModel

Slide 44

Slide 44 text

DataBinding: visibility @BindingAdapter("android:visibleIf") fun View.setVisibleIf(value: Boolean) { isVisible = value }

Slide 45

Slide 45 text

DataBinding: visibility // View.decompiled.java public final class ViewKt { @BindingAdapter({"android:visibleIf"}) public static final void setVisibleIf(@NotNull View view, boolean value) { view.setVisibility(value ? View.VISIBLE : View.GONE); } } // Generated by DataBinding library // HomeActivityBindingImpl.java @Override protected void executeBindings() { com.example.util.ViewKt.setVisibleIf(this.loadingView, isLoading); }

Slide 46

Slide 46 text

DataBinding: visibility // View.kt @BindingAdapter("android:visibleIf") fun View.setVisibleIf(value: Boolean) { isVisible = value } @BindingAdapter("android:invisibleIf") fun View.setInvisibleIf(value: Boolean) { isInvisible = value } @BindingAdapter("android:goneIf") fun View.setGoneIf(value: Boolean) { isGone = value } Custom

Slide 47

Slide 47 text

DataBinding: click ViewModel View UI Event

Slide 48

Slide 48 text

DataBinding: click // HomeViewModel.kt fun onFilterButtonClick() { ... } // HomeActivity.kt private val viewModel: HomeViewModel by viewModels() val binding: HomeActivityBinding = ... binding.filterButton.setOnClickListener { viewModel.onFilterButtonClick() }

Slide 49

Slide 49 text

DataBinding: click // HomeViewModel.kt fun onFilterButtonClick() { ... } // HomeActivity.kt private val viewModel: HomeViewModel by viewModels() val binding: HomeActivityBinding = ... binding.viewModel = viewModel ...

Slide 50

Slide 50 text

DataBinding: click class Item( val id: Long, val name: String )

Slide 51

Slide 51 text

DataBinding: checked ViewModel View UI Model toggle! isChecked

Slide 52

Slide 52 text

DataBinding: checked // TermsViewModel.kt private val _isChecked = MutableLiveData(false) val isChecked: LiveData get() = _isChecked fun onTermsChecked(value: Boolean) { _isChecked.value = value } // TermsActivity.kt private val viewModel: TermsViewModel by viewModels() val binding: TermsActivityBinding = ... viewModel.isChecked.observe(this, Observer { binding.termsCheckbox.checked = it }) binding.termsCheckbox.setOnCheckedChangeListener { _, isChecked -> viewModel.onTermsChecked(isChecked) }

Slide 53

Slide 53 text

DataBinding: checked // TermsViewModel.kt private val _isChecked = MutableLiveData(false) val isChecked: LiveData get() = _isChecked fun onTermsChecked(value: Boolean) { _isChecked.value = value } // TermsActivity.kt private val viewModel: TermsViewModel by viewModels() val binding: TermsActivityBinding = ... binding.viewModel = viewModel

Slide 54

Slide 54 text

DataBinding: checked

Slide 55

Slide 55 text

Link: https://developer.android.com/topic/libraries/data-binding/two-way#two-way-attributes Two-way data binding attributes

Slide 56

Slide 56 text

UseCases

Slide 57

Slide 57 text

UseCase: Load Image URL ImageView , URL . Glide Image Loading Library , Kotlin . ViewModel View URL ImageView

Slide 58

Slide 58 text

DataBinding: Load Image URL val url = "https://..." val imageView: ImageView = ... GlideApp.with(context).load(url).into(imageView) // kotlin extensions imageView.loadUrlAsync(url) val imageUrlFromApi = null imageView.loadUrlAsync(imageUrlFromApi) imageView.loadUrlAsync(imageUrlFromApi, placeholder = R.drawable.placeholder) fun ImageView.loadUrlAsync(url: Url?) { GlideApp.with(context).load(url).into(this) }

Slide 59

Slide 59 text

DataBinding: Load Image URL fun ImageView.loadUrlAsync(url: Url?, placeholder: Drawable? = null) { if (url == null) { GlideApp.with(context).load(placeholder).into(this) } else { GlideApp.with(context).load(url) .apply { if (placeholder != null) { placeholder(placeholder) } } .into(this) } } Custom @BindingAdapter("srcUrl", "placeholder", requireAll = false)

Slide 60

Slide 60 text

DataBinding: Load Image URL // HomeItemBindingImpl.java @Override protected void executeBindings() { ImageViewKt.loadAsync( this.imageView, itemImageUrl, getDrawableFromResource(imageView, R.drawable.placeholder)); } class Item( val id: Long, val imageUrl: String? )

Slide 61

Slide 61 text

UseCase: Debounce Click . ex) startActivity Activity B Activity A Click! Click! Activity B

Slide 62

Slide 62 text

DataBinding: Debounce Click val view: View = ... view.setOnClickListener { view -> startActivity(...) } // click! // click! click! view.setOnDebounceClickListener { view -> startActivity(...) } private typealias OnClickListener = (View) -> Unit fun View.setOnDebounceClickListener( listener: OnClickListener?) { if (listener == null) { setOnClickListener(null) } else { setOnClickListener(OnDebounceClickListener { it.run(listener) }) } }

Slide 63

Slide 63 text

DataBinding: Debounce Click class OnDebounceClickListener(private val listener: OnClickListener) : View.OnClickListener { override fun onClick(v: View?) { val now = System.currentTimeMillis() if (now < lastTime + INTERVAL) return lastTime = now v?.run(listener) } companion object { private const val INTERVAL: Long = 300 private var lastTime: Long = 0 } } Custom

Slide 64

Slide 64 text

DataBinding: Debounce Click

Slide 65

Slide 65 text

DataBinding: Debounce Click @BindingAdapter("onDebounceClick") fun setOnDebounceClickListener(view: View, listener: View.OnClickListener?) { if (listener == null) {...} else { view.setOnClickListener(OnDebounceClickListener { it.run(listener::onClick) }) } } Custom // extension function fun View.setOnDebounceClickListener(listener: OnClickListener?) { if (listener == null) {...} else { setOnClickListener(OnDebounceClickListener { it.run(listener) }) } }

Slide 66

Slide 66 text

Summary Android KTX , View Layer . DataBinding , Layout XML View . Android KTX, DataBinding .