Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A base adapter that handles boilerplate and view reuse.
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* @author jaynewstrom
* Created on 2/4/14.
*
* A base adapter that handles boilerplate and view reuse.
*
* Other useful goodies:
* - Animated deleting.
* - Refresh views data without calling notifyDataSetChanged.
*/
public abstract class ListAdapter<D, V extends View> extends BaseAdapter {
private final Context context;
private final LayoutInflater layoutInflater;
private List<D> data;
private int cachedListSize;
/**
* Create the adapter with an empty list.
* Useful for when data is loaded from the network, and you need to initialize the list adapter.
*/
public ListAdapter(Context context) {
this(context, new ArrayList<D>());
}
/**
* Create the adapter with an initial list.
*/
public ListAdapter(Context context, List<D> dataToSet) {
this.context = context;
this.layoutInflater = LayoutInflater.from(this.context);
setList(dataToSet);
}
public void add(D item) {
data.add(item);
cachedListSize = data.size();
notifyDataSetChanged();
}
public void add(List<D> moreData) {
data.addAll(moreData);
cachedListSize = data.size();
notifyDataSetChanged();
}
public void insert(D item, int index) {
data.add(index, item);
cachedListSize = data.size();
notifyDataSetChanged();
}
public void delete(D item) {
if (data.remove(item)) {
cachedListSize = data.size();
notifyDataSetChanged();
}
}
public void delete(List<D> dataToDelete) {
data.removeAll(dataToDelete);
cachedListSize = data.size();
notifyDataSetChanged();
}
public int indexOf(D item) {
return data.indexOf(item);
}
/**
* returns null if the list is empty, otherwise returns the last item in the list
*/
public D last() {
if (cachedListSize >= 1) {
return getItem(cachedListSize - 1);
} else {
return null;
}
}
public void setList(List<D> dataToSet) {
data = new ArrayList<D>(dataToSet);
cachedListSize = data.size();
notifyDataSetChanged();
}
public Iterable<D> getData() {
return data;
}
public ArrayList<D> getDataCopy() {
return new ArrayList<D>(data);
}
/**
* rebinds the view if it is on screen, otherwise it's a no op
*/
public void reBindView(D item, AbsListView listView) {
int index = indexOf(item);
if (index >= 0) {
// do the animation on the visible views
int firstVisible = listView.getFirstVisiblePosition();
int lastVisible = listView.getLastVisiblePosition();
if (index >= firstVisible && index <= lastVisible) {
@SuppressWarnings("unchecked") V view = (V) listView.getChildAt(index - firstVisible);
bindDataToView(item, view);
}
}
}
/**
* if the view is not found:
* - no op
* if the view is on screen:
* - animates the view off screen
* - removes the item from the adapter.
* if the view is off screen:
* - removes the item from the adapter.
*/
public void deleteView(final D item, AbsListView listView) {
deleteView(item, listView, 0);
}
/**
* This is useful for being called onActivityResult.
*
* if the view is not found:
* - no op
* if the view is on screen:
* - animates the view off screen, with a small delay
* - removes the item from the adapter.
* if the view is off screen:
* - removes the item from the adapter.
*/
public void deleteViewWithSmallDelay(final D item, AbsListView listView) {
deleteView(item, listView, 300);
}
@SuppressWarnings("ConstantConditions")
private void deleteView(final D item, AbsListView listView, int delay) {
int index = indexOf(item);
if (index >= 0) {
// do the animation on the visible views
int firstVisible = listView.getFirstVisiblePosition();
int lastVisible = listView.getLastVisiblePosition();
if (index >= firstVisible && index <= lastVisible) {
final View view = listView.getChildAt(index - firstVisible);
final ViewGroup.LayoutParams lp = view.getLayoutParams();
final int originalHeight = lp.height;
final int currentHeight = view.getHeight();
ValueAnimator animator = ValueAnimator.ofInt(currentHeight, 1);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
delete(item);
// Reset view presentation
view.setAlpha(1f);
view.setTranslationX(0);
lp.height = originalHeight;
view.setLayoutParams(lp);
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
lp.height = (Integer) valueAnimator.getAnimatedValue();
view.setLayoutParams(lp);
}
});
animator.setStartDelay(delay);
animator.start();
} else {
delete(item);
}
}
}
/**
* Replace an item with it's upgraded version,
* rebinding the new contents without calling notifyDataSetChanged.
*/
public void replace(D item, AbsListView listView) {
int index = indexOf(item);
if (index >= 0) {
data.remove(index);
data.add(index, item);
reBindView(item, listView);
}
}
protected Context getContext() {
return context;
}
@Override public int getCount() {
return cachedListSize;
}
@Override public D getItem(int position) {
return data.get(position);
}
@Override public long getItemId(int position) {
return 0;
}
@Override public V getView(int position, View convertView, ViewGroup parent) {
@SuppressWarnings("unchecked") V view = (V) convertView;
if (view == null) {
view = createView(layoutInflater);
}
bindDataToView(getItem(position), view);
return view;
}
/**
* Create a view to be reused.
*/
public abstract V createView(LayoutInflater layoutInflater);
/**
* Bind the data to the view.
*/
public abstract void bindDataToView(D data, V view);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.