mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-22 23:21:49 +01:00
Rewrite catalogue adapter
This commit is contained in:
parent
f86c3c81bf
commit
871e17c2f5
@ -1,107 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.catalogue
|
|
||||||
|
|
||||||
import android.view.Gravity
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
||||||
import android.widget.FrameLayout
|
|
||||||
import eu.davidea.flexibleadapter4.FlexibleAdapter
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.util.inflate
|
|
||||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
|
||||||
import kotlinx.android.synthetic.main.item_catalogue_grid.view.*
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter storing a list of manga from the catalogue.
|
|
||||||
*
|
|
||||||
* @param fragment the fragment containing this adapter.
|
|
||||||
*/
|
|
||||||
class CatalogueAdapter(val fragment: CatalogueFragment) : FlexibleAdapter<CatalogueHolder, Manga>() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property to get the list of manga in the adapter.
|
|
||||||
*/
|
|
||||||
val items: List<Manga>
|
|
||||||
get() = mItems
|
|
||||||
|
|
||||||
init {
|
|
||||||
mItems = ArrayList()
|
|
||||||
setHasStableIds(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a list of manga to the adapter.
|
|
||||||
*
|
|
||||||
* @param list the list to add.
|
|
||||||
*/
|
|
||||||
fun addItems(list: List<Manga>) {
|
|
||||||
if (list.isNotEmpty()) {
|
|
||||||
val sizeBeforeAdding = mItems.size
|
|
||||||
mItems.addAll(list)
|
|
||||||
notifyItemRangeInserted(sizeBeforeAdding, list.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the list of manga from the adapter.
|
|
||||||
*/
|
|
||||||
fun clear() {
|
|
||||||
val sizeBeforeRemoving = mItems.size
|
|
||||||
mItems.clear()
|
|
||||||
notifyItemRangeRemoved(0, sizeBeforeRemoving)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the identifier for a manga.
|
|
||||||
*
|
|
||||||
* @param position the position in the adapter.
|
|
||||||
* @return an identifier for the item.
|
|
||||||
*/
|
|
||||||
override fun getItemId(position: Int): Long {
|
|
||||||
return mItems[position].id!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to filter the list. Required but not used.
|
|
||||||
*/
|
|
||||||
override fun updateDataSet(param: String) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new view holder.
|
|
||||||
*
|
|
||||||
* @param parent the parent view.
|
|
||||||
* @param viewType the type of the holder.
|
|
||||||
* @return a new view holder for a manga.
|
|
||||||
*/
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CatalogueHolder {
|
|
||||||
if (parent.id == R.id.catalogue_grid) {
|
|
||||||
val view = parent.inflate(R.layout.item_catalogue_grid).apply {
|
|
||||||
card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
|
|
||||||
gradient.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM)
|
|
||||||
}
|
|
||||||
return CatalogueGridHolder(view, this, fragment)
|
|
||||||
} else {
|
|
||||||
val view = parent.inflate(R.layout.item_catalogue_list)
|
|
||||||
return CatalogueListHolder(view, this, fragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds a holder with a new position.
|
|
||||||
*
|
|
||||||
* @param holder the holder to bind.
|
|
||||||
* @param position the position to bind.
|
|
||||||
*/
|
|
||||||
override fun onBindViewHolder(holder: CatalogueHolder, position: Int) {
|
|
||||||
val manga = getItem(position)
|
|
||||||
holder.onSetValues(manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property to return the height for the covers based on the width to keep an aspect ratio.
|
|
||||||
*/
|
|
||||||
val coverHeight: Int
|
|
||||||
get() = (fragment.recycler as AutofitRecyclerView).itemWidth / 3 * 4
|
|
||||||
|
|
||||||
}
|
|
@ -11,11 +11,12 @@ import android.widget.ProgressBar
|
|||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.f2prateek.rx.preferences.Preference
|
import com.f2prateek.rx.preferences.Preference
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.source.model.FilterList
|
import eu.kanade.tachiyomi.data.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.data.source.online.LoginSource
|
import eu.kanade.tachiyomi.data.source.online.LoginSource
|
||||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
|
||||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||||
@ -24,7 +25,6 @@ import eu.kanade.tachiyomi.util.inflate
|
|||||||
import eu.kanade.tachiyomi.util.snack
|
import eu.kanade.tachiyomi.util.snack
|
||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||||
import eu.kanade.tachiyomi.widget.EndlessScrollListener
|
|
||||||
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
|
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.android.synthetic.main.fragment_catalogue.*
|
import kotlinx.android.synthetic.main.fragment_catalogue.*
|
||||||
@ -40,7 +40,10 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
|
|||||||
* Uses R.layout.fragment_catalogue.
|
* Uses R.layout.fragment_catalogue.
|
||||||
*/
|
*/
|
||||||
@RequiresPresenter(CataloguePresenter::class)
|
@RequiresPresenter(CataloguePresenter::class)
|
||||||
open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleViewHolder.OnListItemClickListener {
|
open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(),
|
||||||
|
FlexibleAdapter.OnItemClickListener,
|
||||||
|
FlexibleAdapter.OnItemLongClickListener,
|
||||||
|
FlexibleAdapter.EndlessScrollListener<ProgressItem> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spinner shown in the toolbar to change the selected source.
|
* Spinner shown in the toolbar to change the selected source.
|
||||||
@ -50,12 +53,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
/**
|
/**
|
||||||
* Adapter containing the list of manga from the catalogue.
|
* Adapter containing the list of manga from the catalogue.
|
||||||
*/
|
*/
|
||||||
private lateinit var adapter: CatalogueAdapter
|
private lateinit var adapter: FlexibleAdapter<IFlexible<*>>
|
||||||
|
|
||||||
/**
|
|
||||||
* Scroll listener. It loads next pages when the end of the list is reached.
|
|
||||||
*/
|
|
||||||
private var scrollListener: EndlessScrollListener? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query of the search box.
|
* Query of the search box.
|
||||||
@ -130,6 +128,8 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
|
|
||||||
lateinit var recycler: RecyclerView
|
lateinit var recycler: RecyclerView
|
||||||
|
|
||||||
|
private var progressItem: ProgressItem? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of this fragment.
|
* Creates a new instance of this fragment.
|
||||||
@ -160,7 +160,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize adapter, scroll listener and recycler views
|
// Initialize adapter, scroll listener and recycler views
|
||||||
adapter = CatalogueAdapter(this)
|
adapter = FlexibleAdapter(null, this)
|
||||||
setupRecycler()
|
setupRecycler()
|
||||||
|
|
||||||
// Create toolbar spinner
|
// Create toolbar spinner
|
||||||
@ -251,11 +251,18 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
.skip(1)
|
.skip(1)
|
||||||
// Set again the adapter to recalculate the covers height
|
// Set again the adapter to recalculate the covers height
|
||||||
.subscribe { adapter = this@CatalogueFragment.adapter }
|
.subscribe { adapter = this@CatalogueFragment.adapter }
|
||||||
|
|
||||||
|
(layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
override fun getSpanSize(position: Int): Int {
|
||||||
|
return when (adapter.getItemViewType(position)) {
|
||||||
|
R.layout.item_catalogue_grid -> 1
|
||||||
|
else -> spanCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scrollListener = EndlessScrollListener(recycler.layoutManager as LinearLayoutManager, { requestNextPage() })
|
|
||||||
recycler.setHasFixedSize(true)
|
recycler.setHasFixedSize(true)
|
||||||
recycler.addOnScrollListener(scrollListener)
|
|
||||||
recycler.adapter = adapter
|
recycler.adapter = adapter
|
||||||
|
|
||||||
catalogue_view.addView(recycler, 1)
|
catalogue_view.addView(recycler, 1)
|
||||||
@ -376,29 +383,19 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
presenter.restartPager(newQuery)
|
presenter.restartPager(newQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests the next page (if available). Called from scroll listeners when they reach the end.
|
|
||||||
*/
|
|
||||||
private fun requestNextPage() {
|
|
||||||
if (presenter.hasNextPage()) {
|
|
||||||
showGridProgressBar()
|
|
||||||
presenter.requestNext()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the presenter when the network request is received.
|
* Called from the presenter when the network request is received.
|
||||||
*
|
*
|
||||||
* @param page the current page.
|
* @param page the current page.
|
||||||
* @param mangas the list of manga of the page.
|
* @param mangas the list of manga of the page.
|
||||||
*/
|
*/
|
||||||
fun onAddPage(page: Int, mangas: List<Manga>) {
|
fun onAddPage(page: Int, mangas: List<CatalogueItem>) {
|
||||||
hideProgressBar()
|
hideProgressBar()
|
||||||
if (page == 1) {
|
if (page == 1) {
|
||||||
adapter.clear()
|
adapter.clear()
|
||||||
scrollListener?.resetScroll()
|
resetProgressItem()
|
||||||
}
|
}
|
||||||
adapter.addItems(mangas)
|
adapter.onLoadMoreComplete(mangas)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -407,6 +404,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
* @param error the error received.
|
* @param error the error received.
|
||||||
*/
|
*/
|
||||||
fun onAddPageError(error: Throwable) {
|
fun onAddPageError(error: Throwable) {
|
||||||
|
adapter.onLoadMoreComplete(null)
|
||||||
hideProgressBar()
|
hideProgressBar()
|
||||||
|
|
||||||
val message = if (error is NoResultsException) "No results found" else (error.message ?: "")
|
val message = if (error is NoResultsException) "No results found" else (error.message ?: "")
|
||||||
@ -414,12 +412,42 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
snack?.dismiss()
|
snack?.dismiss()
|
||||||
snack = catalogue_view.snack(message, Snackbar.LENGTH_INDEFINITE) {
|
snack = catalogue_view.snack(message, Snackbar.LENGTH_INDEFINITE) {
|
||||||
setAction(R.string.action_retry) {
|
setAction(R.string.action_retry) {
|
||||||
showProgressBar()
|
// If not the first page, show bottom progress bar.
|
||||||
|
if (adapter.mainItemCount > 0) {
|
||||||
|
val item = progressItem ?: return@setAction
|
||||||
|
adapter.addScrollableFooterWithDelay(item, 0, true)
|
||||||
|
} else {
|
||||||
|
showProgressBar()
|
||||||
|
}
|
||||||
presenter.requestNext()
|
presenter.requestNext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new progress item and reenables the scroll listener.
|
||||||
|
*/
|
||||||
|
private fun resetProgressItem() {
|
||||||
|
progressItem = ProgressItem()
|
||||||
|
adapter.endlessTargetCount = 0
|
||||||
|
adapter.setEndlessScrollListener(this, progressItem!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the adapter when scrolled near the bottom.
|
||||||
|
*/
|
||||||
|
override fun onLoadMore(lastPosition: Int, currentPage: Int) {
|
||||||
|
if (presenter.hasNextPage()) {
|
||||||
|
presenter.requestNext()
|
||||||
|
} else {
|
||||||
|
adapter.onLoadMoreComplete(null)
|
||||||
|
adapter.endlessTargetCount = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noMoreLoad(newItemsSize: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the presenter when a manga is initialized.
|
* Called from the presenter when a manga is initialized.
|
||||||
*
|
*
|
||||||
@ -433,13 +461,18 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
* Swaps the current display mode.
|
* Swaps the current display mode.
|
||||||
*/
|
*/
|
||||||
fun swapDisplayMode() {
|
fun swapDisplayMode() {
|
||||||
|
if (!isAdded) return
|
||||||
|
|
||||||
presenter.swapDisplayMode()
|
presenter.swapDisplayMode()
|
||||||
val isListMode = presenter.isListMode
|
val isListMode = presenter.isListMode
|
||||||
activity.invalidateOptionsMenu()
|
activity.invalidateOptionsMenu()
|
||||||
setupRecycler()
|
setupRecycler()
|
||||||
if (!isListMode || !context.connectivityManager.isActiveNetworkMetered) {
|
if (!isListMode || !context.connectivityManager.isActiveNetworkMetered) {
|
||||||
// Initialize mangas if going to grid view or if over wifi when going to list view
|
// Initialize mangas if going to grid view or if over wifi when going to list view
|
||||||
presenter.initializeMangas(adapter.items)
|
val mangas = (0..adapter.itemCount-1).mapNotNull {
|
||||||
|
(adapter.getItem(it) as? CatalogueItem)?.manga
|
||||||
|
}
|
||||||
|
presenter.initializeMangas(mangas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,21 +507,11 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
snack = null
|
snack = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the progress bar at the end of the screen.
|
|
||||||
*/
|
|
||||||
private fun showGridProgressBar() {
|
|
||||||
progress_grid.visibility = ProgressBar.VISIBLE
|
|
||||||
snack?.dismiss()
|
|
||||||
snack = null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides active progress bars.
|
* Hides active progress bars.
|
||||||
*/
|
*/
|
||||||
private fun hideProgressBar() {
|
private fun hideProgressBar() {
|
||||||
progress.visibility = ProgressBar.GONE
|
progress.visibility = ProgressBar.GONE
|
||||||
progress_grid.visibility = ProgressBar.GONE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -497,10 +520,10 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
* @param position the position of the element clicked.
|
* @param position the position of the element clicked.
|
||||||
* @return true if the item should be selected, false otherwise.
|
* @return true if the item should be selected, false otherwise.
|
||||||
*/
|
*/
|
||||||
override fun onListItemClick(position: Int): Boolean {
|
override fun onItemClick(position: Int): Boolean {
|
||||||
val item = adapter.getItem(position) ?: return false
|
val item = adapter.getItem(position) as? CatalogueItem ?: return false
|
||||||
|
|
||||||
val intent = MangaActivity.newIntent(activity, item, true)
|
val intent = MangaActivity.newIntent(activity, item.manga, true)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -510,8 +533,8 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
|||||||
*
|
*
|
||||||
* @param position the position of the element clicked.
|
* @param position the position of the element clicked.
|
||||||
*/
|
*/
|
||||||
override fun onListItemLongClick(position: Int) {
|
override fun onItemLongClick(position: Int) {
|
||||||
val manga = adapter.getItem(position) ?: return
|
val manga = (adapter.getItem(position) as? CatalogueItem?)?.manga ?: return
|
||||||
|
|
||||||
val textRes = if (manga.favorite) R.string.remove_from_library else R.string.add_to_library
|
val textRes = if (manga.favorite) R.string.remove_from_library else R.string.add_to_library
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.catalogue
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
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.database.models.Manga
|
||||||
import kotlinx.android.synthetic.main.item_catalogue_grid.view.*
|
import kotlinx.android.synthetic.main.item_catalogue_grid.view.*
|
||||||
|
|
||||||
@ -12,11 +13,10 @@ import kotlinx.android.synthetic.main.item_catalogue_grid.view.*
|
|||||||
*
|
*
|
||||||
* @param view the inflated view for this holder.
|
* @param view the inflated view for this holder.
|
||||||
* @param adapter the adapter handling this holder.
|
* @param adapter the adapter handling this holder.
|
||||||
* @param listener a listener to react to single tap and long tap events.
|
|
||||||
* @constructor creates a new catalogue holder.
|
* @constructor creates a new catalogue holder.
|
||||||
*/
|
*/
|
||||||
class CatalogueGridHolder(private val view: View, private val adapter: CatalogueAdapter, listener: OnListItemClickListener) :
|
class CatalogueGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
|
||||||
CatalogueHolder(view, adapter, listener) {
|
CatalogueHolder(view, adapter) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
package eu.kanade.tachiyomi.ui.catalogue
|
package eu.kanade.tachiyomi.ui.catalogue
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic class used to hold the displayed data of a manga in the catalogue.
|
* Generic class used to hold the displayed data of a manga in the catalogue.
|
||||||
*
|
*
|
||||||
* @param view the inflated view for this holder.
|
* @param view the inflated view for this holder.
|
||||||
* @param adapter the adapter handling this holder.
|
* @param adapter the adapter handling this holder.
|
||||||
* @param listener a listener to react to single tap and long tap events.
|
|
||||||
*/
|
*/
|
||||||
abstract class CatalogueHolder(view: View, adapter: CatalogueAdapter, listener: OnListItemClickListener) :
|
abstract class CatalogueHolder(view: View, adapter: FlexibleAdapter<*>) :
|
||||||
FlexibleViewHolder(view, adapter, listener) {
|
FlexibleViewHolder(view, adapter) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.catalogue
|
||||||
|
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
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.util.inflate
|
||||||
|
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||||
|
import kotlinx.android.synthetic.main.item_catalogue_grid.view.*
|
||||||
|
|
||||||
|
class CatalogueItem(val manga: Manga) : AbstractFlexibleItem<CatalogueHolder>() {
|
||||||
|
|
||||||
|
override fun getLayoutRes(): Int {
|
||||||
|
return R.layout.item_catalogue_grid
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, inflater: LayoutInflater, parent: ViewGroup): CatalogueHolder {
|
||||||
|
if (parent is AutofitRecyclerView) {
|
||||||
|
val view = parent.inflate(R.layout.item_catalogue_grid).apply {
|
||||||
|
card.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.itemWidth / 3 * 4)
|
||||||
|
gradient.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.itemWidth / 3 * 4 / 2, Gravity.BOTTOM)
|
||||||
|
}
|
||||||
|
return CatalogueGridHolder(view, adapter)
|
||||||
|
} else {
|
||||||
|
val view = parent.inflate(R.layout.item_catalogue_list)
|
||||||
|
return CatalogueListHolder(view, adapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: CatalogueHolder, position: Int, payloads: List<Any?>?) {
|
||||||
|
holder.onSetValues(manga)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other is CatalogueItem) {
|
||||||
|
return manga.id!! == other.manga.id!!
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return manga.id!!.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.catalogue
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
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.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.util.getResourceColor
|
import eu.kanade.tachiyomi.util.getResourceColor
|
||||||
import kotlinx.android.synthetic.main.item_catalogue_list.view.*
|
import kotlinx.android.synthetic.main.item_catalogue_list.view.*
|
||||||
@ -13,11 +14,10 @@ import kotlinx.android.synthetic.main.item_catalogue_list.view.*
|
|||||||
*
|
*
|
||||||
* @param view the inflated view for this holder.
|
* @param view the inflated view for this holder.
|
||||||
* @param adapter the adapter handling this holder.
|
* @param adapter the adapter handling this holder.
|
||||||
* @param listener a listener to react to single tap and long tap events.
|
|
||||||
* @constructor creates a new catalogue holder.
|
* @constructor creates a new catalogue holder.
|
||||||
*/
|
*/
|
||||||
class CatalogueListHolder(private val view: View, adapter: CatalogueAdapter, listener: OnListItemClickListener) :
|
class CatalogueListHolder(private val view: View, adapter: FlexibleAdapter<*>) :
|
||||||
CatalogueHolder(view, adapter, listener) {
|
CatalogueHolder(view, adapter) {
|
||||||
|
|
||||||
private val favoriteColor = view.context.getResourceColor(android.R.attr.textColorHint)
|
private val favoriteColor = view.context.getResourceColor(android.R.attr.textColorHint)
|
||||||
private val unfavoriteColor = view.context.getResourceColor(android.R.attr.textColorPrimary)
|
private val unfavoriteColor = view.context.getResourceColor(android.R.attr.textColorPrimary)
|
||||||
|
@ -163,6 +163,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
|||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
.map { it.first to it.second.map { networkToLocalManga(it, sourceId) } }
|
.map { it.first to it.second.map { networkToLocalManga(it, sourceId) } }
|
||||||
.doOnNext { initializeMangas(it.second) }
|
.doOnNext { initializeMangas(it.second) }
|
||||||
|
.map { it.first to it.second.map(::CatalogueItem) }
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeReplay({ view, pair ->
|
.subscribeReplay({ view, pair ->
|
||||||
view.onAddPage(pair.first, pair.second)
|
view.onAddPage(pair.first, pair.second)
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.catalogue
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import android.widget.TextView
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressItem : AbstractFlexibleItem<ProgressItem.Holder>() {
|
||||||
|
|
||||||
|
var loadMore = true
|
||||||
|
|
||||||
|
override fun getLayoutRes(): Int {
|
||||||
|
return R.layout.progress_item
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, inflater: LayoutInflater, parent: ViewGroup): Holder {
|
||||||
|
return Holder(inflater.inflate(layoutRes, parent, false), adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: Holder, position: Int, payloads: List<Any?>) {
|
||||||
|
holder.progressBar.visibility = View.GONE
|
||||||
|
holder.progressMessage.visibility = View.GONE
|
||||||
|
|
||||||
|
if (!adapter.isEndlessScrollEnabled) {
|
||||||
|
loadMore = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadMore) {
|
||||||
|
holder.progressBar.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
holder.progressMessage.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return this === other
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) {
|
||||||
|
|
||||||
|
val progressBar = view.findViewById(R.id.progress_bar) as ProgressBar
|
||||||
|
val progressMessage = view.findViewById(R.id.progress_message) as TextView
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.widget
|
|
||||||
|
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
|
||||||
import android.support.v7.widget.RecyclerView
|
|
||||||
|
|
||||||
class EndlessScrollListener(
|
|
||||||
private val layoutManager: LinearLayoutManager,
|
|
||||||
private val requestNext: () -> Unit)
|
|
||||||
: RecyclerView.OnScrollListener() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
// The minimum amount of items to have below your current scroll position before loading
|
|
||||||
// more.
|
|
||||||
private val VISIBLE_THRESHOLD = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
private var previousTotal = 0 // The total number of items in the dataset after the last load
|
|
||||||
private var loading = true // True if we are still waiting for the last set of data to load.
|
|
||||||
private var firstVisibleItem = 0
|
|
||||||
private var visibleItemCount = 0
|
|
||||||
private var totalItemCount = 0
|
|
||||||
|
|
||||||
fun resetScroll() {
|
|
||||||
previousTotal = 0
|
|
||||||
loading = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
|
||||||
super.onScrolled(recyclerView, dx, dy)
|
|
||||||
|
|
||||||
visibleItemCount = recyclerView.childCount
|
|
||||||
totalItemCount = layoutManager.itemCount
|
|
||||||
firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
|
|
||||||
|
|
||||||
if (loading && totalItemCount > previousTotal) {
|
|
||||||
loading = false
|
|
||||||
previousTotal = totalItemCount
|
|
||||||
}
|
|
||||||
if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem + VISIBLE_THRESHOLD) {
|
|
||||||
// End has been reached
|
|
||||||
requestNext()
|
|
||||||
loading = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -21,14 +21,6 @@
|
|||||||
android:layout_gravity="center_vertical|center_horizontal"
|
android:layout_gravity="center_vertical|center_horizontal"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progress_grid"
|
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical|center_horizontal"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
25
app/src/main/res/layout/progress_item.xml
Normal file
25
app/src/main/res/layout/progress_item.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/progress_message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/no_more_results"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
@ -223,6 +223,7 @@
|
|||||||
<string name="source_requires_login">This source requires you to log in</string>
|
<string name="source_requires_login">This source requires you to log in</string>
|
||||||
<string name="select_source">Select a source</string>
|
<string name="select_source">Select a source</string>
|
||||||
<string name="no_valid_sources">Please enable at least one valid source</string>
|
<string name="no_valid_sources">Please enable at least one valid source</string>
|
||||||
|
<string name="no_more_results">No more results</string>
|
||||||
|
|
||||||
<!-- Manga activity -->
|
<!-- Manga activity -->
|
||||||
<string name="manga_not_in_db">This manga was removed from the database!</string>
|
<string name="manga_not_in_db">This manga was removed from the database!</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user