Код: Выделить всё
data class AssetMinDataDomain(
val id: String = "",
val metricsMinDomain: MetricsMinDomain = MetricsMinDomain(),
val name: String = "",
val symbol: String? = ""
) {
var isSelected: Boolean = name == DEFAULT_ASSET // Default selected item in Market Cap
companion object {
const val DEFAULT_ASSET = "Bitcoin"
}
}
Код: Выделить всё
class DiffUtilAssetMin : DiffUtil.ItemCallback() {
// DiffUtil uses this test to help discover if an item was added, removed, or moved.
// Use attribute(s) that represent object's identity.
override fun areItemsTheSame(
oldItem: AssetMinDataDomain,
newItem: AssetMinDataDomain
): Boolean {
return oldItem.id == newItem.id
}
// Check whether oldItem and newItem contain the same data; that is, whether they are equal.
// If there are differences between oldItem and newItem, this code tells DiffUtil that the item has been updated.
// Note: If you are using data class and trying to detect changes based on properties outside primary constructor,
// you may need to do additional checking since the default generated `equals` only uses properties inside primary constructor.
override fun areContentsTheSame(
oldItem: AssetMinDataDomain,
newItem: AssetMinDataDomain
): Boolean {
return oldItem == newItem && oldItem.isSelected == newItem.isSelected
}
override fun getChangePayload(oldItem: AssetMinDataDomain, newItem: AssetMinDataDomain): Any? {
if (oldItem.id == newItem.id) {
return if (oldItem.metricsMinDomain.priceUsd == newItem.metricsMinDomain.priceUsd
&& oldItem.isSelected == newItem.isSelected
) {
super.getChangePayload(oldItem, newItem)
} else {
// Add object's attribute(s) that has changed using this payload
Bundle().apply {
newItem.metricsMinDoman.priceUsd?.let {
putDouble(ARG_MARKET_PRICE, it)
}
putBoolean(ARG_IS_SELECTED, newItem.isSelected)
}
}
}
return super.getChangePayload(oldItem, newItem)
}
companion object {
const val ARG_MARKET_PRICE = "arg.market.price"
const val ARG_IS_SELECTED = "arg.is.selected"
}
}
Код: Выделить всё
class AssetMinAdapter(
private val iconLink: String,
private val glide: RequestManager,
private val maximumSelectedAsset: Int,
private val itemListener: ItemListener
) : FilterableListAdapter(DiffUtilAssetMin()) {
inner class ItemView(itemView: AssetMinCardBinding) : RecyclerView.ViewHolder(itemView.root) {
internal val cardView = itemView.cardRoot
private val assetName = itemView.assetName
private val assetSymbol = itemView.assetSymbol
private val assetPrice = itemView.assetPrice
private val assetIcon = itemView.assetIcon
// Full update/binding
fun bindFull(domain: AssetMinDataDomain) {
with(itemView.context) {
bindTextData(
domain.name,
domain.symbol,
domain.metricsMinDomain.marketDataMinDomain.priceUsd,
domain.isSelected
)
glide
.load(
getString(
R.string.icon_url,
iconLink,
domain.id
)
)
.circleCrop()
.into(assetIcon)
}
}
// Partial update/binding
fun bindPartial(domain: AssetMinDataDomain, bundle: Bundle) {
bindTextData(
domain.name,
domain.symbol,
bundle.getDouble(DiffUtilAssetMin.ARG_MARKET_PRICE),
bundle.getBoolean(DiffUtilAssetMin.ARG_IS_SELECTED)
)
}
private fun bindTextData(name: String, symbol: String?, price: Double?, isSelected: Boolean) {
with(itemView.context) {
assetName.text = name
assetSymbol.text = symbol ?: getString(R.string.empty)
assetPrice.text =
getString(R.string.us_dollars, NumbersUtil.formatFractional(price))
cardView.isChecked = isSelected
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemView =
ItemView(
AssetMinCardBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ItemView, position: Int) {
onBindViewHolder(holder, holder.bindingAdapterPosition, emptyList())
}
override fun onBindViewHolder(holder: ItemView, position: Int, payloads: List) {
with(holder) {
val domain = getItem(bindingAdapterPosition)
// Upon scroll we need to rebind click listener regardless if full or partial update
// this is to ensure that click listener is bound to correct item.
cardView.setOnClickListener {
if (domain.name.equals(AssetMinDataDomain.DEFAULT_ASSET, true))
return@setOnClickListener
val selectedSize = currentList.filter { it.isSelected }.size
// To avoid abuse usage, limit the selectable asset market cap
// this also including the default asset which is BTC
if (selectedSize >= maximumSelectedAsset && !domain.isSelected) {
itemListener.onLimitReached()
return@setOnClickListener
}
domain.isSelected = !cardView.isChecked
cardView.isChecked = domain.isSelected
itemListener.onAssetSelected()
}
if (payloads.isEmpty() || payloads.first() !is Bundle)
bindFull(domain) // Full update/binding
else {
val bundle = payloads.first() as Bundle
bindPartial(domain, bundle) // Partial update/binding
}
}
}
// Required when setHasStableIds is set to true
override fun getItemId(position: Int): Long {
return currentList[position].id.hashCode().toLong()
}
override fun onFilter(
list: List,
constraint: String
): List {
return list.filter {
it.name.lowercase().contains(constraint.lowercase()) ||
it.symbol?.lowercase()?.contains(constraint.lowercase()) == true ||
it.name.equals(AssetMinDataDomain.DEFAULT_ASSET, true)
}
}
// Since adapter.currentList() does not immediately reflecting the actual update from filters
// we can use this callback instead to listen and get the latest list
override fun onCurrentListChanged(
previousList: List,
currentList: List
) {
super.onCurrentListChanged(previousList, currentList)
itemListener.onListUpdate(previousList, currentList)
}
interface ItemListener {
fun onAssetSelected()
fun onLimitReached()
fun onListUpdate(
previousList: List,
currentList: List
)
}
}
Код: Выделить всё
// First Row
BTC(isSelected = true) // default
ETH(isSelected = false)
BNB(isSelected = false)
// Second Row
DAI(isSelected = false)
USDT(isSelected = false)
SOL(isSelected = false)
// Third Row
ADA(isSelected = false)
XRP(isSelected = false)
DOGE(isSelected = false)
Код: Выделить всё
// First Row
BTC(isSelected = true) // default
ETH(isSelected = true)
BNB(isSelected = false)
// Second Row
DAI(isSelected = true)
USDT(isSelected = true)
SOL(isSelected = false)
// Third Row
ADA(isSelected = false)
XRP(isSelected = false)
DOGE(isSelected = false)
Код: Выделить всё
// First Row
BTC(isSelected = true) // default
ETH(isSelected = false)
BNB(isSelected = false)
// Second Row
DAI(isSelected = false)
USDT(isSelected = false)
SOL(isSelected = false)
// Third Row
ADA(isSelected = false)
XRP(isSelected = false)
DOGE(isSelected = false)
Код: Выделить всё
// First Row
BTC (isSelected = true) // default
ETH (isSelected = false) // working as expected
BNB(isSelected = true but pointing to old reference)
// Second Row
DAI(isSelected = false) // working as expected
USDT(isSelected = false) // working as expected
SOL(isSelected = true but pointing to old reference)
// Third Row
ADA(isSelected = true but pointing to old reference)
XRP (isSelected = true but pointing to old reference)
DOGE (isSelected = true but pointing to old reference)
Подробнее здесь: https://stackoverflow.com/questions/773 ... submitlist