浏览代码

Load more mangas on scroll

inorichi 9 年之前
父节点
当前提交
e7116bdcab

+ 10 - 0
app/src/main/java/eu/kanade/mangafeed/data/helpers/DatabaseHelper.java

@@ -102,6 +102,11 @@ public class DatabaseHelper implements MangaManager, ChapterManager {
         return mMangaManager.getManga(id);
     }
 
+    @Override
+    public Manga getMangaBlock(String url) {
+        return mMangaManager.getMangaBlock(url);
+    }
+
     @Override
     public Observable<PutResult> insertManga(Manga manga) {
         return mMangaManager.insertManga(manga);
@@ -112,6 +117,11 @@ public class DatabaseHelper implements MangaManager, ChapterManager {
         return mMangaManager.insertMangas(mangas);
     }
 
+    @Override
+    public PutResult insertMangaBlock(Manga manga) {
+        return mMangaManager.insertMangaBlock(manga);
+    }
+
     @Override
     public Observable<DeleteResult> deleteManga(Manga manga) {
         return mMangaManager.deleteManga(manga);

+ 4 - 0
app/src/main/java/eu/kanade/mangafeed/data/managers/MangaManager.java

@@ -20,10 +20,14 @@ public interface MangaManager {
 
     Observable<List<Manga>> getManga(int id);
 
+    Manga getMangaBlock(String url);
+
     Observable<PutResult> insertManga(Manga manga);
 
     Observable<PutResults<Manga>> insertMangas(List<Manga> mangas);
 
+    PutResult insertMangaBlock(Manga manga);
+
     Observable<DeleteResult> deleteManga(Manga manga);
 
     Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas);

+ 25 - 0
app/src/main/java/eu/kanade/mangafeed/data/managers/MangaManagerImpl.java

@@ -70,6 +70,24 @@ public class MangaManagerImpl extends BaseManager implements MangaManager {
         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) {
         return db.put()
                 .object(manga)
@@ -84,6 +102,13 @@ public class MangaManagerImpl extends BaseManager implements MangaManager {
                 .createObservable();
     }
 
+    public PutResult insertMangaBlock(Manga manga) {
+        return db.put()
+                .object(manga)
+                .prepare()
+                .executeAsBlocking();
+    }
+
     public Observable<DeleteResult> deleteManga(Manga manga) {
         return db.delete()
                 .object(manga)

+ 3 - 1
app/src/main/java/eu/kanade/mangafeed/data/tables/MangasTable.java

@@ -83,6 +83,8 @@ public class MangasTable {
                 + COLUMN_INITIALIZED + " BOOLEAN NOT NULL, "
                 + COLUMN_VIEWER + " INTEGER NOT NULL, "
                 + COLUMN_CHAPTER_ORDER + " INTEGER NOT NULL"
-                + ");";
+                + ");"
+                + "CREATE INDEX " + TABLE + "_" + COLUMN_URL + "_index ON " + TABLE + "(" + COLUMN_URL + ");";
+
     }
 }

+ 25 - 14
app/src/main/java/eu/kanade/mangafeed/presenter/CatalogueListPresenter.java

@@ -12,11 +12,12 @@ import eu.kanade.mangafeed.sources.Source;
 import eu.kanade.mangafeed.ui.adapter.CatalogueListHolder;
 import eu.kanade.mangafeed.view.CatalogueListView;
 import rx.Observable;
+import rx.Subscription;
 import rx.android.schedulers.AndroidSchedulers;
 import rx.schedulers.Schedulers;
 import uk.co.ribot.easyadapter.EasyAdapter;
 
-public class CatalogueListPresenter {
+public class CatalogueListPresenter extends BasePresenter {
 
     CatalogueListView view;
     EasyAdapter<Manga> adapter;
@@ -25,6 +26,8 @@ public class CatalogueListPresenter {
     @Inject SourceManager sourceManager;
     @Inject DatabaseHelper db;
 
+    private Subscription mMangaFetchSubscription;
+
 
     public CatalogueListPresenter(CatalogueListView view) {
         this.view = view;
@@ -38,24 +41,32 @@ public class CatalogueListPresenter {
 
         adapter = new EasyAdapter<>(view.getActivity(), CatalogueListHolder.class);
         view.setAdapter(adapter);
+        view.setScrollListener();
 
-        getMangasFromSource();
+        getMangasFromSource(1);
     }
 
-    private void getMangasFromSource() {
-        selectedSource.pullPopularMangasFromNetwork(1)
+    public void getMangasFromSource(int page) {
+        subscriptions.remove(mMangaFetchSubscription);
+
+        mMangaFetchSubscription = selectedSource.pullPopularMangasFromNetwork(page)
                 .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())
-                .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;
     }
 
 }

+ 18 - 1
app/src/main/java/eu/kanade/mangafeed/ui/activity/CatalogueListActivity.java

@@ -10,6 +10,7 @@ import eu.kanade.mangafeed.R;
 import eu.kanade.mangafeed.presenter.CatalogueListPresenter;
 import eu.kanade.mangafeed.sources.Source;
 import eu.kanade.mangafeed.view.CatalogueListView;
+import eu.kanade.mangafeed.widget.EndlessScrollListener;
 import uk.co.ribot.easyadapter.EasyAdapter;
 
 public class CatalogueListActivity extends BaseActivity implements CatalogueListView {
@@ -24,7 +25,7 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
     private Source source;
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_catalogue_list);
         ButterKnife.bind(this);
@@ -35,6 +36,12 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
         presenter.initializeSource();
     }
 
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        presenter.destroySubscriptions();
+    }
+
     public void setSource(Source source) {
         this.source = source;
         setToolbarTitle(source.getName());
@@ -44,4 +51,14 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
         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;
+            }
+        });
+    }
+
 }

+ 1 - 0
app/src/main/java/eu/kanade/mangafeed/view/CatalogueListView.java

@@ -9,4 +9,5 @@ public interface CatalogueListView extends BaseView {
     Intent getIntent();
     void setSource(Source source);
     void setAdapter(EasyAdapter adapter);
+    void setScrollListener();
 }

+ 69 - 0
app/src/main/java/eu/kanade/mangafeed/widget/EndlessScrollListener.java

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