Skip to content

Commit

Permalink
Add sample for RecyclerView
Browse files Browse the repository at this point in the history
  • Loading branch information
grantland committed Jan 28, 2016
1 parent 7011460 commit 5a51b42
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 0 deletions.
1 change: 1 addition & 0 deletions ParseUI-Widget-Sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ android {

dependencies {
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.0'
compile project(':ParseUI-Widget')

testCompile 'junit:junit:4.12'
Expand Down
4 changes: 4 additions & 0 deletions ParseUI-Widget-Sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
</intent-filter>
</activity>

<activity
android:name=".RecyclerActivity"
android:label="@string/recycler_name"/>

<activity
android:name=".ListActivity"
android:label="@string/list_name"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.sample_recycler).setOnClickListener(this);
findViewById(R.id.sample_list).setOnClickListener(this);
}

Expand All @@ -21,6 +22,11 @@ protected void onCreate(Bundle savedInstanceState) {
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.sample_recycler: {
Intent intent = new Intent(this, RecyclerActivity.class);
startActivity(intent);
break;
}
case R.id.sample_list: {
Intent intent = new Intent(this, ListActivity.class);
startActivity(intent);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
package com.parse.ui.widget.sample;

import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.widget.util.ParseQueryPager;

import java.util.List;

import bolts.CancellationTokenSource;
import bolts.Continuation;
import bolts.Task;

public class RecyclerActivity extends AppCompatActivity {

private SwipeRefreshLayout refreshLayout;

private MyAdapter<ParseObject> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);

RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);

adapter = new MyAdapter<>(createPager());
recyclerView.setAdapter(adapter);

refreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh);
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
final ParseQueryPager<ParseObject> pager = createPager();
pager.loadNextPage().continueWith(new Continuation<List<ParseObject>, Void>() {
@Override
public Void then(Task<List<ParseObject>> task) throws Exception {
refreshLayout.setRefreshing(false);

if (task.isCancelled()) {
return null;
}

if (task.isFaulted()) {
return null;
}

adapter.swap(pager);
adapter.notifyDataSetChanged();
return null;
}
}, Task.UI_THREAD_EXECUTOR);
}
});
}

private ParseQueryPager<ParseObject> createPager() {
ParseQuery<ParseObject> query = ParseQuery.getQuery("TestObject");
query.orderByAscending("name");
return new ParseQueryPager<>(query, 25);
}

public static class MyAdapter<T extends ParseObject> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private static final int TYPE_ITEM = 0;
private static final int TYPE_NEXT = 1;

private static class ItemViewHolder extends RecyclerView.ViewHolder {
TextView textView;

public ItemViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView;
}
}

private static class NextViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ProgressBar progressBar;

public NextViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(android.R.id.text1);
progressBar = (ProgressBar) itemView.findViewById(android.R.id.progress);
}

public void setLoading(boolean loading) {
if (loading) {
textView.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.VISIBLE);
progressBar.setIndeterminate(true);
} else {
textView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.INVISIBLE);
progressBar.setIndeterminate(false);
}
}
}

private final Object lock = new Object();
private ParseQueryPager<T> pager;
private CancellationTokenSource cts;

public MyAdapter(ParseQueryPager<T> pager) {
swap(pager);
}

public ParseQueryPager<T> getPager() {
synchronized (lock) {
return pager;
}
}

public void swap(ParseQueryPager<T> pager) {
synchronized (lock) {
if (cts != null) {
cts.cancel();
}
this.pager = pager;
this.cts = new CancellationTokenSource();
}
}

private void loadNextPage() {
final ParseQueryPager<T> pager;
final CancellationTokenSource cts;

synchronized (lock) {
pager = this.pager;
cts = this.cts;
}

final int oldSize = pager.getObjects().size();

// Uses Tasks, so it does not support CACHE_THEN_NETWORK. See ListActivity for a sample
// with callbacks.
pager.loadNextPage(cts.getToken()).continueWith(new Continuation<List<T>, Task<Void>>() {
@Override
public Task<Void> then(Task<List<T>> task) throws Exception {
if (task.isCancelled()) {
return null;
}

if (task.isFaulted()) {
notifyDataSetChanged();
return null;
}

// Remove "Load more..."
notifyItemRemoved(oldSize);

// Insert results
List<T> results = task.getResult();
if (results.size() > 0) {
notifyItemRangeInserted(oldSize, results.size());
}

if (pager.hasNextPage()) {
// Add "Load more..."
notifyItemInserted(pager.getObjects().size());
}
return null;
}
});
notifyItemChanged(oldSize);
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case TYPE_ITEM: {
View v = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
return new ItemViewHolder(v);
}
case TYPE_NEXT: {
View v = inflater.inflate(R.layout.load_more_list_item, parent, false);
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!getPager().isLoadingNextPage()) {
loadNextPage();
}
}
});
NextViewHolder vh = new NextViewHolder(v);
vh.textView.setText(R.string.load_more);
return vh;
}
default:
throw new IllegalStateException("Invalid view type: " + viewType);
}
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case TYPE_ITEM: {
ParseObject item = getPager().getObjects().get(position);

ItemViewHolder vh = (ItemViewHolder) holder;
vh.textView.setText(item.getString("name"));
}
break;
case TYPE_NEXT: {
NextViewHolder vh = (NextViewHolder) holder;
vh.setLoading(getPager().isLoadingNextPage());
}
break;
}
}

@Override
public int getItemCount() {
ParseQueryPager<T> pager = getPager();
return pager.getObjects().size() + (pager.hasNextPage() ? 1 : 0);
}

@Override
public int getItemViewType(int position) {
return position < getPager().getObjects().size() ? TYPE_ITEM : TYPE_NEXT;
}

@Override
public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
super.registerAdapterDataObserver(observer);
// We use this method as a notification that the RecyclerView is bound to the adapter.
loadNextPage();
}
}
}
6 changes: 6 additions & 0 deletions ParseUI-Widget-Sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
android:orientation="vertical"
tools:context="com.parse.ui.widget.sample.MainActivity">

<Button
android:id="@+id/sample_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/recycler_name"/>

<Button
android:id="@+id/sample_list"
android:layout_width="match_parent"
Expand Down
19 changes: 19 additions & 0 deletions ParseUI-Widget-Sample/src/main/res/layout/activity_recycler.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</android.support.v4.widget.SwipeRefreshLayout>

</FrameLayout>
1 change: 1 addition & 0 deletions ParseUI-Widget-Sample/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<resources>
<string name="app_name">ParseUI-Widget Sample</string>
<string name="recycler_name">RecyclerView Sample</string>
<string name="list_name">ListView Sample</string>

<string name="load_more">Load more…</string>
Expand Down

0 comments on commit 5a51b42

Please sign in to comment.