Rewrite catalogue adapter

This commit is contained in:
len 2017-01-17 20:13:29 +01:00
parent f86c3c81bf
commit 871e17c2f5
12 changed files with 207 additions and 213 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

@ -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>

View 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>

View File

@ -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>