Reputation: 1736
I have a fragment called "OverviewFragment" and i have a presenter and Contract for it. I am creating a recyclerview adapter from "Overview fragment".
I have certain doubts regarding how to populate content inside the recyclerview. I have read that an adapter should only act as a View in MVP. But I have certain conditions to be satisfied to display the contents inside recyclerview. Where should i write this business logic?
Also there are answers in SO that we should not create presenters for viewholder. So basically i need to write business logics inside adapter to populate contents in recyclerview or I need to link that to "overview" fragment and then do the business logic in the fragment presenter.
Which is the correct method?
Upvotes: 0
Views: 1204
Reputation: 2764
This is how I solve this prob. First all business logic located in Domain level, but you can create any other abstraction level to map your Data Models to View Layer Models. So only specific models located in presentation level. Then our adapter looks like this:
class CustomerCardsAdapter(
var cards: MutableList<ListItem> = mutableListOf()
) : RecyclerView.Adapter<CustomerCardsAdapter.ItemViewHolder>() {
lateinit var cardItemClick: (cardId: ClientCard) -> Unit
override fun getItemViewType(position: Int) = cards[position].getListItemType().ordinal
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): CustomerCardsAdapter.ItemViewHolder {
val resId = when (ItemType.values()[viewType]) {
ItemType.CARD -> R.layout.card_item
ItemType.SIMPLE_ITEM -> R.layout.account_simple_item_view
else -> throw IllegalArgumentException("viewType = $viewType create not support")
}
val view = LayoutInflater.from(viewGroup.context).inflate(resId, viewGroup, false)
return ItemViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ItemViewHolder, position: Int) {
val item = cards[position]
when (item.getListItemType()) {
ItemType.CARD -> viewHolder.populateCard(item)
ItemType.SIMPLE_ITEM -> viewHolder.populateSimpleItem(item)
else -> throw IllegalArgumentException("viewType = ${item.getListItemType()} create not support")
}
}
override fun getItemCount() = cards.size
inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun populateCard(item: ListItem) {
with(itemView as CustomerCardView) {
val card = item as ClientCard
populate(card)
setOnClickListener { cardItemClick(card) }
}
}
fun populateSimpleItem(item: ListItem) {
with(itemView as SimpleItemView) {
val simpleItem = item as SimpleItem
populate(simpleItem)
setOnClickListener { simpleItem.action() }
}
}
}
}
So onCreateViewHolder
just inflates Views and onBindViewHolder
method calling ViewHolders methods to populate data.
RecyclerView items inherited from View classes and contains logic to actually present our data to Views.
private const val CORNER_RADIUS = 4f
private const val SHADOW_ALPHA = 60
class CustomerCardView
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: LinearLayout(context, attrs, defStyleAttr) {
private val imageCard by bind<BlurryCardView>(R.id.card_image)
private val cardNumberTextView by bind<TextView>(R.id.card_number_text_view)
private val imageIps by bind<IpsImageView>(R.id.card_image_ips)
private val cardNameTextView by bind<TextView>(R.id.card_name_text_view)
private val cardActivateView by bind<View>(R.id.card_activate)
private val cardBlockView by bind<View>(R.id.card_unblock)
private val imageContactlessPayView by bind<ImageView>(R.id.image_contactless_pay)
private val smsNotificationsView by bind<ImageView>(R.id.image_sms_notifications)
private val shadowView by bind<ShadowContainer>(R.id.card_shadow)
fun populate(card: ClientCard) {
with(card) {
cardNameTextView.text = name
cardNumberTextView.text = when {
isNotActiveOnlyTokenization -> context.getString(R.string.card_is_not_active_only_tokenization)
isTokenizationNotActivation -> context.getString(R.string.card_is_tokenization_not_activation)
isTokenizationAndActivation -> context.getString(R.string.card_is_tokenization_and_activation)
isBlockedToken -> context.getString(R.string.card_is_blocked_token)
!isActivated -> context.getString(R.string.card_not_activated_text)
supportsUnblocking() -> context.getString(R.string.card_blocked_text)
isActivated && !supportsUnblocking() -> {
maskLast4Symbols(number)
}
else -> maskLast4Symbols(number)
}
imageContactlessPayView.setVisibility(tokens.isNotEmpty())
smsNotificationsView.setVisibility(isAlfaCheck)
populateCardImage(card)
}
}
private fun populateCardImage(card: ClientCard) {
imageCard.setCornerRadius(CORNER_RADIUS)
shadowView.shadowParams.shadowAlpha = SHADOW_ALPHA
post {
val url = ImageUrlFactory.createUrl(card.cardType, imageCard.width, imageCard.height)
imageCard.populateImage(url, {
imageIps.populateWhite(card.ips)
changeVisibility(card)
})
}
}
private fun changeVisibility(card: ClientCard) {
with(card) {
val blocked = (isLocked && isActivated && !isNotActiveOnlyTokenization) || isBlockedToken
cardActivateView.setVisibility(card.showIsNotActiveIcon())
cardBlockView.setVisibility(blocked)
}
}
}
Upvotes: 0