Skip to content

Commit

Permalink
Add binding adapter to load images. Add data binding to recycler view
Browse files Browse the repository at this point in the history
adapter. Add 2-way data binding to superHero edition.
  • Loading branch information
Serchinastico committed Mar 15, 2019
1 parent 1a2ce62 commit 5ce5d5b
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 119 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.karumi.jetpack.superheroes.ui.view

import android.view.LayoutInflater
import androidx.databinding.DataBindingUtil
import androidx.test.platform.app.InstrumentationRegistry
import com.karumi.jetpack.superheroes.R
import com.karumi.jetpack.superheroes.databinding.SuperHeroRowBinding
import com.karumi.jetpack.superheroes.domain.model.SuperHero
import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesPresenter
import com.karumi.jetpack.superheroes.ui.view.adapter.SuperHeroViewHolder
Expand Down Expand Up @@ -51,15 +53,17 @@ class SuperHeroViewHolderTest : ScreenshotTest {
compareScreenshot(holder, R.dimen.super_hero_row_height)
}

private fun givenASuperHeroViewHolder(): SuperHeroViewHolder {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.super_hero_row, null, false)
return SuperHeroViewHolder(
view,
mock<SuperHeroesPresenter>(SuperHeroesPresenter::class.java)
)
}
private fun givenASuperHeroViewHolder(): SuperHeroViewHolder =
runOnUi {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val inflater = LayoutInflater.from(context)
val binding: SuperHeroRowBinding =
DataBindingUtil.inflate(inflater, R.layout.super_hero_row, null, false)
SuperHeroViewHolder(
binding,
mock<SuperHeroesPresenter>(SuperHeroesPresenter::class.java)
)
}

private fun givenASuperHeroWithALongDescription(): SuperHero {
val superHeroName = "Super Hero Name"
Expand Down Expand Up @@ -94,4 +98,10 @@ class SuperHeroViewHolderTest : ScreenshotTest {
superHeroDescription: String = "Super Hero Description",
isAvenger: Boolean = false
): SuperHero = SuperHero(superHeroId, superHeroName, null, isAvenger, superHeroDescription)
}

