Skip to content
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
Binary file added Result.wmv
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ru.otus.cryptosample.coins.feature

import androidx.recyclerview.widget.DiffUtil
import ru.otus.cryptosample.coins.feature.adapter.CoinsAdapterItem
import ru.otus.cryptosample.coins.feature.adapter.HighlightPayload

class CoinDiffCallback : DiffUtil.ItemCallback<CoinsAdapterItem>() {
override fun areItemsTheSame(oldItem: CoinsAdapterItem, newItem: CoinsAdapterItem): Boolean {
return when {
oldItem is CoinsAdapterItem.CoinItem && newItem is CoinsAdapterItem.CoinItem -> oldItem.coin.id == newItem.coin.id
oldItem is CoinsAdapterItem.CategoryHeader && newItem is CoinsAdapterItem.CategoryHeader -> oldItem.categoryId == newItem.categoryId
oldItem is CoinsAdapterItem.HorizontalCategory && newItem is CoinsAdapterItem.HorizontalCategory -> oldItem.categoryId == newItem.categoryId
else -> false
}
}

override fun areContentsTheSame(oldItem: CoinsAdapterItem, newItem: CoinsAdapterItem): Boolean {
return oldItem == newItem
}

override fun getChangePayload(oldItem: CoinsAdapterItem, newItem: CoinsAdapterItem): Any? {
if (oldItem is CoinsAdapterItem.CoinItem && newItem is CoinsAdapterItem.CoinItem) {
if (oldItem.coin.highlight != newItem.coin.highlight) return "highlight"
}
return null
}
}

//class CoinDiffCallback : DiffUtil.ItemCallback<CoinState>() {
//
// override fun areItemsTheSame(
// oldItem: CoinState,
// newItem: CoinState
// ): Boolean {
// return oldItem.id == newItem.id
// }
//
// override fun areContentsTheSame(
// oldItem: CoinState,
// newItem: CoinState
// ): Boolean {
// return oldItem == newItem
// }
//
// override fun getChangePayload(
// oldItem: CoinState,
// newItem: CoinState
// ): Any? {
// if (oldItem.highlight != newItem.highlight) {
// return HighlightPayload
// }
// return null
// }
//}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch
import ru.otus.cryptosample.CoinsSampleApp
import ru.otus.cryptosample.coins.feature.adapter.CoinsAdapter
Expand All @@ -28,6 +29,15 @@ class CoinListFragment : Fragment() {

private val viewModel: CoinListViewModel by viewModels { factory }

private val sharedViewPool = RecyclerView.RecycledViewPool()

companion object {
private const val VIEW_TYPE_CATEGORY = 0
private const val VIEW_TYPE_COIN = 1
private const val VIEW_TYPE_HORIZONTAL = 2

}

private lateinit var coinsAdapter: CoinsAdapter

override fun onAttach(context: Context) {
Expand Down Expand Up @@ -59,25 +69,32 @@ class CoinListFragment : Fragment() {
}

private fun setupRecyclerView() {
coinsAdapter = CoinsAdapter()

coinsAdapter = CoinsAdapter(sharedViewPool)

val gridLayoutManager = GridLayoutManager(requireContext(), 2)
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (coinsAdapter.getItemViewType(position)) {
0 -> 2 // Category header spans full width
1 -> 1 // Coin item spans half width
else -> 1

gridLayoutManager.spanSizeLookup =
object : GridLayoutManager.SpanSizeLookup() {

override fun getSpanSize(position: Int): Int {
return when (coinsAdapter.getItemViewType(position)) {
VIEW_TYPE_CATEGORY -> 2
VIEW_TYPE_HORIZONTAL -> 2
VIEW_TYPE_COIN -> 1
else -> 1
}
}
}
}

binding.recyclerView.apply {
layoutManager = gridLayoutManager
adapter = coinsAdapter
itemAnimator = FadeSlideItemAnimator()
}
}


private fun setupChipToggle() {
binding.highlightChip.setOnCheckedChangeListener { _, isChecked ->
viewModel.onHighlightMoversToggled(isChecked)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,15 @@ class CoinListViewModel(
}

private fun updateUiState() {
var processedCategories = if (showAll) {
fullCategories
} else {
fullCategories.map { category ->
category.copy(coins = category.coins.take(4))
}
}

processedCategories = processedCategories.map { category ->
category.copy(coins = category.coins.map { coin ->
coin.copy(highlight = highlightMovers && coin.isHotMover)
})
val processedCategories = fullCategories.map { category ->
category.copy(
coins = category.coins.map { coin ->
coin.copy(highlight = highlightMovers && coin.isHotMover)
}
)
}

_state.update { it.copy(categories = processedCategories) }
_state.update { it.copy(categories = processedCategories, showAll = showAll) }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ru.otus.cryptosample.coins.feature

data class CoinsScreenState(
val categories: List<CoinCategoryState> = emptyList(),
val showAll: Boolean = true
)

data class CoinCategoryState(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ru.otus.cryptosample.coins.feature

import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.RecyclerView

class FadeSlideItemAnimator : DefaultItemAnimator() {

override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean {

dispatchAddStarting(holder)

holder.itemView.apply {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

нужно добавить перед стартом dispatchAddStarting

alpha = 0f
translationX = -50f
animate()
.alpha(1f)
.translationX(0f)
.setDuration(250)
.withEndAction {
dispatchAddFinished(holder)
}
.start()
}

return true
}

override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

нужно добавить dispatchRemoveStarting

dispatchRemoveStarting(holder)

holder.itemView.animate()
.alpha(0f)
.translationX(50f)
.setDuration(250)
.withEndAction {
dispatchRemoveFinished(holder)
}
.start()

return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.otus.cryptosample.coins.feature.adapter

import androidx.recyclerview.widget.DiffUtil
import ru.otus.cryptosample.coins.feature.CoinState

class CoinDiffCallback : DiffUtil.ItemCallback<CoinState>() {
override fun areItemsTheSame(oldItem: CoinState, newItem: CoinState): Boolean {
return oldItem.id == newItem.id
}

override fun areContentsTheSame(oldItem: CoinState, newItem: CoinState): Boolean {
return oldItem == newItem
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ru.otus.cryptosample.coins.feature.adapter

import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import coil.load
import ru.otus.cryptosample.R
import ru.otus.cryptosample.coins.feature.CoinState
import ru.otus.cryptosample.databinding.ItemCoinHorizontalBinding

class CoinHorizontalViewHolder(
private val binding: ItemCoinHorizontalBinding
) : RecyclerView.ViewHolder(binding.root) {

fun bind(coin: CoinState) {
with(binding) {
coinName.text = coin.name
coinPrice.text = coin.price
coinChange.text = coin.discount

val changeColor = if (coin.goesUp) {
ContextCompat.getColor(itemView.context, android.R.color.holo_red_dark)
} else {
ContextCompat.getColor(itemView.context, android.R.color.holo_green_dark)
}
coinChange.setTextColor(changeColor)

coinIcon.load(coin.image) {
placeholder(R.drawable.generic)
error(R.drawable.generic)
}

updateHighlight(coin.highlight)
}
}

fun updateHighlight(isHighlighted: Boolean) {
binding.fireBadge.visibility = if (isHighlighted) RecyclerView.VISIBLE else RecyclerView.GONE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ class CoinViewHolder(
fireBadge.isVisible = coin.highlight
}
}

fun updateHighlight(highlight: Boolean) {
binding.fireBadge.isVisible = highlight
}
}
Loading