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:
parent
acaf679000
commit
be8623ad2d
11 changed files with 50 additions and 43 deletions
|
|
@ -41,7 +41,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.ui.Sort
|
import org.oxycblt.auxio.ui.Sort
|
||||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
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.Item
|
||||||
import org.oxycblt.auxio.util.canScroll
|
import org.oxycblt.auxio.util.canScroll
|
||||||
import org.oxycblt.auxio.util.collect
|
import org.oxycblt.auxio.util.collect
|
||||||
|
|
@ -84,13 +83,7 @@ class AlbumDetailFragment :
|
||||||
setOnMenuItemClickListener(this@AlbumDetailFragment)
|
setOnMenuItemClickListener(this@AlbumDetailFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.detailRecycler.apply {
|
binding.detailRecycler.adapter = detailAdapter
|
||||||
adapter = detailAdapter
|
|
||||||
setSpanSizeLookup { pos ->
|
|
||||||
val item = detailAdapter.currentList[pos]
|
|
||||||
item is Album || item is Header || item is SortHeader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- VIEWMODEL SETUP ---
|
// -- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.ui.Sort
|
import org.oxycblt.auxio.ui.Sort
|
||||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
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.Item
|
||||||
import org.oxycblt.auxio.util.collect
|
import org.oxycblt.auxio.util.collect
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
|
@ -79,13 +78,7 @@ class ArtistDetailFragment :
|
||||||
setOnMenuItemClickListener(this@ArtistDetailFragment)
|
setOnMenuItemClickListener(this@ArtistDetailFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.detailRecycler.apply {
|
binding.detailRecycler.adapter = detailAdapter
|
||||||
adapter = detailAdapter
|
|
||||||
setSpanSizeLookup { pos ->
|
|
||||||
val item = detailAdapter.currentList[pos]
|
|
||||||
item is Artist || item is Header || item is SortHeader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.ui.Sort
|
import org.oxycblt.auxio.ui.Sort
|
||||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
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.Item
|
||||||
import org.oxycblt.auxio.util.collect
|
import org.oxycblt.auxio.util.collect
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
|
@ -80,13 +79,7 @@ class GenreDetailFragment :
|
||||||
setOnMenuItemClickListener(this@GenreDetailFragment)
|
setOnMenuItemClickListener(this@GenreDetailFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.detailRecycler.apply {
|
binding.detailRecycler.adapter = detailAdapter
|
||||||
adapter = detailAdapter
|
|
||||||
setSpanSizeLookup { pos ->
|
|
||||||
val item = detailAdapter.currentList[pos]
|
|
||||||
item is Genre || item is Header || item is SortHeader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
companion object {
|
||||||
private val DIFFER =
|
private val DIFFER =
|
||||||
object : SimpleItemCallback<Item>() {
|
object : SimpleItemCallback<Item>() {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
companion object {
|
||||||
private val DIFFER =
|
private val DIFFER =
|
||||||
object : SimpleItemCallback<Item>() {
|
object : SimpleItemCallback<Item>() {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.databinding.ItemSortHeaderBinding
|
import org.oxycblt.auxio.databinding.ItemSortHeaderBinding
|
||||||
import org.oxycblt.auxio.detail.SortHeader
|
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.Header
|
||||||
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
|
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
|
||||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||||
|
|
@ -38,7 +39,7 @@ import org.oxycblt.auxio.util.inflater
|
||||||
abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
||||||
private val listener: L,
|
private val listener: L,
|
||||||
diffCallback: DiffUtil.ItemCallback<Item>
|
diffCallback: DiffUtil.ItemCallback<Item>
|
||||||
) : IndicatorAdapter<RecyclerView.ViewHolder>() {
|
) : IndicatorAdapter<RecyclerView.ViewHolder>(), AuxioRecyclerView.SpanSizeLookup {
|
||||||
private var isPlaying = false
|
private var isPlaying = false
|
||||||
|
|
||||||
@Suppress("LeakingThis") override fun getItemCount() = differ.currentList.size
|
@Suppress("LeakingThis") override fun getItemCount() = differ.currentList.size
|
||||||
|
|
@ -77,6 +78,11 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
||||||
super.onBindViewHolder(holder, position, payloads)
|
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)
|
protected val differ = AsyncListDiffer(this, diffCallback)
|
||||||
|
|
||||||
override val currentList: List<Item>
|
override val currentList: List<Item>
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
companion object {
|
||||||
val DIFFER =
|
val DIFFER =
|
||||||
object : SimpleItemCallback<Item>() {
|
object : SimpleItemCallback<Item>() {
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ import org.oxycblt.auxio.util.getColorCompat
|
||||||
import org.oxycblt.auxio.util.getDrawableCompat
|
import org.oxycblt.auxio.util.getDrawableCompat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View that displays the playback indicator. Nominally emulates [StyledImageView], but is
|
* View that displays the playback indicator. Nominally emulates [StyledImageView], but is much
|
||||||
* much different internally as an animated icon can't be wrapped within StyledDrawable without
|
* different internally as an animated icon can't be wrapped within StyledDrawable without causing
|
||||||
* causing insane issues.
|
* insane issues.
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class IndicatorView
|
class IndicatorView
|
||||||
|
|
@ -45,7 +45,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
||||||
private val playingIndicatorDrawable =
|
private val playingIndicatorDrawable =
|
||||||
context.getDrawableCompat(R.drawable.ic_playing_indicator_24) as AnimationDrawable
|
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 indicatorMatrix = Matrix()
|
||||||
private val indicatorMatrixSrc = RectF()
|
private val indicatorMatrixSrc = RectF()
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
|
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
|
||||||
import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
|
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.GenreViewHolder
|
||||||
import org.oxycblt.auxio.ui.recycler.Header
|
import org.oxycblt.auxio.ui.recycler.Header
|
||||||
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
|
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
|
import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
||||||
|
|
||||||
class SearchAdapter(private val listener: MenuItemListener) :
|
class SearchAdapter(private val listener: MenuItemListener) :
|
||||||
IndicatorAdapter<RecyclerView.ViewHolder>() {
|
IndicatorAdapter<RecyclerView.ViewHolder>(), AuxioRecyclerView.SpanSizeLookup {
|
||||||
private val differ = AsyncListDiffer(this, DIFFER)
|
private val differ = AsyncListDiffer(this, DIFFER)
|
||||||
|
|
||||||
override fun getItemCount() = differ.currentList.size
|
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>
|
override val currentList: List<Item>
|
||||||
get() = differ.currentList
|
get() = differ.currentList
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
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.Item
|
||||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||||
import org.oxycblt.auxio.util.androidViewModels
|
import org.oxycblt.auxio.util.androidViewModels
|
||||||
|
|
@ -104,10 +103,7 @@ class SearchFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.searchRecycler.apply {
|
binding.searchRecycler.adapter = searchAdapter
|
||||||
adapter = searchAdapter
|
|
||||||
setSpanSizeLookup { pos -> searchAdapter.currentList[pos] is Header }
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,13 +54,20 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
||||||
return insets
|
return insets
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move abstraction to adapters since using external data will not work well
|
override fun setAdapter(adapter: Adapter<*>?) {
|
||||||
inline fun setSpanSizeLookup(crossinline fullWidth: (Int) -> Boolean) {
|
super.setAdapter(adapter)
|
||||||
val glm = layoutManager as GridLayoutManager
|
|
||||||
val spanCount = glm.spanCount
|
if (adapter is SpanSizeLookup) {
|
||||||
glm.spanSizeLookup =
|
val glm = (layoutManager as GridLayoutManager)
|
||||||
object : GridLayoutManager.SpanSizeLookup() {
|
glm.spanSizeLookup =
|
||||||
override fun getSpanSize(position: Int) = if (fullWidth(position)) spanCount else 1
|
object : GridLayoutManager.SpanSizeLookup() {
|
||||||
}
|
override fun getSpanSize(position: Int) =
|
||||||
|
if (adapter.isItemFullWidth(position)) glm.spanCount else 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpanSizeLookup {
|
||||||
|
fun isItemFullWidth(position: Int): Boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue