Skip to content

Commit

Permalink
Add LayoutContainer for SuperHeroViewHolder
Browse files Browse the repository at this point in the history
Add binding adapter to load images

Add data binding to recycler view adapter

Fix tests
  • Loading branch information
Serchinastico committed Jan 30, 2019
1 parent 9cabf42 commit b4a01d7
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 85 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 All @@ -14,53 +16,64 @@ class SuperHeroViewHolderTest : ScreenshotTest {
@Test
fun showsAnySuperHero() {
val superHero = givenASuperHero()
val holder = givenASuperHeroViewHolder()
val (holder, binding) = givenASuperHeroViewHolder()

holder.render(superHero)
renderViewHolder(superHero, holder, binding)

compareScreenshot(holder, R.dimen.super_hero_row_height)
}

@Test
fun showsSuperHeroesWithLongNames() {
val superHero = givenASuperHeroWithALongName()
val holder = givenASuperHeroViewHolder()
val (holder, binding) = givenASuperHeroViewHolder()

holder.render(superHero)
renderViewHolder(superHero, holder, binding)

compareScreenshot(holder, R.dimen.super_hero_row_height)
}

@Test
fun showsSuperHeroesWithLongDescriptions() {
val superHero = givenASuperHeroWithALongDescription()
val holder = givenASuperHeroViewHolder()
val (holder, binding) = givenASuperHeroViewHolder()

holder.render(superHero)
renderViewHolder(superHero, holder, binding)

compareScreenshot(holder, R.dimen.super_hero_row_height)
}

@Test
fun showsAvengersBadge() {
val superHero = givenASuperHero(isAvenger = true)
val holder = givenASuperHeroViewHolder()
val (holder, binding) = givenASuperHeroViewHolder()

holder.render(superHero)
renderViewHolder(superHero, holder, binding)

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 renderViewHolder(
superHero: SuperHero,
holder: SuperHeroViewHolder,
binding: SuperHeroRowBinding
) {
holder.render(superHero)
binding.executePendingBindings()
}

private fun givenASuperHeroViewHolder(): Pair<SuperHeroViewHolder, SuperHeroRowBinding> =
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)
) to binding
}

private fun givenASuperHeroWithALongDescription(): SuperHero {
val superHeroName = "Super Hero Name"
val superHeroDescription = """
Expand Down Expand Up @@ -94,4 +107,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 @@ -10,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.Kodein
import org.kodein.di.erased.bind
Expand Down Expand Up @@ -73,7 +72,6 @@ class EditSuperHeroActivity :

override fun showSuperHero(superHero: SuperHero) {
binding.superHero = superHero
iv_super_hero_photo.setImageBackground(superHero.photo)
}

override val activityModules =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,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.Kodein
import org.kodein.di.erased.bind
Expand Down Expand Up @@ -58,7 +57,6 @@ class SuperHeroDetailActivity :
override fun showSuperHero(superHero: SuperHero) {
title = superHero.name
binding.superHero = superHero
iv_super_hero_photo.setImageBackground(superHero.photo)
}

override fun openEditSuperHero(superHeroId: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,21 @@
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

class SuperHeroViewHolder(
itemView: View,
private val binding: SuperHeroRowBinding,
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)
) : RecyclerView.ViewHolder(binding.root) {

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

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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ 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

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, presenter)
}

override fun onBindViewHolder(holder: SuperHeroViewHolder, position: Int) {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/layout/edit_super_hero_activity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,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 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
87 changes: 51 additions & 36 deletions app/src/main/res/layout/super_hero_row.xml
Original file line number Diff line number Diff line change
@@ -1,41 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/super_hero_row_height">
xmlns:tools="http://schemas.android.com/tools">

<ImageView
android:id="@+id/iv_super_hero_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<data>

<import type="android.view.View" />

<View
<variable
name="superHero"
type="com.karumi.jetpack.superheroes.domain.model.SuperHero" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/super_hero_row_gradient_height"
android:background="@drawable/super_hero_gradient"
app:layout_constraintBottom_toBottomOf="parent" />

<TextView
android:id="@+id/tv_super_hero_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Iron Man" />

<ImageView
android:id="@+id/iv_avengers_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:src="@mipmap/ic_avengers"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:contentDescription="@string/is_avenger_badge_content_description" />

</androidx.constraintlayout.widget.ConstraintLayout>
android:layout_height="@dimen/super_hero_row_height">

<ImageView
android:id="@+id/iv_super_hero_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:imageUrl="@{ superHero.photo }"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />

<View
android:layout_width="match_parent"
android:layout_height="@dimen/super_hero_row_gradient_height"
android:background="@drawable/super_hero_gradient"
app:layout_constraintBottom_toBottomOf="parent" />

<TextView
android:id="@+id/tv_super_hero_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:text="@{ superHero.name }"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Iron Man" />

<ImageView
android:id="@+id/iv_avengers_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:contentDescription="@string/is_avenger_badge_content_description"
android:src="@mipmap/ic_avengers"
android:visibility="@{ superHero.avenger ? View.VISIBLE : View.GONE, default=gone }"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

0 comments on commit b4a01d7

Please sign in to comment.