From 9e2b72a089a4a90fce0d95d7db52b881c168b231 Mon Sep 17 00:00:00 2001 From: Tuan Chau Date: Fri, 14 Aug 2015 11:05:52 +0700 Subject: [PATCH 01/10] add `get` for `hasNextPage` --- ParseLoginUI/src/com/parse/ParseQueryAdapter.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ParseLoginUI/src/com/parse/ParseQueryAdapter.java b/ParseLoginUI/src/com/parse/ParseQueryAdapter.java index 82f57ab..f71a189 100644 --- a/ParseLoginUI/src/com/parse/ParseQueryAdapter.java +++ b/ParseLoginUI/src/com/parse/ParseQueryAdapter.java @@ -693,6 +693,13 @@ private int getPaginationCellRow() { private boolean shouldShowPaginationCell() { return this.paginationEnabled && this.objects.size() > 0 && this.hasNextPage; } + + /** + * @return hasNextPage + */ + public boolean isHasNextPage(){ + return this.hasNextPage; + } private void notifyOnLoadingListeners() { for (OnQueryLoadListener listener : this.onQueryLoadListeners) { @@ -705,4 +712,4 @@ private void notifyOnLoadedListeners(List objects, Exception e) { listener.onLoaded(objects, e); } } -} \ No newline at end of file +} From 2042424a9e995be68117d7b9cfa1ec7fb9073c92 Mon Sep 17 00:00:00 2001 From: Tuan Chau Date: Mon, 26 Oct 2015 07:09:48 +0700 Subject: [PATCH 02/10] Add ParseListLoader --- .../src/com/parse/ParseListLoader.java | 194 ++++++++++++++++++ .../src/com/parse/ParseQueryAdapter.java | 7 - build.gradle | 2 +- parselistloaderexample/.gitignore | 1 + parselistloaderexample/README.md | 5 + parselistloaderexample/build.gradle | 30 +++ parselistloaderexample/proguard-rules.pro | 17 ++ .../ApplicationTest.java | 13 ++ .../src/main/AndroidManifest.xml | 40 ++++ .../ListLoaderApplication.java | 21 ++ .../parselistloaderexample/MainActivity.java | 62 ++++++ .../ParseDemoObject.java | 39 ++++ .../listview/ListViewAdapter.java | 79 +++++++ .../listview/ListViewDemoActivity.java | 84 ++++++++ .../recycler/RecyclerAdapter.java | 94 +++++++++ .../recycler/RecyclerViewDemoActivity.java | 89 ++++++++ .../viewpager/ViewPagerAdapter.java | 99 +++++++++ .../viewpager/ViewPagerDemoActivity.java | 84 ++++++++ .../src/main/res/drawable/ic_launcher.png | Bin 0 -> 7718 bytes .../main/res/layout/activity_list_view.xml | 11 + .../src/main/res/layout/activity_main.xml | 40 ++++ .../res/layout/activity_recycler_view.xml | 12 ++ .../main/res/layout/activity_view_pager.xml | 11 + .../src/main/res/layout/item_layout.xml | 21 ++ .../src/main/res/layout/item_layout_2.xml | 19 ++ .../src/main/res/layout/item_waiting.xml | 15 ++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10486 bytes .../src/main/res/values/strings.xml | 5 + settings.gradle | 2 +- 33 files changed, 1087 insertions(+), 9 deletions(-) create mode 100644 ParseLoginUI/src/com/parse/ParseListLoader.java create mode 100644 parselistloaderexample/.gitignore create mode 100644 parselistloaderexample/README.md create mode 100644 parselistloaderexample/build.gradle create mode 100644 parselistloaderexample/proguard-rules.pro create mode 100644 parselistloaderexample/src/androidTest/java/com/example/tuanchauict/parselistloaderexample/ApplicationTest.java create mode 100644 parselistloaderexample/src/main/AndroidManifest.xml create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ListLoaderApplication.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/MainActivity.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ParseDemoObject.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewAdapter.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewDemoActivity.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerAdapter.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerViewDemoActivity.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerAdapter.java create mode 100644 parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerDemoActivity.java create mode 100644 parselistloaderexample/src/main/res/drawable/ic_launcher.png create mode 100644 parselistloaderexample/src/main/res/layout/activity_list_view.xml create mode 100644 parselistloaderexample/src/main/res/layout/activity_main.xml create mode 100644 parselistloaderexample/src/main/res/layout/activity_recycler_view.xml create mode 100644 parselistloaderexample/src/main/res/layout/activity_view_pager.xml create mode 100644 parselistloaderexample/src/main/res/layout/item_layout.xml create mode 100644 parselistloaderexample/src/main/res/layout/item_layout_2.xml create mode 100644 parselistloaderexample/src/main/res/layout/item_waiting.xml create mode 100644 parselistloaderexample/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 parselistloaderexample/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 parselistloaderexample/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 parselistloaderexample/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 parselistloaderexample/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 parselistloaderexample/src/main/res/values/strings.xml diff --git a/ParseLoginUI/src/com/parse/ParseListLoader.java b/ParseLoginUI/src/com/parse/ParseListLoader.java new file mode 100644 index 0000000..d95daa6 --- /dev/null +++ b/ParseLoginUI/src/com/parse/ParseListLoader.java @@ -0,0 +1,194 @@ +package com.parse; + +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +import bolts.Capture; + +public class ParseListLoader { + private List> mObjectPages; + private int mCurrentPage; + private boolean mHasNextPage; + private LoaderTarget mTarget; + private QueryFactory mQueryFactory; + private List> mOnQueryLoadListeners; + private int mObjectsPerPage; + private List mRunningQueries; + + + public ParseListLoader(@NonNull LoaderTarget target, @NonNull QueryFactory factory) { + mTarget = target; + mQueryFactory = factory; + mObjectPages = new ArrayList<>(); + mCurrentPage = -1; // init -1 so that we don't care about call loadObjects or loadNext first + mHasNextPage = true; + mOnQueryLoadListeners = new ArrayList<>(); + mObjectsPerPage = 25; + mRunningQueries = new ArrayList<>(); + } + + public void setObjectsPerPage(int objectsPerPage) { + mObjectsPerPage = objectsPerPage; + } + + public int getObjectsPerPage() { + return mObjectsPerPage; + } + + public void addOnQueryLoadListener(OnQueryLoadListener onQueryLoadListener) { + mOnQueryLoadListeners.add(onQueryLoadListener); + } + + public void clearOnQueryLoadListeners() { + mOnQueryLoadListeners.clear(); + } + + public boolean hasOnQueryLoadListeners() { + return !mOnQueryLoadListeners.isEmpty(); + } + + /** + * support this function because sometime, we have to force the list reload + * + * @param hasNextPage + */ + public void setHasNextPage(boolean hasNextPage) { + mHasNextPage = hasNextPage; + } + + public boolean hasNextPage() { + return mHasNextPage; + } + + public void loadObjects() { + this.loadObjects(0, true); + } + + public void loadNextPage() { + loadObjects(mCurrentPage + 1, false); + } + + private void loadObjects(final int page, final boolean shouldClear) { + if (!mHasNextPage) { + return; + } + if (mQueryFactory == null) { + return; + } + + final ParseQuery query = mQueryFactory.create(); + if (query == null) { + return; + } + + mRunningQueries.add(query); + + setPageOnQuery(page, query); + notifyOnLoadingListeners(); + if (page >= mObjectPages.size()) { + for (int i = mObjectPages.size(); i <= page; i++) { + mObjectPages.add(new ArrayList()); + } + } + + final int objectsPerPage = mObjectsPerPage; + + final Capture firstCallback = new Capture<>(Boolean.TRUE); + query.findInBackground(new FindCallback() { + @Override + public void done(List foundObjects, ParseException e) { + if (Parse.isLocalDatastoreEnabled() + || query.getCachePolicy() != ParseQuery.CachePolicy.CACHE_ONLY + || e == null + || e.getCode() != ParseException.CACHE_MISS) { + if (e != null && (e.getCode() == ParseException.CONNECTION_FAILED + || e.getCode() != ParseException.CACHE_MISS)) { +// mHasNextPage = false; + } else if (foundObjects != null) { + if (shouldClear && firstCallback.get()) { + mObjectPages.clear(); + mObjectPages.add(new ArrayList()); + mCurrentPage = page; + firstCallback.set(Boolean.FALSE); + } + + if (page >= mCurrentPage) { + mCurrentPage = page; + mHasNextPage = foundObjects.size() > objectsPerPage; + } + + if (mHasNextPage) { + foundObjects.remove(objectsPerPage); + } + + List currentPage = mObjectPages.get(page); + currentPage.clear(); + currentPage.addAll(foundObjects); + syncTargetWithPages(); + mTarget.notifyDataChanged(); + } + } + notifyOnLoadedListeners(foundObjects, mHasNextPage, e); + mRunningQueries.remove(query); + } + }); + + } + + + private void notifyOnLoadingListeners() { + for (OnQueryLoadListener listener : mOnQueryLoadListeners) { + listener.onLoading(); + } + } + + private void notifyOnLoadedListeners(List list, boolean hasNextPage, ParseException e) { + for (OnQueryLoadListener listener : mOnQueryLoadListeners) { + listener.onLoaded(list, hasNextPage, e); + } + } + + private void syncTargetWithPages() { + mTarget.clearList(); + for (List list : mObjectPages) { + mTarget.appendList(list); + } + } + + private void setPageOnQuery(int page, ParseQuery query) { + query.setLimit(mObjectsPerPage + 1); + query.setSkip(page * mObjectsPerPage); + } + + public void cancelAllRunningQueries() { + for (ParseQuery query : mRunningQueries) { + query.cancel(); + } + + mRunningQueries.clear(); + } + + public boolean isLoading(){ + return !mRunningQueries.isEmpty(); + } + + public interface LoaderTarget { + void appendList(List sublist); + + void clearList(); + + void notifyDataChanged(); + } + + public interface QueryFactory { + ParseQuery create(); + } + + public interface OnQueryLoadListener { + void onLoading(); + + void onLoaded(List list, boolean hasNextPage, ParseException e); + } +} diff --git a/ParseLoginUI/src/com/parse/ParseQueryAdapter.java b/ParseLoginUI/src/com/parse/ParseQueryAdapter.java index f71a189..2ef5719 100644 --- a/ParseLoginUI/src/com/parse/ParseQueryAdapter.java +++ b/ParseLoginUI/src/com/parse/ParseQueryAdapter.java @@ -693,13 +693,6 @@ private int getPaginationCellRow() { private boolean shouldShowPaginationCell() { return this.paginationEnabled && this.objects.size() > 0 && this.hasNextPage; } - - /** - * @return hasNextPage - */ - public boolean isHasNextPage(){ - return this.hasNextPage; - } private void notifyOnLoadingListeners() { for (OnQueryLoadListener listener : this.onQueryLoadListeners) { diff --git a/build.gradle b/build.gradle index 3ef1a06..98fd9f7 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:1.3.0' } } diff --git a/parselistloaderexample/.gitignore b/parselistloaderexample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/parselistloaderexample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/parselistloaderexample/README.md b/parselistloaderexample/README.md new file mode 100644 index 0000000..032ad23 --- /dev/null +++ b/parselistloaderexample/README.md @@ -0,0 +1,5 @@ +Create an App on [Parse.com](https://parse.com) then modify strings.xml with your keys + + YOUR_PARSE_APP_ID + YOUR_PARSE_CLIENT_KEY + diff --git a/parselistloaderexample/build.gradle b/parselistloaderexample/build.gradle new file mode 100644 index 0000000..d09baa2 --- /dev/null +++ b/parselistloaderexample/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + applicationId "com.example.tuanchauict.parselistloaderexample" + minSdkVersion 15 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile 'com.android.support:appcompat-v7:23.1.0' + compile 'com.android.support:recyclerview-v7:23.1.0' + compile 'com.android.support:support-v4:23.1.0' + compile project(':ParseLoginUI') + compile rootProject.ext.androidSupport + compile files(rootProject.ext.parsePath) +} diff --git a/parselistloaderexample/proguard-rules.pro b/parselistloaderexample/proguard-rules.pro new file mode 100644 index 0000000..f15b465 --- /dev/null +++ b/parselistloaderexample/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/tuanchauict/Data/Software/Dev/android-sdk-macosx/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/parselistloaderexample/src/androidTest/java/com/example/tuanchauict/parselistloaderexample/ApplicationTest.java b/parselistloaderexample/src/androidTest/java/com/example/tuanchauict/parselistloaderexample/ApplicationTest.java new file mode 100644 index 0000000..9890e4c --- /dev/null +++ b/parselistloaderexample/src/androidTest/java/com/example/tuanchauict/parselistloaderexample/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.example.tuanchauict.parselistloaderexample; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/parselistloaderexample/src/main/AndroidManifest.xml b/parselistloaderexample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..16ca6dc --- /dev/null +++ b/parselistloaderexample/src/main/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ListLoaderApplication.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ListLoaderApplication.java new file mode 100644 index 0000000..332ccf3 --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ListLoaderApplication.java @@ -0,0 +1,21 @@ +package com.example.tuanchauict.parselistloaderexample; + +import android.app.Application; + +import com.parse.Parse; +import com.parse.ParseObject; + +/** + * Created by tuanchauict on 10/25/15. + */ +public class ListLoaderApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + + // Required - Initialize the Parse SDK + Parse.initialize(this); + ParseObject.registerSubclass(ParseDemoObject.class); + + } +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/MainActivity.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/MainActivity.java new file mode 100644 index 0000000..c88eae3 --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/MainActivity.java @@ -0,0 +1,62 @@ +package com.example.tuanchauict.parselistloaderexample; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Toast; + +import com.example.tuanchauict.parselistloaderexample.listview.ListViewDemoActivity; +import com.example.tuanchauict.parselistloaderexample.recycler.RecyclerViewDemoActivity; +import com.example.tuanchauict.parselistloaderexample.viewpager.ViewPagerDemoActivity; + + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + findViewById(R.id.create_1000_objects).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + create1000Objects(); + } + }); + + + findViewById(R.id.goto_list_view).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(MainActivity.this, ListViewDemoActivity.class); + startActivity(intent); + } + }); + + findViewById(R.id.goto_viewpager).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(MainActivity.this, ViewPagerDemoActivity.class); + startActivity(intent); + } + }); + + findViewById(R.id.goto_recycler_view).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(MainActivity.this, RecyclerViewDemoActivity.class); + startActivity(intent); + } + }); + } + + public void create1000Objects() { + + for (int i = 0; i < 1000; i++) { + ParseDemoObject object = ParseDemoObject.getObject(i); + object.saveEventually(); + } + Toast.makeText(this, "Check on your app's data", Toast.LENGTH_LONG).show(); + } +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ParseDemoObject.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ParseDemoObject.java new file mode 100644 index 0000000..754f9e2 --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/ParseDemoObject.java @@ -0,0 +1,39 @@ +package com.example.tuanchauict.parselistloaderexample; + +import com.parse.ParseClassName; +import com.parse.ParseObject; + +/** + * Created by tuanchauict on 10/25/15. + */ +@ParseClassName("ListLoaderItem") +public class ParseDemoObject extends ParseObject { + public static final String ATTR_SSD = "ssd"; + public static final String ATTR_FIRST_NAME = "firstName"; + public static final String ATTR_LAST_NAME = "lastName"; + + + public static ParseDemoObject getObject(int ssd){ + ParseDemoObject object = new ParseDemoObject(); + object.put(ATTR_SSD, ssd); + object.put(ATTR_FIRST_NAME, "FirstName " + ssd); + object.put(ATTR_LAST_NAME, "LastName " + ssd); + return object; + } + + + public int getSSD(){ + return getInt(ATTR_SSD); + } + + public String getFirstname(){ + return getString(ATTR_FIRST_NAME); + } + + + public String getLastName(){ + return getString(ATTR_LAST_NAME); + } + + +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewAdapter.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewAdapter.java new file mode 100644 index 0000000..6296115 --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewAdapter.java @@ -0,0 +1,79 @@ +package com.example.tuanchauict.parselistloaderexample.listview; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import com.example.tuanchauict.parselistloaderexample.ParseDemoObject; +import com.example.tuanchauict.parselistloaderexample.R; +import com.parse.ParseListLoader; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tuanchauict on 10/25/15. + */ +public class ListViewAdapter extends ArrayAdapter + implements ParseListLoader.LoaderTarget { + private List mObjects; + private boolean mHasNextPage; + + private LayoutInflater mInflater; + + public ListViewAdapter(Context context) { + super(context, 0); + mInflater = LayoutInflater.from(context); + mObjects = new ArrayList<>(); + mHasNextPage = true; + } + + public boolean hasNextPage() { + return mHasNextPage; + } + + public void setHasNextPage(boolean hasNextPage) { + mHasNextPage = hasNextPage; + } + + @Override + public int getCount() { + return mObjects.isEmpty() ? 0 : mHasNextPage ? mObjects.size() + 1 : mObjects.size(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if(position >= mObjects.size()){ + return mInflater.inflate(R.layout.item_waiting, parent, false); + } + + if (convertView == null || convertView.getTag() != Boolean.TRUE) { + convertView = mInflater.inflate(R.layout.item_layout, parent, false); + convertView.setTag(Boolean.TRUE); + } + ParseDemoObject item = mObjects.get(position); + + ((TextView) convertView.findViewById(R.id.txt_name)) + .setText(item.getFirstname() + " " + item.getLastName()); + + return convertView; + } + + @Override + public void appendList(List sublist) { + mObjects.addAll(sublist); + } + + @Override + public void clearList() { + mObjects.clear(); + } + + @Override + public void notifyDataChanged() { + notifyDataSetChanged(); + } +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewDemoActivity.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewDemoActivity.java new file mode 100644 index 0000000..8786753 --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/listview/ListViewDemoActivity.java @@ -0,0 +1,84 @@ +package com.example.tuanchauict.parselistloaderexample.listview; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.view.MenuItem; +import android.widget.AbsListView; +import android.widget.ListView; + +import com.example.tuanchauict.parselistloaderexample.ParseDemoObject; +import com.example.tuanchauict.parselistloaderexample.R; +import com.parse.ParseException; +import com.parse.ParseListLoader; +import com.parse.ParseQuery; + +import java.util.List; + +/** + * Created by tuanchauict on 10/25/15. + */ +public class ListViewDemoActivity extends FragmentActivity + implements ParseListLoader.OnQueryLoadListener{ + + + private ListView mListView; + private ListViewAdapter mAdapter; + private ParseListLoader mLoader; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_view); + + getActionBar().setDisplayHomeAsUpEnabled(true); + + mListView = (ListView) findViewById(R.id.list_view); + mAdapter = new ListViewAdapter(this); + mLoader = new ParseListLoader<>(mAdapter, + new ParseListLoader.QueryFactory() { + @Override + public ParseQuery create() { + ParseQuery query = ParseQuery.getQuery(ParseDemoObject.class); + query.addAscendingOrder(ParseDemoObject.ATTR_SSD); + return query; + } + }); + mLoader.loadObjects(); + + mListView.setAdapter(mAdapter); + + mListView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, + int totalItemCount) { + if(firstVisibleItem + visibleItemCount > totalItemCount -5 + && mAdapter.hasNextPage()){ + mLoader.loadNextPage(); + } + } + }); + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if(item.getItemId() == android.R.id.home){ + onBackPressed(); + } + return super.onMenuItemSelected(featureId, item); + } + + @Override + public void onLoading() { + + } + + @Override + public void onLoaded(List list, boolean hasNextPage, ParseException e) { + mAdapter.setHasNextPage(hasNextPage); + } +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerAdapter.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerAdapter.java new file mode 100644 index 0000000..d87d34f --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerAdapter.java @@ -0,0 +1,94 @@ +package com.example.tuanchauict.parselistloaderexample.recycler; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.example.tuanchauict.parselistloaderexample.ParseDemoObject; +import com.example.tuanchauict.parselistloaderexample.R; +import com.parse.ParseListLoader; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tuanchauict on 10/26/15. + */ +public class RecyclerAdapter extends RecyclerView.Adapter implements ParseListLoader.LoaderTarget { + + static class Holder extends RecyclerView.ViewHolder { + View mView; + public Holder(View itemView) { + super(itemView); + mView = itemView; + } + } + + private List mObjects; + private boolean mHasNextPage; + private LayoutInflater mInflater; + + public RecyclerAdapter(Context context) { + mInflater = LayoutInflater.from(context); + + mObjects = new ArrayList<>(); + mHasNextPage = true; + } + + @Override + public int getItemViewType(int position) { + return mObjects.size() <= position ? 1 : 0; + } + + @Override + public Holder onCreateViewHolder(ViewGroup parent, int viewType) { + if(viewType == 0){ + return new Holder(mInflater.inflate(R.layout.item_layout, parent, false)); + } + else{ + return new Holder(mInflater.inflate(R.layout.item_waiting, parent, false)); + } + } + + @Override + public void onBindViewHolder(Holder holder, int position) { + if(getItemViewType(position) > 0){ + return; + } + + ParseDemoObject object = mObjects.get(position); + TextView txt = (TextView) holder.mView.findViewById(R.id.txt_name); + txt.setText(object.getFirstname() + " " + object.getLastName()); + } + + @Override + public int getItemCount() { + return mObjects.isEmpty() ? 0 : mHasNextPage ? mObjects.size() + 1 : mObjects.size(); + } + + @Override + public void appendList(List sublist) { + mObjects.addAll(sublist); + } + + @Override + public void clearList() { + mObjects.clear(); + } + + @Override + public void notifyDataChanged() { + notifyDataSetChanged(); + } + + public boolean hasNextPage() { + return mHasNextPage; + } + + public void setHasNextPage(boolean hasNextPage) { + mHasNextPage = hasNextPage; + } +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerViewDemoActivity.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerViewDemoActivity.java new file mode 100644 index 0000000..23ebb0e --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/recycler/RecyclerViewDemoActivity.java @@ -0,0 +1,89 @@ +package com.example.tuanchauict.parselistloaderexample.recycler; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.MenuItem; + +import com.example.tuanchauict.parselistloaderexample.ParseDemoObject; +import com.example.tuanchauict.parselistloaderexample.R; +import com.parse.ParseException; +import com.parse.ParseListLoader; +import com.parse.ParseQuery; + +import java.util.List; + +/** + * Created by tuanchauict on 10/26/15. + */ +public class RecyclerViewDemoActivity extends FragmentActivity { + + RecyclerView mRecyclerView; + RecyclerAdapter mAdapter; + + ParseListLoader mLoader; + + LinearLayoutManager mLayoutManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_recycler_view); + getActionBar().setDisplayHomeAsUpEnabled(true); + mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); + + mAdapter = new RecyclerAdapter(this); + mLoader = new ParseListLoader<>(mAdapter, new ParseListLoader.QueryFactory() { + @Override + public ParseQuery create() { + ParseQuery query = ParseQuery.getQuery(ParseDemoObject.class); + query.addAscendingOrder(ParseDemoObject.ATTR_SSD); + return query; + } + }); + + mLayoutManager = new LinearLayoutManager(this); + + mRecyclerView.setAdapter(mAdapter); + mRecyclerView.setLayoutManager(mLayoutManager); + mLoader.loadObjects(); + + mLoader.addOnQueryLoadListener(new ParseListLoader.OnQueryLoadListener() { + @Override + public void onLoading() { + + } + + @Override + public void onLoaded(List list, boolean hasNextPage, ParseException e) { + mAdapter.setHasNextPage(hasNextPage); + } + }); + + mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + int pastVisibleItems, visibleItemCount, totalItemCount; + + visibleItemCount = mLayoutManager.getChildCount(); + totalItemCount = mLayoutManager.getItemCount(); + pastVisibleItems = mLayoutManager.findFirstVisibleItemPosition(); + + if(mAdapter.hasNextPage() && visibleItemCount + pastVisibleItems > totalItemCount - 5){ + mLoader.loadNextPage(); + } + } + }); + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if(item.getItemId() == android.R.id.home){ + onBackPressed(); + } + return super.onMenuItemSelected(featureId, item); + } +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerAdapter.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerAdapter.java new file mode 100644 index 0000000..bfa19c1 --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerAdapter.java @@ -0,0 +1,99 @@ +package com.example.tuanchauict.parselistloaderexample.viewpager; + +import android.content.Context; +import android.support.v4.view.PagerAdapter; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.example.tuanchauict.parselistloaderexample.ParseDemoObject; +import com.example.tuanchauict.parselistloaderexample.R; +import com.parse.ParseListLoader; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tuanchauict on 10/25/15. + */ +public class ViewPagerAdapter extends PagerAdapter + implements ParseListLoader.LoaderTarget { + private List mObjects; + private boolean mHasNextPage; + + private List mPool; + + LayoutInflater mInflater; + + public ViewPagerAdapter(Context context) { + mInflater = LayoutInflater.from(context); + mObjects = new ArrayList<>(); + mHasNextPage = true; + + mPool = new ArrayList<>(); + } + + @Override + public int getCount() { + return mObjects.isEmpty() ? 0 : mHasNextPage ? mObjects.size() + 1 : mObjects.size(); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + if (position >= mObjects.size()) { + return mInflater.inflate(R.layout.item_waiting, container, true); + } + + View view; + if (mPool.isEmpty()) { + view = mInflater.inflate(R.layout.item_layout_2, container, false); + } else { + view = mPool.remove(0); + } + + ParseDemoObject object = mObjects.get(position); + + TextView txt = (TextView) view.findViewById(R.id.txt_name); + txt.setText(object.getFirstname() + " " + object.getLastName()); + container.addView(view); + return view; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + View view = (View) object; + container.removeView(view); + if (position < mObjects.size()) + mPool.add((View) object); + } + + public boolean hasNextPage() { + return mHasNextPage; + } + + public void setHasNextPage(boolean hasNextPage) { + mHasNextPage = hasNextPage; + } + + @Override + public void appendList(List sublist) { + mObjects.addAll(sublist); + } + + @Override + public void clearList() { + mObjects.clear(); + } + + @Override + public void notifyDataChanged() { + notifyDataSetChanged(); + } +} diff --git a/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerDemoActivity.java b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerDemoActivity.java new file mode 100644 index 0000000..84ccae5 --- /dev/null +++ b/parselistloaderexample/src/main/java/com/example/tuanchauict/parselistloaderexample/viewpager/ViewPagerDemoActivity.java @@ -0,0 +1,84 @@ +package com.example.tuanchauict.parselistloaderexample.viewpager; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.view.ViewPager; +import android.util.Log; +import android.view.MenuItem; + +import com.example.tuanchauict.parselistloaderexample.ParseDemoObject; +import com.example.tuanchauict.parselistloaderexample.R; +import com.parse.ParseException; +import com.parse.ParseListLoader; +import com.parse.ParseQuery; + +import java.util.List; + +/** + * Created by tuanchauict on 10/25/15. + */ +public class ViewPagerDemoActivity extends FragmentActivity { + ViewPager mViewPager; + ViewPagerAdapter mAdapter; + ParseListLoader mLoader; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_view_pager); + getActionBar().setDisplayHomeAsUpEnabled(true); + + mViewPager = (ViewPager) findViewById(R.id.view_pager); + mAdapter = new ViewPagerAdapter(this); + mLoader = new ParseListLoader<>(mAdapter, new ParseListLoader.QueryFactory() { + @Override + public ParseQuery create() { + ParseQuery query = ParseQuery.getQuery(ParseDemoObject.class); + query.addAscendingOrder(ParseDemoObject.ATTR_SSD); + return query; + } + }); + mViewPager.setAdapter(mAdapter); + mLoader.loadObjects(); + + + mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + if(position + 5 > mAdapter.getCount() && mAdapter.hasNextPage()){ + mLoader.loadNextPage(); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + mLoader.addOnQueryLoadListener(new ParseListLoader.OnQueryLoadListener() { + @Override + public void onLoading() { + + } + + @Override + public void onLoaded(List list, boolean hasNextPage, ParseException e) { + mAdapter.setHasNextPage(hasNextPage); + } + }); + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if(item.getItemId() == android.R.id.home){ + onBackPressed(); + } + return super.onMenuItemSelected(featureId, item); + } +} diff --git a/parselistloaderexample/src/main/res/drawable/ic_launcher.png b/parselistloaderexample/src/main/res/drawable/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..324e72cdd7480cb983fa1bcc7ce686e51ef87fe7 GIT binary patch literal 7718 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 literal 0 HcmV?d00001 diff --git a/parselistloaderexample/src/main/res/layout/activity_list_view.xml b/parselistloaderexample/src/main/res/layout/activity_list_view.xml new file mode 100644 index 0000000..64af1ec --- /dev/null +++ b/parselistloaderexample/src/main/res/layout/activity_list_view.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/parselistloaderexample/src/main/res/layout/activity_main.xml b/parselistloaderexample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..ded4c24 --- /dev/null +++ b/parselistloaderexample/src/main/res/layout/activity_main.xml @@ -0,0 +1,40 @@ + + + +