Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Viewbinding #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ android {
}
}

dataBinding {
viewBinding {
isEnabled = true
}
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.spitchenko.pokeapp.component.extensions

import android.content.Context
import android.view.LayoutInflater
import android.widget.Toast
import com.spitchenko.pokeapp.component.messaging.Message

val Context.layoutInflater: LayoutInflater get() = LayoutInflater.from(this)

fun Context.showMessage(message: Message, duration: Duration = Duration.LONG) {
val toastDuration = when (duration) {
Duration.LONG -> Toast.LENGTH_LONG
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
package com.spitchenko.pokeapp.component.databinding.adapters
package com.spitchenko.pokeapp.component.extensions

import android.widget.ImageView
import androidx.databinding.BindingAdapter
import com.bumptech.glide.request.RequestOptions
import com.spitchenko.pokeapp.component.glide.GlideApp

@BindingAdapter(
value = [
"imageUrl",
"imagePlaceholder"
],
requireAll = false
)
fun ImageView.setImageGlide(
imageUrl: String?,
imagePlaceholder: Int?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package com.spitchenko.pokeapp.component.extensions

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer

fun <T> LiveData<List<T>>.size(): Int = value?.size ?: 0
fun <T> LiveData<List<T>>.size(): Int = value?.size ?: 0

inline fun <T> LiveData<T>.observe(
viewLifecycleOwner: LifecycleOwner,
crossinline action: (T) -> Unit
) = observe(viewLifecycleOwner, Observer {
action(it)
})
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package com.spitchenko.pokeapp.component.databinding.adapters
package com.spitchenko.pokeapp.component.extensions

import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BinderAdapter
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BindingClass
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BindingDiffUtilCallback

@BindingAdapter("onScrolledToFooter")
fun RecyclerView.setOnScrolledToFooter(action: () -> Unit) {
inline fun RecyclerView.setOnScrolledToFooter(crossinline action: () -> Unit) {
addOnScrollListener(
object : RecyclerView.OnScrollListener() {

Expand All @@ -22,22 +20,26 @@ fun RecyclerView.setOnScrolledToFooter(action: () -> Unit) {
val totalItemCount = itemCount

if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount) {
action()
post {
action()
}
}
}
}
})
}

@BindingAdapter(value = ["bindingList", "detectMoves"], requireAll = false)
fun RecyclerView.setBindingList(bindingList: List<BindingClass>?, detectMoves: Boolean?) {
fun RecyclerView.setBindingList(
bindingList: List<BindingClass>?,
detectMoves: Boolean = false
) {
val bindingAdapter = adapter as? BinderAdapter ?: return

val newOrEmptyList = bindingList.orEmpty()

val bindingsDiffResult = DiffUtil.calculateDiff(
BindingDiffUtilCallback(bindingAdapter.itemList, newOrEmptyList),
detectMoves ?: false
detectMoves
)

bindingAdapter.setItems(bindingsDiffResult, newOrEmptyList)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.spitchenko.pokeapp.component.extensions

import android.graphics.drawable.Drawable
import android.widget.TextView
import com.spitchenko.pokeapp.component.messaging.Message

fun TextView.setMessage(message: Message?) {
text = message?.getString(context)
}

fun TextView.setTextIfDifferent(message: Message?) {
val newText = message?.getString(context)

if (this.text != newText) {
this.text = newText
}
}

fun TextView.setTextIfDifferent(text: CharSequence) {
if (this.text != text) {
this.text = text
}
}

fun TextView.setDrawables(
drawableStart: Drawable? = null,
drawableEnd: Drawable? = null,
drawableTop: Drawable? = null,
drawableBottom: Drawable? = null
) {
if (isDrawableDifferent(drawableStart, CompoundDrawables.START)
|| isDrawableDifferent(drawableEnd, CompoundDrawables.END)
|| isDrawableDifferent(drawableTop, CompoundDrawables.TOP)
|| isDrawableDifferent(drawableBottom, CompoundDrawables.BOTTOM)) {
setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, drawableTop, drawableEnd, drawableBottom)
}
}

private fun TextView.isDrawableDifferent(drawable: Drawable?, compoundDrawable: CompoundDrawables) : Boolean =
compoundDrawablesRelative[compoundDrawable.ordinal] != drawable

enum class CompoundDrawables {
START,
TOP,
END,
BOTTOM
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.spitchenko.pokeapp.component.extensions

import android.view.View

fun View.setVisibleOrGone(visibleOrGone: Boolean) {
if (visibleOrGone) {
if (visibility != View.VISIBLE) {
visibility = View.VISIBLE
}
} else {
if (visibility != View.GONE) {
visibility = View.GONE
}
}
}

fun View.setClickableAndFocusable(clickable: Boolean) {

if (isClickable != clickable) {
isClickable = clickable
}
if (isFocusable != clickable) {
isFocusable = clickable
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.spitchenko.pokeapp.feature.details.presentation

import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import com.spitchenko.pokeapp.component.extensions.layoutInflater
import com.spitchenko.pokeapp.databinding.ItemPokemonDetailBinding
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BinderAdapter
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BindingViewHolder
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.ViewHolderFactory

object ItemPokemonDetailViewHolderFactory : ViewHolderFactory {

override fun create(
parent: ViewGroup,
adapter: BinderAdapter
): BindingViewHolder<ViewBinding> {
val binding = ItemPokemonDetailBinding.inflate(parent.context.layoutInflater, parent, false)

return BindingViewHolder(binding)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.spitchenko.pokeapp.feature.details.presentation

import androidx.viewbinding.ViewBinding
import com.spitchenko.pokeapp.component.extensions.setTextIfDifferent
import com.spitchenko.pokeapp.databinding.ItemPokemonDetailBinding
import com.spitchenko.pokeapp.feature.details.presentation.model.PokemonDetailUiModel
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BindingClass
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BindingViewHolder
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.ViewRenderer

object ItemPokemonDetailViewRenderer : ViewRenderer {

override fun render(
item: BindingClass,
viewHolder: BindingViewHolder<ViewBinding>,
position: Int
) {
if (item !is PokemonDetailUiModel || viewHolder.binding !is ItemPokemonDetailBinding) {
return
}

viewHolder.binding.pokemonDetails.setTextIfDifferent(item.text)
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
package com.spitchenko.pokeapp.feature.details.presentation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.transition.TransitionInflater
import com.spitchenko.pokeapp.R
import com.spitchenko.pokeapp.component.extensions.getViewModel
import com.spitchenko.pokeapp.component.extensions.initToolbar
import com.spitchenko.pokeapp.component.lifecycle.ViewModelFactory
import com.spitchenko.pokeapp.databinding.PokemonDetailsFragmentBinding
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BinderAdapter
import javax.inject.Inject

class PokemonDetailsFragment @Inject constructor(
private val pokemonDetailsUiConverter: PokemonDetailsUiConverter
) : Fragment() {
private val pokemonDetailsUiConverter: PokemonDetailsUiConverter,
private val adapter: BinderAdapter
) : Fragment(R.layout.pokemon_details_fragment) {

lateinit var viewModel: PokemonDetailsViewModel
private lateinit var viewModel: PokemonDetailsViewModel

lateinit var args: PokemonDetailsFragmentArgs

Expand All @@ -39,16 +38,10 @@ class PokemonDetailsFragment @Inject constructor(
})
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = PokemonDetailsFragmentBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val binding = PokemonDetailsFragmentBinding.bind(view)

binding.viewModel = viewModel

binding.pokemonDetailsList.adapter = BinderAdapter()
binding.pokemonDetailsList.adapter = adapter

binding.pokemonDetailsList.addItemDecoration(
DividerItemDecoration(
Expand All @@ -61,6 +54,15 @@ class PokemonDetailsFragment @Inject constructor(

binding.toolbar.initToolbar(findNavController())

return binding.root
binding.pokemonDetailsList.setBindingList(viewModel.uiModel.details)

binding.headerImage.setImageGlide(
args.pokemonDetails.details.image?.url,
R.drawable.ic_pokeball_pokemon
)

binding.headerImage.transitionName = args.transitionName

binding.toolbar.title = args.pokemonDetails.details.name
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
package com.spitchenko.pokeapp.feature.details.presentation.di

import com.spitchenko.pokeapp.feature.details.presentation.ItemPokemonDetailViewHolderFactory
import com.spitchenko.pokeapp.feature.details.presentation.ItemPokemonDetailViewRenderer
import com.spitchenko.pokeapp.feature.details.presentation.PokemonDetailsUiConverter
import com.spitchenko.pokeapp.feature.details.presentation.PokemonDetailsUiConverterImpl
import com.spitchenko.pokeapp.feature.details.presentation.model.PokemonDetailUiModel
import com.spitchenko.pokeapp.feature.list.presentation.binderadapter.BinderAdapter
import dagger.Binds
import dagger.Module
import dagger.Provides

@Module
interface PokemonDetailsModule {
abstract class PokemonDetailsModule {

@Module
companion object {

@JvmStatic
@Provides
fun provideAdapter(): BinderAdapter =
BinderAdapter(
factory = mapOf(
PokemonDetailUiModel.viewType to ItemPokemonDetailViewHolderFactory
),
renders = mapOf(
PokemonDetailUiModel.viewType to ItemPokemonDetailViewRenderer
)
)
}

@Binds
fun bindPokemonDetailsUiConverter(impl: PokemonDetailsUiConverterImpl): PokemonDetailsUiConverter
abstract fun bindPokemonDetailsUiConverter(impl: PokemonDetailsUiConverterImpl): PokemonDetailsUiConverter
}
Loading