Skip to content

Instantly share code, notes, and snippets.

@davidjoneshedgehog
Created January 17, 2017 11:34
Show Gist options
  • Save davidjoneshedgehog/246cf7a351944e525fb96ba0559c8bea to your computer and use it in GitHub Desktop.
Save davidjoneshedgehog/246cf7a351944e525fb96ba0559c8bea to your computer and use it in GitHub Desktop.
Android activity demonstrating having progress bars within RecyclerView elements and how these progress bars can be updated from an Asynchronous task.
package com.hedgehoglab.progressrecyclerviewsample;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* This class handles hosting {@link ProgressBar} views within {@link RecyclerView} items, by default
* if a {@link android.support.v7.widget.RecyclerView.ViewHolder} is designated as a listener and passed
* to another thread then as this ViewHolder is recycled the listener is also recycled. This means
* that when the thread calls back to the ViewHolder it is acting on the recycled cell (e.g. start
* task on progress bar one, scroll down, and progress bar 20 might be updating when on screen).
*
* This class demonstrates attaching and detaching the ViewHolder listener as the ViewHolder is being
* bound and recycled respectively, ensuring that the task is only updating the desired ViewHolder.
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int NUM_OF_ITEMS = 50;
private SparseArray<WaitingTask> mWaitingTaskSparseArray = new SparseArray<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_recyclerview);
LinearLayoutManager llm = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(llm);
ProgressAdapter progressAdapter = new ProgressAdapter();
recyclerView.setAdapter(progressAdapter);
List<ProgressObject> progressObjects = new ArrayList<>();
for (int i = 0; i < NUM_OF_ITEMS; i++) {
progressObjects.add(new ProgressObject(i, "Position " + i, 0));
}
progressAdapter.updateProgressObjects(progressObjects);
}
private interface WaitingListener {
void onProgressUpdated(int progress);
}
private class ProgressAdapter extends RecyclerView.Adapter<ProgressViewHolder> {
private List<ProgressObject> mProgressObjectList = new ArrayList<>();
void updateProgressObjects(@NonNull List<ProgressObject> progressObjects) {
mProgressObjectList = progressObjects;
notifyDataSetChanged();
}
@Override
public ProgressViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewholder_progress_object, parent, false);
return new ProgressViewHolder(root);
}
@Override
public void onBindViewHolder(ProgressViewHolder holder, int position) {
holder.bind(mProgressObjectList.get(position));
}
/**
* If a task has been created for the {@link ProgressObject} for this {@link ProgressViewHolder}
* then the listener is removed and the view is then recycled
* @param holder {@link ProgressViewHolder} ViewHolder to recycle
*/
@Override
public void onViewRecycled(ProgressViewHolder holder) {
WaitingTask task = mWaitingTaskSparseArray.get(holder.getId());
if (task != null) {
task.updateListener(null);
}
super.onViewRecycled(holder);
}
@Override
public int getItemCount() {
return mProgressObjectList.size();
}
}
private class ProgressViewHolder extends RecyclerView.ViewHolder implements WaitingListener {
private ProgressBar mProgressBar;
private TextView mProgressText;
private int mId;
ProgressViewHolder(View itemView) {
super(itemView);
mProgressBar = (ProgressBar) itemView.findViewById(R.id.viewholder_progress_bar);
mProgressText = (TextView) itemView.findViewById(R.id.viewholder_progress_text);
}
/**
* When binding the {@link ProgressObject} to the ViewHolder, ensure that there is some form
* of ID held as a member variable to identify the task for this item, then update the
* progress bar to it's current value and if a task already exists for this data item, update
* the listener.
*
* @param progressObject {@link ProgressObject} Data to be bound
*/
void bind(final ProgressObject progressObject) {
mId = progressObject.getId();
mProgressText.setText(progressObject.getTitle());
mProgressBar.setProgress(progressObject.getProgress());
WaitingTask task = mWaitingTaskSparseArray.get(mId);
if (task != null) {
task.updateListener(ProgressViewHolder.this);
}
itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
// Create the task, set the listener, add to the task controller, and run
WaitingTask task = new WaitingTask(progressObject, ProgressViewHolder.this);
mWaitingTaskSparseArray.put(mId, task);
task.execute();
}
});
}
@Override
public void onProgressUpdated(int progress) {
// Must be run on the UI thread, updates the progress bar to a new value via the callback
mProgressBar.setProgress(progress);
}
public int getId() {
return mId;
}
}
private class WaitingTask extends AsyncTask<Void, Integer, Void> {
private ProgressObject mProgressObject;
private WeakReference<WaitingListener> mWaitingListenerWeakReference;
WaitingTask(ProgressObject progressObject, WaitingListener waitingListener) {
mProgressObject = progressObject;
updateListener(waitingListener);
}
void updateListener(@Nullable WaitingListener waitingListener) {
mWaitingListenerWeakReference = new WeakReference<>(waitingListener);
}
@Override
protected Void doInBackground(Void... voids) {
try {
for (int i = 0; i <= 100; i++) {
Thread.sleep(50); // Random delay
mProgressObject.setProgress(i); // Update data set
publishProgress(i); // Inform UI of progress
}
} catch (InterruptedException ie) {
Log.e(TAG, "Interrupted Exception: " + ie.getLocalizedMessage());
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (mWaitingListenerWeakReference != null) {
WaitingListener listener = mWaitingListenerWeakReference.get();
if (listener != null) {
listener.onProgressUpdated(values[0]);
}
}
}
}
private class ProgressObject {
private int mId;
private String mTitle;
private int mProgress;
ProgressObject(int id, String title, int progress) {
mId = id;
mTitle = title;
mProgress = progress;
}
int getId() {
return mId;
}
void setId(int id) {
mId = id;
}
String getTitle() {
return mTitle;
}
void setTitle(String title) {
mTitle = title;
}
int getProgress() {
return mProgress;
}
void setProgress(int progress) {
mProgress = progress;
}
}
}
@Sternbach-Software
Copy link

Can you please post this on stackexchange? I wish I found this a long time a ago!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment