Skip to content

Commit

Permalink
Replace kotlin synthetic with view binding
Browse files Browse the repository at this point in the history
  • Loading branch information
Drjacky committed Nov 9, 2020
1 parent 189db8f commit cfc9075
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 67 deletions.
7 changes: 7 additions & 0 deletions presentation/build.gradle
Expand Up @@ -27,6 +27,9 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildFeatures {
viewBinding true
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
Expand Down Expand Up @@ -63,6 +66,10 @@ dependencies {
implementation "androidx.paging:paging-runtime-ktx:$pagingVersion"
implementation "androidx.paging:paging-rxjava2:$pagingVersion"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
implementation "androidx.multidex:multidex:$multidexVersion"
implementation "androidx.core:core-ktx:$coreKtxVersion"
implementation "androidx.fragment:fragment-ktx:$fragmentExtVersion"
Expand Down
Expand Up @@ -4,14 +4,15 @@ import android.view.View
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import app.web.drjackycv.domain.base.RecyclerItem

abstract class BasePagedListAdapter(
vararg types: Cell<RecyclerItem>,
vararg types: Cell<RecyclerItem, ViewBinding>,
private val onItemClick: (RecyclerItem, View) -> Unit
) : PagingDataAdapter<RecyclerItem, RecyclerView.ViewHolder>(BASE_DIFF_CALLBACK) {

private val cellTypes: CellTypes<RecyclerItem> = CellTypes(*types)
private val cellTypes: CellTypes<RecyclerItem, ViewBinding> = CellTypes(*types)

override fun getItemViewType(position: Int): Int {
getItem(position).let {
Expand Down
@@ -1,27 +1,20 @@
package app.web.drjackycv.presentation.base.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import app.web.drjackycv.domain.base.RecyclerItem

abstract class Cell<T> {
abstract class Cell<T, R> {

abstract fun belongsTo(item: T?): Boolean
abstract fun type(): Int
abstract fun binding(parent: ViewGroup): R
abstract fun holder(parent: ViewGroup): RecyclerView.ViewHolder
abstract fun bind(
holder: RecyclerView.ViewHolder,
item: T?,
onItemClick: ((RecyclerItem, View) -> Unit)?
)

protected fun ViewGroup.viewOf(@LayoutRes resource: Int): View {
return LayoutInflater
.from(context)
.inflate(resource, this, false)
}

}
@@ -1,25 +1,25 @@
package app.web.drjackycv.presentation.base.adapter

class CellTypes<T>(vararg types: Cell<T>) {
class CellTypes<T, R>(vararg types: Cell<T, R>) {

private val cellTypes: ArrayList<Cell<T>> = ArrayList()
private val cellTypes: ArrayList<Cell<T, R>> = ArrayList()

init {
types.forEach { addType(it) }
}

private fun addType(type: Cell<T>) {
private fun addType(type: Cell<T, R>) {
cellTypes.add(type)
}

fun of(item: T?): Cell<T> {
fun of(item: T?): Cell<T, R> {
for (cellType in cellTypes) {
if (cellType.belongsTo(item)) return cellType
}
throw NoSuchRecyclerItemTypeException()
}

fun of(viewType: Int): Cell<T> {
fun of(viewType: Int): Cell<T, R> {
for (cellType in cellTypes) {
if (cellType.type() == viewType) return cellType
}
Expand Down
@@ -1,10 +1,10 @@
package app.web.drjackycv.presentation.base.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.LoadState
import androidx.paging.LoadStateAdapter
import app.web.drjackycv.presentation.R
import app.web.drjackycv.presentation.extension.inflate
import app.web.drjackycv.presentation.databinding.ItemLoadingRowBinding

class LoadingStateAdapter : LoadStateAdapter<LoadingStateViewHolder>() {

Expand All @@ -15,7 +15,10 @@ class LoadingStateAdapter : LoadStateAdapter<LoadingStateViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
): LoadingStateViewHolder =
LoadingStateViewHolder(parent.inflate(R.layout.item_loading_row))
): LoadingStateViewHolder {
val itemBinding =
ItemLoadingRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return LoadingStateViewHolder(itemBinding)
}

}
@@ -1,15 +1,15 @@
package app.web.drjackycv.presentation.base.adapter

import android.view.View
import androidx.core.view.isVisible
import androidx.paging.LoadState
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_loading_row.view.*
import app.web.drjackycv.presentation.databinding.ItemLoadingRowBinding

class LoadingStateViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
class LoadingStateViewHolder(private val itemBinding: ItemLoadingRowBinding) :
RecyclerView.ViewHolder(itemBinding.root) {

fun bind(loadState: LoadState) {
view.itemLoadingRowContainer.isVisible = loadState is LoadState.Loading
itemBinding.itemLoadingRowContainer.isVisible = loadState is LoadState.Loading
}

}
@@ -1,9 +1,71 @@
package app.web.drjackycv.presentation.extension

import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
liveData.observe(this, Observer { it?.let { t -> action(t) } })
}
}

/*fun <T> Fragment.viewBinding(initialise: () -> T): ReadOnlyProperty<Fragment, T> =
object : ReadOnlyProperty<Fragment, T>, DefaultLifecycleObserver {*/
class FragmentViewBindingDelegate<T : ViewBinding>(
val fragment: Fragment,
val viewBindingFactory: (View) -> T
) : ReadOnlyProperty<Fragment, T>, DefaultLifecycleObserver {

// A backing property to hold our value
private var binding: T? = null

private var viewLifecycleOwner: LifecycleOwner? = null

init {
// Observe the View Lifecycle of the Fragment
this.fragment
.viewLifecycleOwnerLiveData
.observe(fragment, Observer { newLifecycleOwner ->
viewLifecycleOwner
?.lifecycle
?.removeObserver(this)

viewLifecycleOwner = newLifecycleOwner.also {
it.lifecycle.addObserver(this)
}
})
}

override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
binding = null

}

override fun getValue(
thisRef: Fragment,
property: KProperty<*>
): T {
// Return the backing property if it's set, or initialise
return this.binding ?: viewBindingFactory(fragment.requireView()).also {
this.binding = it
}
}
}

fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
FragmentViewBindingDelegate(this, viewBindingFactory)

inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
crossinline bindingInflater: (LayoutInflater) -> T
) =
lazy(LazyThreadSafetyMode.NONE) {
bindingInflater.invoke(layoutInflater)
}
@@ -1,16 +1,18 @@
package app.web.drjackycv.presentation.products.entity

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import app.web.drjackycv.domain.base.RecyclerItem
import app.web.drjackycv.domain.products.entity.Beer
import app.web.drjackycv.presentation.R
import app.web.drjackycv.presentation.base.adapter.Cell
import app.web.drjackycv.presentation.databinding.ItemProductBinding
import app.web.drjackycv.presentation.products.productlist.BeerMapper
import kotlinx.android.synthetic.main.item_product.view.*

object BeerCell : Cell<RecyclerItem>() {
object BeerCell : Cell<RecyclerItem, ViewBinding>() {

override fun belongsTo(item: RecyclerItem?): Boolean {
return item is Beer
Expand All @@ -20,8 +22,12 @@ object BeerCell : Cell<RecyclerItem>() {
return R.layout.item_product
}

override fun binding(parent: ViewGroup): ItemProductBinding {
return ItemProductBinding.inflate(LayoutInflater.from(parent.context), parent, false)
}

override fun holder(parent: ViewGroup): RecyclerView.ViewHolder {
return BeerViewHolder(parent.viewOf(type()))
return BeerViewHolder(binding(parent))
}

override fun bind(
Expand All @@ -35,7 +41,7 @@ object BeerCell : Cell<RecyclerItem>() {
holder.bind(itemUI)
holder.itemView.setOnClickListener {
onItemClick?.run {
this(item, holder.itemView.itemProductContainer)
this(item, holder.itemBinding.itemProductContainer)
}
}
}
Expand Down
@@ -1,18 +1,18 @@
package app.web.drjackycv.presentation.products.entity

import android.view.View
import androidx.recyclerview.widget.RecyclerView
import app.web.drjackycv.presentation.databinding.ItemProductBinding
import app.web.drjackycv.presentation.extension.load
import kotlinx.android.synthetic.main.item_product.view.*

class BeerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
class BeerViewHolder(val itemBinding: ItemProductBinding) :
RecyclerView.ViewHolder(itemBinding.root) {
fun bind(beer: BeerUI) = with(itemView) {
itemProductContainer.transitionName = beer.id.toString()
itemProductIdTxv.text = beer.id.toString()
itemProductImv.load(beer.imageUrl)
itemProductNameTxv.text = beer.name
itemProductAbvTxv.text = beer.abv.toString()
//itemProductTypeTxv.text = beer.type
itemBinding.itemProductContainer.transitionName = beer.id.toString()
itemBinding.itemProductIdTxv.text = beer.id.toString()
itemBinding.itemProductImv.load(beer.imageUrl)
itemBinding.itemProductNameTxv.text = beer.name
itemBinding.itemProductAbvTxv.text = beer.abv.toString()
//itemBinding.itemProductTypeTxv.text = beer.type
}

}
Expand Up @@ -6,15 +6,18 @@ import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import app.web.drjackycv.presentation.R
import app.web.drjackycv.presentation.databinding.FragmentProductDetailBinding
import app.web.drjackycv.presentation.extension.load
import app.web.drjackycv.presentation.extension.viewBinding
import com.google.android.material.transition.platform.MaterialArcMotion
import com.google.android.material.transition.platform.MaterialContainerTransform
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_product_detail.*

@AndroidEntryPoint
class ProductDetailFragment : Fragment(R.layout.fragment_product_detail) {

private val binding by viewBinding(FragmentProductDetailBinding::bind)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupUI()
Expand All @@ -27,12 +30,12 @@ class ProductDetailFragment : Fragment(R.layout.fragment_product_detail) {
setSharedElementTransitionOnEnter()

with(product) {
productDetailIdTxv.text = id.toString()
productDetailContainer.transitionName = id.toString()
productDetailImv.load(url = imageUrl, activity = activity)
productDetailNameTxv.text = name
productDetailDescriptionTxv.text = getString(R.string.description, description)
productDetailAbvTxv.text = getString(R.string.abv, abv.toString())
binding.productDetailIdTxv.text = id.toString()
binding.productDetailContainer.transitionName = id.toString()
binding.productDetailImv.load(url = imageUrl, activity = activity)
binding.productDetailNameTxv.text = name
binding.productDetailDescriptionTxv.text = getString(R.string.description, description)
binding.productDetailAbvTxv.text = getString(R.string.abv, abv.toString())
}
}

Expand Down

0 comments on commit cfc9075

Please sign in to comment.