Skip to content

Instantly share code, notes, and snippets.

@sme-shyl001
Last active August 30, 2023 09:36
Show Gist options
  • Save sme-shyl001/1122797d67922ff2febaef92d7e6bfc1 to your computer and use it in GitHub Desktop.
Save sme-shyl001/1122797d67922ff2febaef92d7e6bfc1 to your computer and use it in GitHub Desktop.
Adapter for switching items
class FavoriteItemsListAdapter(
private val activity: Activity,
private val listener: (section: FavoriteSection) -> Unit
) : ListAdapter<FavoriteItem, FavoriteItemsListAdapter.FavoriteViewHolder>(DiffCallback()) {
private var recyclerView: RecyclerView? = null
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
this.recyclerView = recyclerView
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavoriteViewHolder {
val binding = ItemFavoriteBinding.inflate(parent.getInflater(), parent, false)
if (itemCount == 2) binding.root.setTwoItemsWidth()
return FavoriteViewHolder(binding)
}
override fun onBindViewHolder(holder: FavoriteViewHolder, position: Int) {
super.onBindViewHolder(holder, position, emptyList())
}
override fun onBindViewHolder(
holder: FavoriteViewHolder,
position: Int,
payloads: MutableList<Any>
) {
val payload = payloads.firstOrNull() as? FavoritePayload
if (payload == null) {
holder.bind(currentList[position])
} else {
holder.updateSelection(payload.backgroundRes, payload.selected)
}
}
override fun getItemCount() = currentList.size
private fun ViewGroup.setTwoItemsWidth() {
if (activity.isTablet) {
val width = resources.getDimensionPixelSize(R.dimen.top_lists_selectors_container_width)
layoutParams.width = width / 2
} else {
val screenWidth = activity.getScreenParams().widthPixels
val padding = recyclerView?.paddingStart ?: 0
layoutParams.width = screenWidth / 2 - padding
}
}
inner class FavoriteViewHolder(
private val binding: ItemFavoriteBinding,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: FavoriteItem) {
with(binding) {
updateSelection(item.backgroundRes, item.selected)
tvTitle.text = item.type.title.string()
root.setOnClickListener { listener.invoke(item.type) }
}
}
fun updateSelection(@DrawableRes backgroundRes: Int, selected: Boolean) {
with(binding) {
selector.visibleOrGone(selected)
root.setBackgroundResource(backgroundRes)
}
}
}
private class DiffCallback : DiffUtil.ItemCallback<FavoriteItem>() {
override fun areItemsTheSame(oldItem: FavoriteItem, newItem: FavoriteItem) =
oldItem.type == newItem.type
override fun areContentsTheSame(oldItem: FavoriteItem, newItem: FavoriteItem) =
oldItem == newItem
override fun getChangePayload(oldItem: FavoriteItem, newItem: FavoriteItem): Any? {
return when {
oldItem.selected != newItem.selected -> {
FavoritePayload(newItem.backgroundRes, newItem.selected)
}
else -> null
}
}
}
private data class FavoritePayload(
@DrawableRes val backgroundRes: Int,
val selected: Boolean
)
}
class FavoriteItemsLayoutManager(
context: Context
) : LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) {
private val smoothScroller: RecyclerView.SmoothScroller by lazy {
CenterSmoothScroller(context)
}
override fun smoothScrollToPosition(
recyclerView: RecyclerView,
state: RecyclerView.State,
position: Int
) {
smoothScroller.targetPosition = position
startSmoothScroll(smoothScroller)
}
private class CenterSmoothScroller(context: Context) : LinearSmoothScroller(context) {
override fun calculateDtToFit(
viewStart: Int,
viewEnd: Int,
boxStart: Int,
boxEnd: Int,
snapPreference: Int
): Int {
return boxStart + (boxEnd - boxStart) / 2 - (viewStart + (viewEnd - viewStart) / 2)
}
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi
}
override fun calculateTimeForScrolling(dx: Int): Int {
return super.calculateTimeForScrolling(dx)
.coerceAtLeast(MIN_SCROLLING_TIME_MS)
}
}
companion object {
private const val MILLISECONDS_PER_INCH = 100f // default is 25f (bigger = slower)
private const val MIN_SCROLLING_TIME_MS = 200
}
}
// GetFavoriteItemsUseCase.kt
class GetFavoriteItemsUseCase(
coroutineDispatcher: CoroutineDispatcher,
private val getUserUseCase: GetUserUseCase,
) : CoroutineUseCase<GetFavoriteItemsUseCase.Params, List<FavoriteItem>>(coroutineDispatcher) {
override suspend fun execute(parameters: Params): List<FavoriteItem> {
val (sectionId, isUserUSM) = parameters
val user = getUserUseCase(Unit).successOrThrow()
val hasFavorites = user.user.hasA360Favorites
val currentSection = when {
sectionId != -1 -> sectionId
isUserUSM && !hasFavorites -> FavoriteSection.TOP_LISTS.id
else -> FavoriteSection.FAVORITES.id
}
val sections = FavoriteSection.values().toMutableList()
if (!isUserUSM) sections.remove(FavoriteSection.TOP_LISTS)
return sections.mapIndexed { index, favoriteSection ->
FavoriteItem(
type = favoriteSection,
backgroundRes = when (index) {
0 -> R.drawable.background_favorite_start
sections.lastIndex -> R.drawable.background_favorite_end
else -> R.drawable.background_favorite_default
},
selected = favoriteSection.id == currentSection,
)
}
}
data class Params(
val sectionId: Int,
val isUserUSM: Boolean,
)
}
// FavoriteItem.kt
data class FavoriteItem(
val type: FavoriteSection,
@DrawableRes val backgroundRes: Int = R.drawable.background_favorite_default,
val selected: Boolean = false,
)
//
// item_favorite.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="@dimen/favorite_item_height">
<View
android:id="@+id/selector"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="@dimen/margin_2"
android:background="@drawable/background_favorite_selected"
android:elevation="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvTitle"
style="@style/TextAppearance.TextSemiBold"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_margin="@dimen/margin_2"
android:elevation="2dp"
android:gravity="center"
android:includeFontPadding="false"
android:paddingHorizontal="@dimen/padding_16"
android:textColor="@color/black"
android:textSize="@dimen/textSize_14"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
// Selected
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorWhite" />
<corners android:radius="@dimen/selector_shapeRadius" />
</shape>
// Default
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/bgFavoriteItem" />
</shape>
// End
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/bgFavoriteItem" />
<corners
android:bottomRightRadius="@dimen/selector_shapeRadius"
android:topRightRadius="@dimen/selector_shapeRadius" />
</shape>
// Start
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/bgFavoriteItem" />
<corners
android:bottomLeftRadius="@dimen/selector_shapeRadius"
android:topLeftRadius="@dimen/selector_shapeRadius" />
</shape>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment