Skip to content

Commit 2fc51ef

Browse files
committed
Add transition animation
1 parent 78ffbad commit 2fc51ef

File tree

10 files changed

+80
-29
lines changed

10 files changed

+80
-29
lines changed

presentation/src/main/kotlin/app/web/drjackycv/presentation/base/adapter/BasePagedListAdapter.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package app.web.drjackycv.presentation.base.adapter
22

33
import android.view.ViewGroup
4+
import android.widget.ImageView
45
import androidx.paging.PagedListAdapter
56
import androidx.recyclerview.widget.RecyclerView
67
import app.web.drjackycv.domain.base.RecyclerItem
78

89
abstract class BasePagedListAdapter(
910
vararg types: Cell<RecyclerItem>,
10-
private val onItemClick: (RecyclerItem) -> Unit
11+
private val onItemClick: (RecyclerItem, ImageView) -> Unit
1112
) : PagedListAdapter<RecyclerItem, RecyclerView.ViewHolder>(BASE_DIFF_CALLBACK) {
1213

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

presentation/src/main/kotlin/app/web/drjackycv/presentation/base/adapter/Cell.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package app.web.drjackycv.presentation.base.adapter
33
import android.view.LayoutInflater
44
import android.view.View
55
import android.view.ViewGroup
6+
import android.widget.ImageView
67
import androidx.annotation.LayoutRes
78
import androidx.recyclerview.widget.RecyclerView
89
import app.web.drjackycv.domain.base.RecyclerItem
@@ -15,7 +16,7 @@ abstract class Cell<T> {
1516
abstract fun bind(
1617
holder: RecyclerView.ViewHolder,
1718
item: T?,
18-
onItemClick: ((RecyclerItem) -> Unit)?
19+
onItemClick: ((RecyclerItem, ImageView) -> Unit)?
1920
)
2021

2122
protected fun ViewGroup.viewOf(@LayoutRes resource: Int): View {

presentation/src/main/kotlin/app/web/drjackycv/presentation/extension/ViewExtensions.kt

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package app.web.drjackycv.presentation.extension
22

3+
import android.app.Activity
4+
import android.graphics.drawable.Drawable
35
import android.view.LayoutInflater
46
import android.view.View
57
import android.view.ViewGroup
68
import android.widget.ImageView
79
import androidx.annotation.DrawableRes
810
import androidx.annotation.LayoutRes
911
import androidx.appcompat.content.res.AppCompatResources
12+
import androidx.core.app.ActivityCompat.startPostponedEnterTransition
1013
import app.web.drjackycv.presentation.R
1114
import app.web.drjackycv.presentation.base.util.GlideApp
15+
import com.bumptech.glide.load.DataSource
16+
import com.bumptech.glide.load.engine.GlideException
17+
import com.bumptech.glide.request.RequestListener
1218
import com.bumptech.glide.request.RequestOptions
19+
import com.bumptech.glide.request.target.Target
1320

1421
fun View.gone() {
1522
visibility = View.GONE
@@ -28,7 +35,8 @@ fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false):
2835

2936
fun ImageView.load(
3037
url: String,
31-
@DrawableRes placeholderRes: Int = R.drawable.ic_cloud_download
38+
@DrawableRes placeholderRes: Int = R.drawable.ic_cloud_download,
39+
activity: Activity? = null
3240
) {
3341
val safePlaceholderDrawable = AppCompatResources.getDrawable(context, placeholderRes)
3442
val requestOptions = RequestOptions().apply {
@@ -39,6 +47,33 @@ fun ImageView.load(
3947
.with(context)
4048
.setDefaultRequestOptions(requestOptions)
4149
.load(url)
50+
.dontAnimate()
51+
52+
activity?.let {
53+
glideRequest
54+
.listener(object : RequestListener<Drawable> {
55+
override fun onLoadFailed(
56+
e: GlideException?,
57+
model: Any?,
58+
target: Target<Drawable>?,
59+
isFirstResource: Boolean
60+
): Boolean {
61+
startPostponedEnterTransition(it)
62+
return false
63+
}
64+
65+
override fun onResourceReady(
66+
resource: Drawable,
67+
model: Any,
68+
target: Target<Drawable>,
69+
dataSource: DataSource,
70+
isFirstResource: Boolean
71+
): Boolean {
72+
startPostponedEnterTransition(it)
73+
return false
74+
}
75+
})
76+
}
4277

4378
glideRequest.into(this)
4479
}

presentation/src/main/kotlin/app/web/drjackycv/presentation/products/entity/BeerCell.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package app.web.drjackycv.presentation.products.entity
22

33
import android.view.ViewGroup
4+
import android.widget.ImageView
45
import androidx.recyclerview.widget.RecyclerView
56
import app.web.drjackycv.domain.base.RecyclerItem
67
import app.web.drjackycv.domain.products.entity.Beer
78
import app.web.drjackycv.presentation.R
89
import app.web.drjackycv.presentation.base.adapter.Cell
910
import app.web.drjackycv.presentation.products.productlist.BeerMapper
11+
import kotlinx.android.synthetic.main.item_product.view.*
1012

1113
object BeerCell : Cell<RecyclerItem>() {
1214

@@ -25,13 +27,14 @@ object BeerCell : Cell<RecyclerItem>() {
2527
override fun bind(
2628
holder: RecyclerView.ViewHolder,
2729
item: RecyclerItem?,
28-
onItemClick: ((RecyclerItem) -> Unit)?
30+
onItemClick: ((RecyclerItem, ImageView) -> Unit)?
2931
) {
3032
if (holder is BeerViewHolder && item is Beer) {
3133
val itemUI = BeerMapper().mapToUI(item)
34+
3235
holder.bind(itemUI)
3336
holder.itemView.setOnClickListener {
34-
onItemClick?.run { this(item) }
37+
onItemClick?.run { this(item, holder.itemView.itemProductImv) }
3538
}
3639
}
3740
}

presentation/src/main/kotlin/app/web/drjackycv/presentation/products/entity/BeerViewHolder.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
package app.web.drjackycv.presentation.products.entity
22

33
import android.view.View
4-
import androidx.core.view.ViewCompat
54
import androidx.recyclerview.widget.RecyclerView
65
import app.web.drjackycv.presentation.extension.load
76
import kotlinx.android.synthetic.main.item_product.view.*
87

98
class BeerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
10-
119
fun bind(beer: BeerUI) = with(itemView) {
12-
ViewCompat.setTransitionName(itemProductImv, beer.id.toString())
10+
itemProductImv.transitionName = beer.id.toString()
1311
itemProductIdTxv.text = beer.id.toString()
1412
itemProductImv.load(beer.imageUrl)
1513
itemProductNameTxv.text = beer.name

presentation/src/main/kotlin/app/web/drjackycv/presentation/products/productdetail/ProductDetailFragment.kt

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.os.Bundle
44
import android.view.View
55
import androidx.fragment.app.Fragment
66
import androidx.navigation.fragment.navArgs
7+
import androidx.transition.TransitionInflater
78
import app.web.drjackycv.presentation.R
89
import app.web.drjackycv.presentation.extension.load
910
import dagger.hilt.android.AndroidEntryPoint
@@ -21,13 +22,22 @@ class ProductDetailFragment : Fragment(R.layout.fragment_product_detail) {
2122
val safeArgs: ProductDetailFragmentArgs by navArgs()
2223
val product = safeArgs.productDetailBeerUI
2324

25+
setSharedElementTransitionOnEnter()
26+
//postponeEnterTransition()
27+
2428
with(product) {
2529
productDetailIdTxv.text = id.toString()
26-
productDetailImv.load(imageUrl)
30+
productDetailImv.transitionName = id.toString()
31+
productDetailImv.load(url = imageUrl, activity = activity)
2732
productDetailNameTxv.text = name
2833
productDetailDescriptionTxv.text = getString(R.string.description, description)
2934
productDetailAbvTxv.text = getString(R.string.abv, abv.toString())
3035
}
3136
}
3237

38+
private fun setSharedElementTransitionOnEnter() {
39+
sharedElementEnterTransition = TransitionInflater.from(context)
40+
.inflateTransition(R.transition.shared_element_transition)
41+
}
42+
3343
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package app.web.drjackycv.presentation.products.productlist
22

3+
import android.widget.ImageView
34
import app.web.drjackycv.domain.base.RecyclerItem
45
import app.web.drjackycv.presentation.base.adapter.BasePagedListAdapter
56
import app.web.drjackycv.presentation.products.entity.BeerCell
67

7-
class ProductsListAdapter(onItemClick: (RecyclerItem) -> Unit) : BasePagedListAdapter(
8+
class ProductsListAdapter(onItemClick: (RecyclerItem, ImageView) -> Unit) : BasePagedListAdapter(
89
BeerCell,
910
onItemClick = onItemClick
1011
)

presentation/src/main/kotlin/app/web/drjackycv/presentation/products/productlist/ProductsListFragment.kt

+16-13
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package app.web.drjackycv.presentation.products.productlist
22

33
import android.os.Bundle
44
import android.view.View
5-
import androidx.core.os.bundleOf
5+
import android.widget.ImageView
66
import androidx.fragment.app.Fragment
77
import androidx.fragment.app.viewModels
8+
import androidx.navigation.fragment.FragmentNavigatorExtras
89
import androidx.navigation.fragment.findNavController
9-
import androidx.navigation.navOptions
1010
import androidx.paging.PagedList
1111
import app.web.drjackycv.domain.base.Failure
1212
import app.web.drjackycv.domain.base.RecyclerItem
@@ -38,6 +38,12 @@ class ProductsListFragment : Fragment(R.layout.fragment_product_list) {
3838
private fun setupRecycler() {
3939
itemErrorContainer.gone()
4040
productListRecyclerView.adapter = productsListAdapter
41+
//productListRecyclerView.itemAnimator = null
42+
postponeEnterTransition()
43+
productListRecyclerView.viewTreeObserver.addOnPreDrawListener {
44+
startPostponedEnterTransition()
45+
true
46+
}
4147
}
4248

4349
private fun setupViewModel() {
@@ -79,17 +85,14 @@ class ProductsListFragment : Fragment(R.layout.fragment_product_list) {
7985
}
8086
}
8187

82-
private fun navigateToProductDetail(item: RecyclerItem) {
83-
val bundle = bundleOf("productDetailBeerUI" to BeerMapper().mapToUI(item as Beer))
84-
val options = navOptions {
85-
anim {
86-
enter = R.anim.enter
87-
exit = R.anim.exit
88-
popEnter = R.anim.enter
89-
popExit = R.anim.exit
90-
}
91-
}
92-
findNavController().navigate(R.id.productDetailFragment, bundle, options)
88+
private fun navigateToProductDetail(item: RecyclerItem, imageView: ImageView) {
89+
val itemUI = BeerMapper().mapToUI(item as Beer)
90+
val action =
91+
ProductsListFragmentDirections.navigateToProductDetailFragment(itemUI)
92+
val extras = FragmentNavigatorExtras(
93+
imageView to itemUI.id.toString()
94+
)
95+
findNavController().navigate(action, extras)
9396
}
9497

9598
}

presentation/src/main/res/navigation/main_navigation.xml

+2-6
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,8 @@
1111
tools:layout="@layout/fragment_product_list">
1212

1313
<action
14-
android:id="@+id/navigateToAlbumDetailFragment"
15-
app:destination="@id/productDetailFragment"
16-
app:enterAnim="@anim/enter"
17-
app:exitAnim="@anim/exit"
18-
app:popEnterAnim="@anim/pop_enter"
19-
app:popExitAnim="@anim/pop_exit" />
14+
android:id="@+id/navigateToProductDetailFragment"
15+
app:destination="@id/productDetailFragment" />
2016

2117
</fragment>
2218

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<changeBounds xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:interpolator="@android:interpolator/fast_out_slow_in" />

0 commit comments

Comments
 (0)