package com.emilsjolander.components.stickylistheaders; import java.util.LinkedList; import java.util.List; import android.content.Context; import android.database.DataSetObserver; import android.graphics.drawable.Drawable; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Checkable; import android.widget.ListAdapter; /** * A {@link ListAdapter} which wraps a {@link StickyListHeadersAdapter} and * automatically handles wrapping the result of * {@link StickyListHeadersAdapter#getView(int, android.view.View, android.view.ViewGroup)} * and * {@link StickyListHeadersAdapter#getHeaderView(int, android.view.View, android.view.ViewGroup)} * appropriately. * * @author Jake Wharton (jakewharton@gmail.com) */ public class AdapterWrapper extends BaseAdapter implements StickyListHeadersAdapter { public interface OnHeaderClickListener{ public void onHeaderClick(View header, int itemPosition, long headerId); } public final StickyListHeadersAdapter mDelegate; private final List<View> mHeaderCache = new LinkedList<View>(); private final Context mContext; private Drawable mDivider; private int mDividerHeight; private OnHeaderClickListener mOnHeaderClickListener; private DataSetObserver mDataSetObserver = new DataSetObserver() { @Override public void onInvalidated() { mHeaderCache.clear(); AdapterWrapper.super.notifyDataSetInvalidated(); } @Override public void onChanged() { AdapterWrapper.super.notifyDataSetChanged(); } }; public AdapterWrapper(Context context, StickyListHeadersAdapter delegate) { this.mContext = context; this.mDelegate = delegate; delegate.registerDataSetObserver(mDataSetObserver); } public void setDivider(Drawable divider) { this.mDivider = divider; } public void setDividerHeight(int dividerHeight) { this.mDividerHeight = dividerHeight; } @Override public boolean areAllItemsEnabled() { return mDelegate.areAllItemsEnabled(); } @Override public boolean isEnabled(int position) { return mDelegate.isEnabled(position); } @Override public int getCount() { return mDelegate.getCount(); } @Override public Object getItem(int position) { return mDelegate.getItem(position); } @Override public long getItemId(int position) { return mDelegate.getItemId(position); } @Override public boolean hasStableIds() { return mDelegate.hasStableIds(); } @Override public int getItemViewType(int position) { return mDelegate.getItemViewType(position); } @Override public int getViewTypeCount() { return mDelegate.getViewTypeCount(); } @Override public boolean isEmpty() { return mDelegate.isEmpty(); } /** * Will recycle header from {@link WrapperView} if it exists */ private void recycleHeaderIfExists(WrapperView wv) { View header = wv.mHeader; if (header != null) { mHeaderCache.add(header); } } /** * Get a header view. This optionally pulls a header from the supplied * {@link WrapperView} and will also recycle the divider if it exists. */ private View configureHeader(WrapperView wv, final int position) { View header = wv.mHeader == null ? popHeader() : wv.mHeader; header = mDelegate.getHeaderView(position, header, wv); if (header == null) { throw new NullPointerException("Header view must not be null."); } //if the header isn't clickable, the listselector will be drawn on top of the header header.setClickable(true); header.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(mOnHeaderClickListener != null){ long headerId = mDelegate.getHeaderId(position); mOnHeaderClickListener.onHeaderClick(v, position, headerId); } } }); return header; } private View popHeader() { if(mHeaderCache.size() > 0) { return mHeaderCache.remove(0); } return null; } /** Returns {@code true} if the previous position has the same header ID. */ private boolean previousPositionHasSameHeader(int position) { return position != 0 && mDelegate.getHeaderId(position) == mDelegate .getHeaderId(position - 1); } @Override public WrapperView getView(int position, View convertView, ViewGroup parent) { WrapperView wv = (convertView == null) ? new WrapperView(mContext) : (WrapperView) convertView; View item = mDelegate.getView(position, wv.mItem, wv); View header = null; if (previousPositionHasSameHeader(position)) { recycleHeaderIfExists(wv); } else { header = configureHeader(wv, position); } if((item instanceof Checkable) && !(wv instanceof CheckableWrapperView)) { // Need to create Checkable subclass of WrapperView for ListView to work correctly wv = new CheckableWrapperView(mContext); } else if(!(item instanceof Checkable) && (wv instanceof CheckableWrapperView)) { wv = new WrapperView(mContext); } wv.update(item, header, mDivider, mDividerHeight); return wv; } public void setOnHeaderClickListener(OnHeaderClickListener onHeaderClickListener){ this.mOnHeaderClickListener = onHeaderClickListener; } @Override public boolean equals(Object o) { return mDelegate.equals(o); } @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { return ((BaseAdapter) mDelegate).getDropDownView(position, convertView, parent); } @Override public int hashCode() { return mDelegate.hashCode(); } @Override public void notifyDataSetChanged() { ((BaseAdapter) mDelegate).notifyDataSetChanged(); } @Override public void notifyDataSetInvalidated() { ((BaseAdapter) mDelegate).notifyDataSetInvalidated(); } @Override public String toString() { return mDelegate.toString(); } @Override public View getHeaderView(int position, View convertView, ViewGroup parent) { return mDelegate.getHeaderView(position, convertView, parent); } @Override public long getHeaderId(int position) { return mDelegate.getHeaderId(position); } }