mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-23 13:23:28 +03:00
Load more mangas on scroll
This commit is contained in:
parent
2aaaad7a24
commit
e7116bdcab
8 changed files with 155 additions and 16 deletions
|
@ -102,6 +102,11 @@ public class DatabaseHelper implements MangaManager, ChapterManager {
|
||||||
return mMangaManager.getManga(id);
|
return mMangaManager.getManga(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Manga getMangaBlock(String url) {
|
||||||
|
return mMangaManager.getMangaBlock(url);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Observable<PutResult> insertManga(Manga manga) {
|
public Observable<PutResult> insertManga(Manga manga) {
|
||||||
return mMangaManager.insertManga(manga);
|
return mMangaManager.insertManga(manga);
|
||||||
|
@ -112,6 +117,11 @@ public class DatabaseHelper implements MangaManager, ChapterManager {
|
||||||
return mMangaManager.insertMangas(mangas);
|
return mMangaManager.insertMangas(mangas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PutResult insertMangaBlock(Manga manga) {
|
||||||
|
return mMangaManager.insertMangaBlock(manga);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Observable<DeleteResult> deleteManga(Manga manga) {
|
public Observable<DeleteResult> deleteManga(Manga manga) {
|
||||||
return mMangaManager.deleteManga(manga);
|
return mMangaManager.deleteManga(manga);
|
||||||
|
|
|
@ -20,10 +20,14 @@ public interface MangaManager {
|
||||||
|
|
||||||
Observable<List<Manga>> getManga(int id);
|
Observable<List<Manga>> getManga(int id);
|
||||||
|
|
||||||
|
Manga getMangaBlock(String url);
|
||||||
|
|
||||||
Observable<PutResult> insertManga(Manga manga);
|
Observable<PutResult> insertManga(Manga manga);
|
||||||
|
|
||||||
Observable<PutResults<Manga>> insertMangas(List<Manga> mangas);
|
Observable<PutResults<Manga>> insertMangas(List<Manga> mangas);
|
||||||
|
|
||||||
|
PutResult insertMangaBlock(Manga manga);
|
||||||
|
|
||||||
Observable<DeleteResult> deleteManga(Manga manga);
|
Observable<DeleteResult> deleteManga(Manga manga);
|
||||||
|
|
||||||
Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas);
|
Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas);
|
||||||
|
|
|
@ -70,6 +70,24 @@ public class MangaManagerImpl extends BaseManager implements MangaManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Manga getMangaBlock(String url) {
|
||||||
|
List<Manga> result = db.get()
|
||||||
|
.listOfObjects(Manga.class)
|
||||||
|
.withQuery(Query.builder()
|
||||||
|
.table(MangasTable.TABLE)
|
||||||
|
.where(MangasTable.COLUMN_URL + "=?")
|
||||||
|
.whereArgs(url)
|
||||||
|
.build())
|
||||||
|
.prepare()
|
||||||
|
.executeAsBlocking();
|
||||||
|
|
||||||
|
if (result.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return result.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
public Observable<PutResult> insertManga(Manga manga) {
|
public Observable<PutResult> insertManga(Manga manga) {
|
||||||
return db.put()
|
return db.put()
|
||||||
.object(manga)
|
.object(manga)
|
||||||
|
@ -84,6 +102,13 @@ public class MangaManagerImpl extends BaseManager implements MangaManager {
|
||||||
.createObservable();
|
.createObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PutResult insertMangaBlock(Manga manga) {
|
||||||
|
return db.put()
|
||||||
|
.object(manga)
|
||||||
|
.prepare()
|
||||||
|
.executeAsBlocking();
|
||||||
|
}
|
||||||
|
|
||||||
public Observable<DeleteResult> deleteManga(Manga manga) {
|
public Observable<DeleteResult> deleteManga(Manga manga) {
|
||||||
return db.delete()
|
return db.delete()
|
||||||
.object(manga)
|
.object(manga)
|
||||||
|
|
|
@ -83,6 +83,8 @@ public class MangasTable {
|
||||||
+ COLUMN_INITIALIZED + " BOOLEAN NOT NULL, "
|
+ COLUMN_INITIALIZED + " BOOLEAN NOT NULL, "
|
||||||
+ COLUMN_VIEWER + " INTEGER NOT NULL, "
|
+ COLUMN_VIEWER + " INTEGER NOT NULL, "
|
||||||
+ COLUMN_CHAPTER_ORDER + " INTEGER NOT NULL"
|
+ COLUMN_CHAPTER_ORDER + " INTEGER NOT NULL"
|
||||||
+ ");";
|
+ ");"
|
||||||
|
+ "CREATE INDEX " + TABLE + "_" + COLUMN_URL + "_index ON " + TABLE + "(" + COLUMN_URL + ");";
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,12 @@ import eu.kanade.mangafeed.sources.Source;
|
||||||
import eu.kanade.mangafeed.ui.adapter.CatalogueListHolder;
|
import eu.kanade.mangafeed.ui.adapter.CatalogueListHolder;
|
||||||
import eu.kanade.mangafeed.view.CatalogueListView;
|
import eu.kanade.mangafeed.view.CatalogueListView;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
|
import rx.Subscription;
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
import rx.schedulers.Schedulers;
|
import rx.schedulers.Schedulers;
|
||||||
import uk.co.ribot.easyadapter.EasyAdapter;
|
import uk.co.ribot.easyadapter.EasyAdapter;
|
||||||
|
|
||||||
public class CatalogueListPresenter {
|
public class CatalogueListPresenter extends BasePresenter {
|
||||||
|
|
||||||
CatalogueListView view;
|
CatalogueListView view;
|
||||||
EasyAdapter<Manga> adapter;
|
EasyAdapter<Manga> adapter;
|
||||||
|
@ -25,6 +26,8 @@ public class CatalogueListPresenter {
|
||||||
@Inject SourceManager sourceManager;
|
@Inject SourceManager sourceManager;
|
||||||
@Inject DatabaseHelper db;
|
@Inject DatabaseHelper db;
|
||||||
|
|
||||||
|
private Subscription mMangaFetchSubscription;
|
||||||
|
|
||||||
|
|
||||||
public CatalogueListPresenter(CatalogueListView view) {
|
public CatalogueListPresenter(CatalogueListView view) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
|
@ -38,24 +41,32 @@ public class CatalogueListPresenter {
|
||||||
|
|
||||||
adapter = new EasyAdapter<>(view.getActivity(), CatalogueListHolder.class);
|
adapter = new EasyAdapter<>(view.getActivity(), CatalogueListHolder.class);
|
||||||
view.setAdapter(adapter);
|
view.setAdapter(adapter);
|
||||||
|
view.setScrollListener();
|
||||||
|
|
||||||
getMangasFromSource();
|
getMangasFromSource(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getMangasFromSource() {
|
public void getMangasFromSource(int page) {
|
||||||
selectedSource.pullPopularMangasFromNetwork(1)
|
subscriptions.remove(mMangaFetchSubscription);
|
||||||
|
|
||||||
|
mMangaFetchSubscription = selectedSource.pullPopularMangasFromNetwork(page)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.flatMap(Observable::from)
|
|
||||||
.flatMap(networkManga -> db.getManga(networkManga.url)
|
|
||||||
.flatMap(result -> {
|
|
||||||
if (result.size() == 0) {
|
|
||||||
return db.insertManga(networkManga)
|
|
||||||
.flatMap(i -> Observable.just(networkManga));
|
|
||||||
}
|
|
||||||
return Observable.just(networkManga);
|
|
||||||
}))
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(adapter::addItem);
|
.flatMap(Observable::from)
|
||||||
|
.map(this::networkToLocalManga)
|
||||||
|
.toList()
|
||||||
|
.subscribe(adapter::addItems);
|
||||||
|
|
||||||
|
subscriptions.add(mMangaFetchSubscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Manga networkToLocalManga(Manga networkManga) {
|
||||||
|
Manga localManga = db.getMangaBlock(networkManga.url);
|
||||||
|
if (localManga == null) {
|
||||||
|
db.insertMangaBlock(networkManga);
|
||||||
|
localManga = db.getMangaBlock(networkManga.url);
|
||||||
|
}
|
||||||
|
return localManga;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import eu.kanade.mangafeed.R;
|
||||||
import eu.kanade.mangafeed.presenter.CatalogueListPresenter;
|
import eu.kanade.mangafeed.presenter.CatalogueListPresenter;
|
||||||
import eu.kanade.mangafeed.sources.Source;
|
import eu.kanade.mangafeed.sources.Source;
|
||||||
import eu.kanade.mangafeed.view.CatalogueListView;
|
import eu.kanade.mangafeed.view.CatalogueListView;
|
||||||
|
import eu.kanade.mangafeed.widget.EndlessScrollListener;
|
||||||
import uk.co.ribot.easyadapter.EasyAdapter;
|
import uk.co.ribot.easyadapter.EasyAdapter;
|
||||||
|
|
||||||
public class CatalogueListActivity extends BaseActivity implements CatalogueListView {
|
public class CatalogueListActivity extends BaseActivity implements CatalogueListView {
|
||||||
|
@ -24,7 +25,7 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
|
||||||
private Source source;
|
private Source source;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_catalogue_list);
|
setContentView(R.layout.activity_catalogue_list);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
|
@ -35,6 +36,12 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
|
||||||
presenter.initializeSource();
|
presenter.initializeSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
presenter.destroySubscriptions();
|
||||||
|
}
|
||||||
|
|
||||||
public void setSource(Source source) {
|
public void setSource(Source source) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
setToolbarTitle(source.getName());
|
setToolbarTitle(source.getName());
|
||||||
|
@ -44,4 +51,14 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
|
||||||
manga_list.setAdapter(adapter);
|
manga_list.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setScrollListener() {
|
||||||
|
manga_list.setOnScrollListener(new EndlessScrollListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLoadMore(int page, int totalItemsCount) {
|
||||||
|
presenter.getMangasFromSource(page);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,5 @@ public interface CatalogueListView extends BaseView {
|
||||||
Intent getIntent();
|
Intent getIntent();
|
||||||
void setSource(Source source);
|
void setSource(Source source);
|
||||||
void setAdapter(EasyAdapter adapter);
|
void setAdapter(EasyAdapter adapter);
|
||||||
|
void setScrollListener();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package eu.kanade.mangafeed.widget;
|
||||||
|
|
||||||
|
import android.widget.AbsListView;
|
||||||
|
|
||||||
|
public abstract class EndlessScrollListener implements AbsListView.OnScrollListener {
|
||||||
|
// The minimum amount of items to have below your current scroll position
|
||||||
|
// before loading more.
|
||||||
|
private int visibleThreshold = 5;
|
||||||
|
// The current offset index of data you have loaded
|
||||||
|
private int currentPage = 0;
|
||||||
|
// The total number of items in the dataset after the last load
|
||||||
|
private int previousTotalItemCount = 0;
|
||||||
|
// True if we are still waiting for the last set of data to load.
|
||||||
|
private boolean loading = true;
|
||||||
|
// Sets the starting page index
|
||||||
|
private int startingPageIndex = 0;
|
||||||
|
|
||||||
|
public EndlessScrollListener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public EndlessScrollListener(int visibleThreshold) {
|
||||||
|
this.visibleThreshold = visibleThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EndlessScrollListener(int visibleThreshold, int startPage) {
|
||||||
|
this.visibleThreshold = visibleThreshold;
|
||||||
|
this.startingPageIndex = startPage;
|
||||||
|
this.currentPage = startPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This happens many times a second during a scroll, so be wary of the code you place here.
|
||||||
|
// We are given a few useful parameters to help us work out if we need to load some more data,
|
||||||
|
// but first we check if we are waiting for the previous load to finish.
|
||||||
|
@Override
|
||||||
|
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
|
||||||
|
{
|
||||||
|
// If the total item count is zero and the previous isn't, assume the
|
||||||
|
// list is invalidated and should be reset back to initial state
|
||||||
|
if (totalItemCount < previousTotalItemCount) {
|
||||||
|
this.currentPage = this.startingPageIndex;
|
||||||
|
this.previousTotalItemCount = totalItemCount;
|
||||||
|
if (totalItemCount == 0) { this.loading = true; }
|
||||||
|
}
|
||||||
|
// If it’s still loading, we check to see if the dataset count has
|
||||||
|
// changed, if so we conclude it has finished loading and update the current page
|
||||||
|
// number and total item count.
|
||||||
|
if (loading && (totalItemCount > previousTotalItemCount)) {
|
||||||
|
loading = false;
|
||||||
|
previousTotalItemCount = totalItemCount;
|
||||||
|
currentPage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it isn’t currently loading, we check to see if we have breached
|
||||||
|
// the visibleThreshold and need to reload more data.
|
||||||
|
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
|
||||||
|
if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) {
|
||||||
|
loading = onLoadMore(currentPage + 1, totalItemCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines the process for actually loading more data based on page
|
||||||
|
// Returns true if more data is being loaded; returns false if there is no more data to load.
|
||||||
|
public abstract boolean onLoadMore(int page, int totalItemsCount);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||||
|
// Don't take any action on changed
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue