package com.mm.android.deviceaddmodule.mobilecommon.widget.sticky.stickygridheaders;
|
|
import android.content.Context;
|
import android.database.DataSetObserver;
|
import android.util.AttributeSet;
|
import android.view.View;
|
import android.view.ViewGroup;
|
import android.widget.BaseAdapter;
|
import android.widget.FrameLayout;
|
|
/**
|
* Adapter wrapper to insert extra views and otherwise hack around GridView to add sections and
|
* headers.
|
*/
|
public class StickyGridHeadersBaseAdapterWrapper extends BaseAdapter {
|
private static final int sNumViewTypes = 3;
|
|
protected static final int ID_FILLER = -0x02;
|
|
protected static final int ID_HEADER = -0x01;
|
|
protected static final int ID_HEADER_FILLER = -0x03;
|
|
protected static final int POSITION_FILLER = -0x01;
|
|
protected static final int POSITION_HEADER = -0x02;
|
|
protected static final int POSITION_HEADER_FILLER = -0x03;
|
|
protected static final int VIEW_TYPE_FILLER = 0x00;
|
|
protected static final int VIEW_TYPE_HEADER = 0x01;
|
|
protected static final int VIEW_TYPE_HEADER_FILLER = 0x02;
|
|
private final Context mContext;
|
|
private int mCount;
|
|
private boolean mCounted = false;
|
|
private DataSetObserver mDataSetObserver = new DataSetObserver() {
|
@Override
|
public void onChanged() {
|
updateCount();
|
}
|
|
@Override
|
public void onInvalidated() {
|
mCounted = false;
|
}
|
};
|
|
private final StickyGridHeadersBaseAdapter mDelegate;
|
|
private StickyGridHeadersGridView mGridView;
|
|
private View mLastHeaderViewSeen;
|
|
private View mLastViewSeen;
|
|
private int mNumColumns = 1;
|
|
public StickyGridHeadersBaseAdapterWrapper(Context context, StickyGridHeadersGridView gridView,
|
StickyGridHeadersBaseAdapter delegate) {
|
mContext = context;
|
mDelegate = delegate;
|
mGridView = gridView;
|
delegate.registerDataSetObserver(mDataSetObserver);
|
}
|
|
@Override
|
public boolean areAllItemsEnabled() {
|
return false;
|
}
|
|
@Override
|
public int getCount() {
|
if (mCounted) {
|
return mCount;
|
}
|
mCount = 0;
|
int numHeaders = mDelegate.getNumHeaders();
|
if (numHeaders == 0) {
|
mCount = mDelegate.getCount();
|
mCounted = true;
|
return mCount;
|
}
|
|
for (int i = 0; i < numHeaders; i++) {
|
// Pad count with space for header and trailing filler in header
|
// group.
|
mCount += mDelegate.getCountForHeader(i) + unFilledSpacesInHeaderGroup(i) + mNumColumns;
|
}
|
mCounted = true;
|
return mCount;
|
}
|
|
/**
|
* Get the data item associated with the specified position in the data set.
|
* <p>
|
* Since this wrapper inserts fake entries to fill out items grouped by header and also spaces
|
* to insert headers into some positions will return null.
|
* </p>
|
*
|
* @param position
|
* Position of the item whose data we want within the adapter's data set.
|
* @return The data at the specified position.
|
*/
|
@Override
|
public Object getItem(int position) throws ArrayIndexOutOfBoundsException {
|
Position adapterPosition = translatePosition(position);
|
if (adapterPosition.mPosition == POSITION_FILLER || adapterPosition.mPosition == POSITION_HEADER) {
|
// Fake entry in view.
|
return null;
|
}
|
|
return mDelegate.getItem(adapterPosition.mPosition);
|
}
|
|
@Override
|
public long getItemId(int position) {
|
Position adapterPosition = translatePosition(position);
|
if (adapterPosition.mPosition == POSITION_HEADER) {
|
return ID_HEADER;
|
}
|
if (adapterPosition.mPosition == POSITION_FILLER) {
|
return ID_FILLER;
|
}
|
if (adapterPosition.mPosition == POSITION_HEADER_FILLER) {
|
return ID_HEADER_FILLER;
|
}
|
return mDelegate.getItemId(adapterPosition.mPosition);
|
}
|
|
@Override
|
public int getItemViewType(int position) {
|
Position adapterPosition = translatePosition(position);
|
if (adapterPosition.mPosition == POSITION_HEADER) {
|
return VIEW_TYPE_HEADER;
|
}
|
if (adapterPosition.mPosition == POSITION_FILLER) {
|
return VIEW_TYPE_FILLER;
|
}
|
if (adapterPosition.mPosition == POSITION_HEADER_FILLER) {
|
return VIEW_TYPE_HEADER_FILLER;
|
}
|
int itemViewType = mDelegate.getItemViewType(adapterPosition.mPosition);
|
if (itemViewType == IGNORE_ITEM_VIEW_TYPE) {
|
return itemViewType;
|
}
|
return itemViewType + sNumViewTypes;
|
}
|
|
@Override
|
public View getView(int position, View convertView, ViewGroup parent) {
|
Position adapterPosition = translatePosition(position);
|
if (adapterPosition.mPosition == POSITION_HEADER) {
|
HeaderFillerView v = getHeaderFillerView(adapterPosition.mHeader, convertView, parent);
|
View view = mDelegate.getHeaderView(adapterPosition.mHeader, (View) v.getTag(), parent);
|
mGridView.detachHeader((View) v.getTag());
|
v.setTag(view);
|
mGridView.attachHeader(view);
|
convertView = v;
|
mLastHeaderViewSeen = v;
|
v.forceLayout();
|
} else if (adapterPosition.mPosition == POSITION_HEADER_FILLER) {
|
convertView = getFillerView(convertView, parent, mLastHeaderViewSeen);
|
convertView.forceLayout();
|
} else if (adapterPosition.mPosition == POSITION_FILLER) {
|
convertView = getFillerView(convertView, parent, mLastViewSeen);
|
} else {
|
convertView = mDelegate.getView(adapterPosition.mPosition, convertView, parent);
|
mLastViewSeen = convertView;
|
}
|
|
return convertView;
|
}
|
|
@Override
|
public int getViewTypeCount() {
|
return mDelegate.getViewTypeCount() + sNumViewTypes;
|
}
|
|
/**
|
* @return the adapter wrapped by this adapter.
|
*/
|
public StickyGridHeadersBaseAdapter getWrappedAdapter() {
|
return mDelegate;
|
}
|
|
@Override
|
public boolean hasStableIds() {
|
return mDelegate.hasStableIds();
|
}
|
|
@Override
|
public boolean isEmpty() {
|
return mDelegate.isEmpty();
|
}
|
|
@Override
|
public boolean isEnabled(int position) {
|
Position adapterPosition = translatePosition(position);
|
if (adapterPosition.mPosition == POSITION_FILLER || adapterPosition.mPosition == POSITION_HEADER) {
|
return false;
|
}
|
|
return mDelegate.isEnabled(adapterPosition.mPosition);
|
}
|
|
@Override
|
public void registerDataSetObserver(DataSetObserver observer) {
|
super.registerDataSetObserver(observer);
|
mDelegate.registerDataSetObserver(observer);
|
}
|
|
public void setNumColumns(int numColumns) {
|
mNumColumns = numColumns;
|
mCounted = false;
|
// notifyDataSetChanged();
|
}
|
|
@Override
|
public void unregisterDataSetObserver(DataSetObserver observer) {
|
super.unregisterDataSetObserver(observer);
|
mDelegate.unregisterDataSetObserver(observer);
|
}
|
|
private FillerView getFillerView(View convertView, ViewGroup parent, View lastViewSeen) {
|
FillerView fillerView = (FillerView) convertView;
|
if (fillerView == null) {
|
fillerView = new FillerView(mContext);
|
}
|
|
fillerView.setMeasureTarget(lastViewSeen);
|
|
return fillerView;
|
}
|
|
private HeaderFillerView getHeaderFillerView(int headerPosition, View convertView, ViewGroup parent) {
|
HeaderFillerView headerFillerView = (HeaderFillerView) convertView;
|
if (headerFillerView == null) {
|
headerFillerView = new HeaderFillerView(mContext);
|
}
|
|
return headerFillerView;
|
}
|
|
/**
|
* Counts the number of items that would be need to fill out the last row in the group of items
|
* with the given header.
|
*
|
* @param header
|
* Header set of items are grouped by.
|
* @return The count of unfilled spaces in the last row.
|
*/
|
private int unFilledSpacesInHeaderGroup(int header) {
|
// If mNumColumns is equal to zero we will have a divide by 0 exception
|
if (mNumColumns == 0) {
|
return 0;
|
}
|
|
int remainder = mDelegate.getCountForHeader(header) % mNumColumns;
|
return remainder == 0 ? 0 : mNumColumns - remainder;
|
}
|
|
protected long getHeaderId(int position) {
|
return translatePosition(position).mHeader;
|
}
|
|
protected View getHeaderView(int position, View convertView, ViewGroup parent) {
|
if (mDelegate.getNumHeaders() == 0) {
|
return null;
|
}
|
|
return mDelegate.getHeaderView(translatePosition(position).mHeader, convertView, parent);
|
}
|
|
protected Position translatePosition(int position) {
|
int numHeaders = mDelegate.getNumHeaders();
|
if (numHeaders == 0) {
|
if (position >= mDelegate.getCount()) {
|
return new Position(POSITION_FILLER, 0);
|
}
|
return new Position(position, 0);
|
}
|
|
// Translate GridView position to Adapter position.
|
int adapterPosition = position;
|
int place = position;
|
int i;
|
|
for (i = 0; i < numHeaders; i++) {
|
int sectionCount = mDelegate.getCountForHeader(i);
|
|
// Skip past fake items making space for header in front of
|
// sections.
|
if (place == 0) {
|
// Position is first column where header will be.
|
return new Position(POSITION_HEADER, i);
|
}
|
place -= mNumColumns;
|
if (place < 0) {
|
// Position is a fake so return null.
|
return new Position(POSITION_HEADER_FILLER, i);
|
}
|
adapterPosition -= mNumColumns;
|
|
if (place < sectionCount) {
|
return new Position(adapterPosition, i);
|
}
|
|
// Skip past section end of section row filler;
|
int filler = unFilledSpacesInHeaderGroup(i);
|
adapterPosition -= filler;
|
place -= sectionCount + filler;
|
|
if (place < 0) {
|
// Position is a fake so return null.
|
return new Position(POSITION_FILLER, i);
|
}
|
}
|
|
// Position is a fake.
|
return new Position(POSITION_FILLER, i);
|
}
|
|
protected void updateCount() {
|
mCount = 0;
|
int numHeaders = mDelegate.getNumHeaders();
|
if (numHeaders == 0) {
|
mCount = mDelegate.getCount();
|
mCounted = true;
|
return;
|
}
|
|
for (int i = 0; i < numHeaders; i++) {
|
mCount += mDelegate.getCountForHeader(i) + mNumColumns;
|
}
|
mCounted = true;
|
}
|
|
/**
|
* Simple view to fill space in grid view.
|
*
|
*/
|
protected static class FillerView extends View {
|
private View mMeasureTarget;
|
|
public FillerView(Context context) {
|
super(context);
|
}
|
|
public FillerView(Context context, AttributeSet attrs) {
|
super(context, attrs);
|
}
|
|
public FillerView(Context context, AttributeSet attrs, int defStyle) {
|
super(context, attrs, defStyle);
|
}
|
|
public void setMeasureTarget(View lastViewSeen) {
|
mMeasureTarget = lastViewSeen;
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMeasureTarget.getMeasuredHeight(), MeasureSpec.EXACTLY);
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
}
|
}
|
|
/**
|
* A view to hold the section header and measure the header row height correctly.
|
*
|
*/
|
protected class HeaderFillerView extends FrameLayout {
|
private int mHeaderId;
|
|
public HeaderFillerView(Context context) {
|
super(context);
|
}
|
|
public HeaderFillerView(Context context, AttributeSet attrs) {
|
super(context, attrs);
|
}
|
|
public HeaderFillerView(Context context, AttributeSet attrs, int defStyle) {
|
super(context, attrs, defStyle);
|
}
|
|
public int getHeaderId() {
|
return mHeaderId;
|
}
|
|
/**
|
* Set the adapter id for this header so we can easily pull it later.
|
*/
|
public void setHeaderId(int headerId) {
|
mHeaderId = headerId;
|
}
|
|
@Override
|
protected LayoutParams generateDefaultLayoutParams() {
|
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
View v = (View) getTag();
|
ViewGroup.LayoutParams params = v.getLayoutParams();
|
if (params == null) {
|
params = generateDefaultLayoutParams();
|
v.setLayoutParams(params);
|
}
|
if (v.getVisibility() != View.GONE) {
|
int heightSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0,
|
params.height);
|
int widthSpec = getChildMeasureSpec(
|
MeasureSpec.makeMeasureSpec(mGridView.getWidth(), MeasureSpec.EXACTLY), 0, params.width);
|
v.measure(widthSpec, heightSpec);
|
}
|
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), v.getMeasuredHeight());
|
}
|
}
|
|
// protected static class HeaderHolder {
|
// protected View mHeaderView;
|
// }
|
|
protected static class Position {
|
protected int mHeader;
|
|
protected int mPosition;
|
|
protected Position(int position, int header) {
|
mPosition = position;
|
mHeader = header;
|
}
|
}
|
}
|