private fun <T> runOnUi(block: () -> T): T {
var response: T? = null
InstrumentationRegistry.getInstrumentation().runOnMainSync { response = block() }
return response!!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.karumi.jetpack.superheroes.common

import android.widget.ImageView
import androidx.databinding.BindingAdapter
import com.karumi.jetpack.superheroes.ui.utils.picasso

@BindingAdapter("imageUrl")
fun setImageUrl(view: ImageView, url: String?) {
if (url != null) {
view.context.picasso.load(url).fit().centerCrop().fit().into(view)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,21 @@ class EditSuperHeroPresenter(
}

override fun onSaveSuperHeroSelected(
name: String,
description: String,
isAvenger: Boolean
editableSuperHero: EditSuperHeroPresenter.EditableSuperHero
) {
saveSuperHero(name, description, isAvenger)
saveSuperHero(editableSuperHero)
}

private fun saveSuperHero(
name: String,
description: String,
isAvenger: Boolean
editableSuperHero: EditSuperHeroPresenter.EditableSuperHero
) = executor.submit {
view?.showLoading()
val superHero = superHero ?: return@submit
saveSuperHero(
superHero.copy(
name = name,
description = description,
isAvenger = isAvenger
name = editableSuperHero.name,
description = editableSuperHero.description,
isAvenger = editableSuperHero.isAvenger
)
)
view?.close()
Expand All @@ -72,6 +68,12 @@ class EditSuperHeroPresenter(
this@EditSuperHeroPresenter.superHero = superHero
}

data class EditableSuperHero(
var isAvenger: Boolean,
var name: String,
var description: String
)

interface View {
fun close()
fun hideLoading()
Expand All @@ -81,9 +83,5 @@ class EditSuperHeroPresenter(
}

interface EditSuperHeroListener {
fun onSaveSuperHeroSelected(
name: String,
description: String,
isAvenger: Boolean
)
fun onSaveSuperHeroSelected(editableSuperHero: EditSuperHeroPresenter.EditableSuperHero)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class SuperHeroesPresenter(
view: View,
private val getSuperHeroes: GetSuperHeroes,
private val executor: ExecutorService
) : LifecycleObserver {
) : SuperHeroesListener, LifecycleObserver {

private val view: View? by weak(view)

Expand All @@ -37,7 +37,7 @@ class SuperHeroesPresenter(
}
}

fun onSuperHeroClicked(superHero: SuperHero) {
override fun onSuperHeroClicked(superHero: SuperHero) {
view?.openDetail(superHero.id)
}

Expand All @@ -48,4 +48,8 @@ class SuperHeroesPresenter(
fun showSuperHeroes(superHeroes: List<SuperHero>)
fun openDetail(id: String)
}
}

interface SuperHeroesListener {
fun onSuperHeroClicked(superHero: SuperHero)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.karumi.jetpack.superheroes.ui.view

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.widget.Toolbar
import com.karumi.jetpack.superheroes.R
import com.karumi.jetpack.superheroes.common.module
Expand All @@ -11,7 +10,6 @@ import com.karumi.jetpack.superheroes.domain.model.SuperHero
import com.karumi.jetpack.superheroes.domain.usecase.GetSuperHeroById
import com.karumi.jetpack.superheroes.domain.usecase.SaveSuperHero
import com.karumi.jetpack.superheroes.ui.presenter.EditSuperHeroPresenter
import com.karumi.jetpack.superheroes.ui.utils.setImageBackground
import kotlinx.android.synthetic.main.edit_super_hero_activity.*
import org.kodein.di.erased.bind
import org.kodein.di.erased.instance
Expand All @@ -37,17 +35,6 @@ class EditSuperHeroActivity :
get() = toolbar
private val superHeroId: String by lazy { intent?.extras?.getString(SUPER_HERO_ID_KEY) ?: "" }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bt_save_edition.setOnClickListener {
presenter.onSaveSuperHeroSelected(
name = et_super_hero_name.text?.toString() ?: "",
description = et_super_hero_description.text?.toString() ?: "",
isAvenger = cb_is_avenger.isChecked
)
}
}

override fun configureBinding(binding: EditSuperHeroActivityBinding) {
binding.listener = presenter
binding.isLoading = false
Expand All @@ -71,8 +58,9 @@ class EditSuperHeroActivity :
}

override fun showSuperHero(superHero: SuperHero) = runOnUiThread {
binding.listener = presenter
binding.superHero = superHero
iv_super_hero_photo.setImageBackground(superHero.photo)
binding.editableSuperHero = superHero.toEditable()
}

override val activityModules = module {
Expand All @@ -82,4 +70,7 @@ class EditSuperHeroActivity :
bind<GetSuperHeroById>() with provider { GetSuperHeroById(instance()) }
bind<SaveSuperHero>() with provider { SaveSuperHero(instance()) }
}

private fun SuperHero.toEditable() =
EditSuperHeroPresenter.EditableSuperHero(isAvenger, name, description)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import com.karumi.jetpack.superheroes.databinding.SuperHeroDetailActivityBinding
import com.karumi.jetpack.superheroes.domain.model.SuperHero
import com.karumi.jetpack.superheroes.domain.usecase.GetSuperHeroById
import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroDetailPresenter
import com.karumi.jetpack.superheroes.ui.utils.setImageBackground
import kotlinx.android.synthetic.main.super_hero_detail_activity.*
import org.kodein.di.erased.bind
import org.kodein.di.erased.instance
Expand Down Expand Up @@ -57,7 +56,6 @@ class SuperHeroDetailActivity :
override fun showSuperHero(superHero: SuperHero) = runOnUiThread {
title = superHero.name
binding.superHero = superHero
iv_super_hero_photo.setImageBackground(superHero.photo)
}

override fun openEditSuperHero(superHeroId: String) = runOnUiThread {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,18 @@
package com.karumi.jetpack.superheroes.ui.view.adapter

import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.karumi.jetpack.superheroes.R
import com.karumi.jetpack.superheroes.databinding.SuperHeroRowBinding
import com.karumi.jetpack.superheroes.domain.model.SuperHero
import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesPresenter
import com.karumi.jetpack.superheroes.ui.utils.setImageBackground
import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesListener

class SuperHeroViewHolder(
itemView: View,
private val presenter: SuperHeroesPresenter
) : RecyclerView.ViewHolder(itemView) {

private val photoImageView: ImageView = itemView.findViewById(R.id.iv_super_hero_photo)
private val nameTextView: TextView = itemView.findViewById(R.id.tv_super_hero_name)
private val avengersBadgeView: View = itemView.findViewById(R.id.iv_avengers_badge)
private val binding: SuperHeroRowBinding,
private val listener: SuperHeroesListener
) : RecyclerView.ViewHolder(binding.root) {

fun render(superHero: SuperHero) {
hookListeners(superHero)
renderSuperHeroPhoto(superHero.photo)
renderSuperHeroName(superHero.name)
renderAvengersBadge(superHero.isAvenger)
}

private fun hookListeners(superHero: SuperHero) {
itemView.setOnClickListener { presenter.onSuperHeroClicked(superHero) }
}

private fun renderSuperHeroPhoto(photo: String?) {
photoImageView.setImageBackground(photo)
}

private fun renderSuperHeroName(name: String) {
nameTextView.text = name
}

private fun renderAvengersBadge(isAvenger: Boolean) {
avengersBadgeView.visibility = if (isAvenger) View.VISIBLE else View.GONE
binding.superHero = superHero
binding.listener = listener
binding.executePendingBindings()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package com.karumi.jetpack.superheroes.ui.view.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.karumi.jetpack.superheroes.R
import com.karumi.jetpack.superheroes.databinding.SuperHeroRowBinding
import com.karumi.jetpack.superheroes.domain.model.SuperHero
import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesPresenter
import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesListener

internal class SuperHeroesAdapter(
private val presenter: SuperHeroesPresenter
private val listener: SuperHeroesListener
) : RecyclerView.Adapter<SuperHeroViewHolder>() {
private val superHeroes: MutableList<SuperHero> = ArrayList()

Expand All @@ -17,11 +19,14 @@ internal class SuperHeroesAdapter(
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SuperHeroViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.super_hero_row, parent,
val binding: SuperHeroRowBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.super_hero_row,
parent,
false
)
return SuperHeroViewHolder(view, presenter)

return SuperHeroViewHolder(binding, listener)
}

override fun onBindViewHolder(holder: SuperHeroViewHolder, position: Int) {
Expand Down
12 changes: 9 additions & 3 deletions app/src/main/res/layout/edit_super_hero_activity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
name="superHero"
type="com.karumi.jetpack.superheroes.domain.model.SuperHero" />

<variable
name="editableSuperHero"
type="com.karumi.jetpack.superheroes.ui.presenter.EditSuperHeroPresenter.EditableSuperHero" />

<variable
name="isLoading"
type="boolean" />
Expand Down Expand Up @@ -46,6 +50,7 @@
android:id="@+id/iv_super_hero_photo"
android:layout_width="match_parent"
android:layout_height="@dimen/super_hero_detail_header_height"
app:imageUrl="@{ superHero.photo }"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/color_primary_dark"
tools:ignore="ContentDescription" />
Expand All @@ -68,7 +73,7 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:buttonTint="@color/white"
android:checked="@{ superHero.avenger }"
android:checked="@={ editableSuperHero.avenger, default = false }"
android:enabled="@{ superHero != null }"
android:text="@string/is_super_hero_an_avenger_checkbox"
android:textColor="@color/white"
Expand All @@ -92,7 +97,7 @@
android:hint="@string/edit_super_hero_name_hint"
android:importantForAutofill="no"
android:inputType="text"
android:text="@{ superHero.name }"
android:text="@={ editableSuperHero.name }"
android:textColor="@color/white"
tools:ignore="UnusedAttribute" />

Expand All @@ -116,7 +121,7 @@
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:scrollbars="vertical"
android:text="@{ superHero.description }"
android:text="@={ editableSuperHero.description }"
android:textColor="@color/white"
tools:ignore="UnusedAttribute" />

Expand Down Expand Up @@ -150,6 +155,7 @@
android:layout_height="56dp"
android:background="@color/royal_blue"
android:enabled="@{ superHero != null }"
android:onClick="@{ () -> listener.onSaveSuperHeroSelected(editableSuperHero) }"
android:text="@string/save_edited_super_hero_button"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/layout/super_hero_detail_activity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
android:id="@+id/iv_super_hero_photo"
android:layout_width="match_parent"
android:layout_height="@dimen/super_hero_detail_header_height"
app:imageUrl="@{ superHero.photo }"
app:layout_constraintTop_toBottomOf="@id/toolbar"
tools:background="@color/color_primary_dark"
tools:ignore="ContentDescription" />
Expand Down
Loading

0 comments on commit 5ce5d5b

Please sign in to comment.