Comfortable Grid (#3238)

* Comfortable Grid

* Add requested changes

* Add more requested changes
This commit is contained in:
jobobby04
2020-05-25 13:39:14 -04:00
committed by GitHub
parent f05b99ec1f
commit 52e82b3548
17 changed files with 383 additions and 72 deletions
@@ -22,6 +22,9 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMFORTABLE_GRID
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMPACT_GRID
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_LIST
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.databinding.SourceControllerBinding
@@ -187,7 +190,7 @@ open class BrowseSourceController(bundle: Bundle) :
binding.catalogueView.removeView(oldRecycler)
}
val recycler = if (presenter.isListMode) {
val recycler = if (preferences.catalogueDisplayMode().get() == DISPLAY_LIST) {
RecyclerView(view.context).apply {
id = R.id.recycler
layoutManager = LinearLayoutManager(context)
@@ -205,7 +208,7 @@ open class BrowseSourceController(bundle: Bundle) :
(layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (adapter?.getItemViewType(position)) {
R.layout.source_grid_item, null -> 1
R.layout.source_grid_item, R.layout.source_comfortable_grid_item, null -> 1
else -> spanCount
}
}
@@ -266,15 +269,13 @@ open class BrowseSourceController(bundle: Bundle) :
}
)
// Show next display mode
menu.findItem(R.id.action_display_mode).apply {
val icon = if (presenter.isListMode) {
R.drawable.ic_view_module_24dp
} else {
R.drawable.ic_view_list_24dp
}
setIcon(icon)
val displayItem = when (preferences.catalogueDisplayMode().get()) {
DISPLAY_COMPACT_GRID -> R.id.action_compact_grid
DISPLAY_LIST -> R.id.action_list
DISPLAY_COMFORTABLE_GRID -> R.id.action_comfortable_grid
else -> throw NotImplementedError("Unimplemented display")
}
menu.findItem(displayItem).isChecked = true
}
override fun onPrepareOptionsMenu(menu: Menu) {
@@ -290,7 +291,9 @@ open class BrowseSourceController(bundle: Bundle) :
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_search -> expandActionViewFromInteraction = true
R.id.action_display_mode -> swapDisplayMode()
R.id.action_compact_grid -> setDisplayMode(DISPLAY_COMPACT_GRID)
R.id.action_list -> setDisplayMode(DISPLAY_LIST)
R.id.action_comfortable_grid -> setDisplayMode(DISPLAY_COMFORTABLE_GRID)
R.id.action_open_in_web_view -> openInWebView()
R.id.action_local_source_help -> openLocalSourceHelpGuide()
}
@@ -433,17 +436,19 @@ open class BrowseSourceController(bundle: Bundle) :
}
/**
* Swaps the current display mode.
* Sets the current display mode.
*
* @param mode the mode to change to
*/
private fun swapDisplayMode() {
private fun setDisplayMode(mode: Int) {
val view = view ?: return
val adapter = adapter ?: return
presenter.swapDisplayMode()
val isListMode = presenter.isListMode
preferences.catalogueDisplayMode().set(mode)
presenter.refreshDisplayMode()
activity?.invalidateOptionsMenu()
setupRecycler(view)
if (!isListMode || !view.context.connectivityManager.isActiveNetworkMetered) {
if (mode == DISPLAY_LIST || !view.context.connectivityManager.isActiveNetworkMetered) {
// Initialize mangas if going to grid view or if over wifi when going to list view
val mangas = (0 until adapter.itemCount).mapNotNull {
(adapter.getItem(it) as? SourceItem)?.manga
@@ -87,12 +87,6 @@ open class BrowseSourcePresenter(
*/
private val mangaDetailSubject = PublishSubject.create<List<Manga>>()
/**
* Whether the view is in list mode or not.
*/
var isListMode: Boolean = false
private set
/**
* Subscription for the pager.
*/
@@ -119,7 +113,6 @@ open class BrowseSourcePresenter(
query = savedState.getString(::query.name, "")
}
isListMode = prefs.catalogueAsList().get()
restartPager()
}
@@ -145,7 +138,7 @@ open class BrowseSourcePresenter(
val sourceId = source.id
val catalogueAsList = prefs.catalogueAsList()
val catalogueDisplayMode = prefs.catalogueDisplayMode()
// Prepare the pager.
pagerSubscription?.let { remove(it) }
@@ -153,7 +146,7 @@ open class BrowseSourcePresenter(
.observeOn(Schedulers.io())
.map { pair -> pair.first to pair.second.map { networkToLocalManga(it, sourceId) } }
.doOnNext { initializeMangas(it.second) }
.map { pair -> pair.first to pair.second.map { SourceItem(it, catalogueAsList) } }
.map { pair -> pair.first to pair.second.map { SourceItem(it, catalogueDisplayMode) } }
.observeOn(AndroidSchedulers.mainThread())
.subscribeReplay(
{ view, (page, mangas) ->
@@ -273,12 +266,9 @@ open class BrowseSourcePresenter(
}
/**
* Changes the active display mode.
* Refreshes the active display mode.
*/
fun swapDisplayMode() {
val mode = !isListMode
prefs.catalogueAsList().set(mode)
isListMode = mode
fun refreshDisplayMode() {
subscribeToMangaInitializer()
}
@@ -0,0 +1,56 @@
package eu.kanade.tachiyomi.ui.browse.source.browse
import android.view.View
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.widget.StateImageViewTarget
import kotlinx.android.synthetic.main.source_comfortable_grid_item.card
import kotlinx.android.synthetic.main.source_comfortable_grid_item.progress
import kotlinx.android.synthetic.main.source_comfortable_grid_item.thumbnail
import kotlinx.android.synthetic.main.source_comfortable_grid_item.title
/**
* Class used to hold the displayed data of a manga in the catalogue, like the cover or the title.
* All the elements from the layout file "item_source_grid" are available in this class.
*
* @param view the inflated view for this holder.
* @param adapter the adapter handling this holder.
* @constructor creates a new catalogue holder.
*/
class SourceComfortableGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
SourceGridHolder(view, adapter) {
/**
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
* holder with the given manga.
*
* @param manga the manga to bind.
*/
override fun onSetValues(manga: Manga) {
// Set manga title
title.text = manga.title
// Set alpha of thumbnail.
thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f
setImage(manga)
}
override fun setImage(manga: Manga) {
// Setting this via XML doesn't work
card.clipToOutline = true
GlideApp.with(view.context).clear(thumbnail)
if (!manga.thumbnail_url.isNullOrEmpty()) {
GlideApp.with(view.context)
.load(manga.toMangaThumbnail())
.diskCacheStrategy(DiskCacheStrategy.DATA)
.centerCrop()
.placeholder(android.R.color.transparent)
.into(StateImageViewTarget(thumbnail, progress))
}
}
}
@@ -20,7 +20,7 @@ import kotlinx.android.synthetic.main.source_grid_item.title
* @param adapter the adapter handling this holder.
* @constructor creates a new catalogue holder.
*/
class SourceGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
open class SourceGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
SourceHolder(view, adapter) {
/**
@@ -4,6 +4,7 @@ import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import com.tfcporciuncula.flow.Preference
import eu.davidea.flexibleadapter.FlexibleAdapter
@@ -11,18 +12,20 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMPACT_GRID
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_LIST
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import kotlinx.android.synthetic.main.source_grid_item.view.card
import kotlinx.android.synthetic.main.source_grid_item.view.gradient
class SourceItem(val manga: Manga, private val catalogueAsList: Preference<Boolean>) :
class SourceItem(val manga: Manga, private val catalogueDisplayMode: Preference<Int>) :
AbstractFlexibleItem<SourceHolder>() {
override fun getLayoutRes(): Int {
return if (catalogueAsList.get()) {
R.layout.source_list_item
} else {
R.layout.source_grid_item
return when (catalogueDisplayMode.get()) {
DISPLAY_COMPACT_GRID -> R.layout.source_grid_item
DISPLAY_LIST -> R.layout.source_list_item
else -> R.layout.source_comfortable_grid_item
}
}
@@ -32,15 +35,28 @@ class SourceItem(val manga: Manga, private val catalogueAsList: Preference<Boole
): SourceHolder {
val parent = adapter.recyclerView
return if (parent is AutofitRecyclerView) {
view.apply {
card.layoutParams = FrameLayout.LayoutParams(
MATCH_PARENT, parent.itemWidth / 3 * 4
)
gradient.layoutParams = FrameLayout.LayoutParams(
MATCH_PARENT, parent.itemWidth / 3 * 4 / 2, Gravity.BOTTOM
)
val coverHeight = parent.itemWidth / 3 * 4
if (catalogueDisplayMode.get() == DISPLAY_COMPACT_GRID) {
view.apply {
card.layoutParams = FrameLayout.LayoutParams(
MATCH_PARENT, coverHeight
)
gradient.layoutParams = FrameLayout.LayoutParams(
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
)
}
SourceGridHolder(view, adapter)
} else {
view.apply {
card.layoutParams = ConstraintLayout.LayoutParams(
MATCH_PARENT, coverHeight
)
gradient.layoutParams = FrameLayout.LayoutParams(
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
)
}
SourceComfortableGridHolder(view, adapter)
}
SourceGridHolder(view, adapter)
} else {
SourceListHolder(view, adapter)
}