recycler: add span size abstraction to adapter

Make adapter instances handle span sizes.

This supercedes the hackier solution where the fragments would have to
reference adapter data in order to determine span size. Not anymore.
This commit is contained in:
Alexander Capehart 2022-09-02 13:35:59 -06:00
parent acaf679000
commit be8623ad2d
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
11 changed files with 50 additions and 43 deletions

View file

@ -41,7 +41,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.Sort
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.canScroll
import org.oxycblt.auxio.util.collect
@ -84,13 +83,7 @@ class AlbumDetailFragment :
setOnMenuItemClickListener(this@AlbumDetailFragment)
}
binding.detailRecycler.apply {
adapter = detailAdapter
setSpanSizeLookup { pos ->
val item = detailAdapter.currentList[pos]
item is Album || item is Header || item is SortHeader
}
}
binding.detailRecycler.adapter = detailAdapter
// -- VIEWMODEL SETUP ---

View file

@ -39,7 +39,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.Sort
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately
@ -79,13 +78,7 @@ class ArtistDetailFragment :
setOnMenuItemClickListener(this@ArtistDetailFragment)
}
binding.detailRecycler.apply {
adapter = detailAdapter
setSpanSizeLookup { pos ->
val item = detailAdapter.currentList[pos]
item is Artist || item is Header || item is SortHeader
}
}
binding.detailRecycler.adapter = detailAdapter
// --- VIEWMODEL SETUP ---

View file

@ -40,7 +40,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.Sort
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately
@ -80,13 +79,7 @@ class GenreDetailFragment :
setOnMenuItemClickListener(this@GenreDetailFragment)
}
binding.detailRecycler.apply {
adapter = detailAdapter
setSpanSizeLookup { pos ->
val item = detailAdapter.currentList[pos]
item is Genre || item is Header || item is SortHeader
}
}
binding.detailRecycler.adapter = detailAdapter
// --- VIEWMODEL SETUP ---

View file

@ -77,6 +77,11 @@ class AlbumDetailAdapter(private val listener: Listener) :
}
}
override fun isItemFullWidth(position: Int): Boolean {
val item = differ.currentList[position]
return super.isItemFullWidth(position) || item is Album || item is DiscHeader
}
companion object {
private val DIFFER =
object : SimpleItemCallback<Item>() {

View file

@ -78,6 +78,11 @@ class ArtistDetailAdapter(private val listener: Listener) :
}
}
override fun isItemFullWidth(position: Int): Boolean {
val item = differ.currentList[position]
return super.isItemFullWidth(position) || item is Artist
}
companion object {
private val DIFFER =
object : SimpleItemCallback<Item>() {

View file

@ -26,6 +26,7 @@ import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.databinding.ItemSortHeaderBinding
import org.oxycblt.auxio.detail.SortHeader
import org.oxycblt.auxio.ui.recycler.AuxioRecyclerView
import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
@ -38,7 +39,7 @@ import org.oxycblt.auxio.util.inflater
abstract class DetailAdapter<L : DetailAdapter.Listener>(
private val listener: L,
diffCallback: DiffUtil.ItemCallback<Item>
) : IndicatorAdapter<RecyclerView.ViewHolder>() {
) : IndicatorAdapter<RecyclerView.ViewHolder>(), AuxioRecyclerView.SpanSizeLookup {
private var isPlaying = false
@Suppress("LeakingThis") override fun getItemCount() = differ.currentList.size
@ -77,6 +78,11 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
super.onBindViewHolder(holder, position, payloads)
}
override fun isItemFullWidth(position: Int): Boolean {
val item = differ.currentList[position]
return item is Header || item is SortHeader
}
protected val differ = AsyncListDiffer(this, diffCallback)
override val currentList: List<Item>

View file

@ -71,6 +71,11 @@ class GenreDetailAdapter(private val listener: Listener) :
}
}
override fun isItemFullWidth(position: Int): Boolean {
val item = differ.currentList[position]
return super.isItemFullWidth(position) || item is Genre
}
companion object {
val DIFFER =
object : SimpleItemCallback<Item>() {

View file

@ -33,9 +33,9 @@ import org.oxycblt.auxio.util.getColorCompat
import org.oxycblt.auxio.util.getDrawableCompat
/**
* View that displays the playback indicator. Nominally emulates [StyledImageView], but is
* much different internally as an animated icon can't be wrapped within StyledDrawable without
* causing insane issues.
* View that displays the playback indicator. Nominally emulates [StyledImageView], but is much
* different internally as an animated icon can't be wrapped within StyledDrawable without causing
* insane issues.
* @author OxygenCobalt
*/
class IndicatorView
@ -45,7 +45,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
private val playingIndicatorDrawable =
context.getDrawableCompat(R.drawable.ic_playing_indicator_24) as AnimationDrawable
private val pausedIndicatorDrawable = context.getDrawableCompat(R.drawable.ic_paused_indicator_24)
private val pausedIndicatorDrawable =
context.getDrawableCompat(R.drawable.ic_paused_indicator_24)
private val indicatorMatrix = Matrix()
private val indicatorMatrixSrc = RectF()

View file

@ -26,6 +26,7 @@ import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
import org.oxycblt.auxio.ui.recycler.AuxioRecyclerView
import org.oxycblt.auxio.ui.recycler.GenreViewHolder
import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
@ -36,7 +37,7 @@ import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
import org.oxycblt.auxio.ui.recycler.SongViewHolder
class SearchAdapter(private val listener: MenuItemListener) :
IndicatorAdapter<RecyclerView.ViewHolder>() {
IndicatorAdapter<RecyclerView.ViewHolder>(), AuxioRecyclerView.SpanSizeLookup {
private val differ = AsyncListDiffer(this, DIFFER)
override fun getItemCount() = differ.currentList.size
@ -79,6 +80,8 @@ class SearchAdapter(private val listener: MenuItemListener) :
}
}
override fun isItemFullWidth(position: Int) = differ.currentList[position] is Header
override val currentList: List<Item>
get() = differ.currentList

View file

@ -39,7 +39,6 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.ui.recycler.MenuItemListener
import org.oxycblt.auxio.util.androidViewModels
@ -104,10 +103,7 @@ class SearchFragment :
}
}
binding.searchRecycler.apply {
adapter = searchAdapter
setSpanSizeLookup { pos -> searchAdapter.currentList[pos] is Header }
}
binding.searchRecycler.adapter = searchAdapter
// --- VIEWMODEL SETUP ---

View file

@ -54,13 +54,20 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
return insets
}
// TODO: Move abstraction to adapters since using external data will not work well
inline fun setSpanSizeLookup(crossinline fullWidth: (Int) -> Boolean) {
val glm = layoutManager as GridLayoutManager
val spanCount = glm.spanCount
override fun setAdapter(adapter: Adapter<*>?) {
super.setAdapter(adapter)
if (adapter is SpanSizeLookup) {
val glm = (layoutManager as GridLayoutManager)
glm.spanSizeLookup =
object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int) = if (fullWidth(position)) spanCount else 1
override fun getSpanSize(position: Int) =
if (adapter.isItemFullWidth(position)) glm.spanCount else 1
}
}
}
interface SpanSizeLookup {
fun isItemFullWidth(position: Int): Boolean
}
}