package se.emilsjolander.stickylistheaders;
import com.lesgens.blindr.R;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.SectionIndexer;
import se.emilsjolander.stickylistheaders.WrapperViewList.LifeCycleListener;
/**
* Even though this is a FrameLayout subclass we still consider it a ListView.
* This is because of 2 reasons:
* 1. It acts like as ListView.
* 2. It used to be a ListView subclass and refactoring the name would cause compatibility errors.
*
* @author Emil Sjölander
*/
public class StickyListHeadersListView extends FrameLayout {
public interface OnHeaderClickListener {
void onHeaderClick(StickyListHeadersListView l, View header,
int itemPosition, long headerId, boolean currentlySticky);
}
/**
* Notifies the listener when the sticky headers top offset has changed.
*/
public interface OnStickyHeaderOffsetChangedListener {
/**
* @param l The view parent
* @param header The currently sticky header being offset.
* This header is not guaranteed to have it's measurements set.
* It is however guaranteed that this view has been measured,
* therefor you should user getMeasured* methods instead of
* get* methods for determining the view's size.
* @param offset The amount the sticky header is offset by towards to top of the screen.
*/
void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset);
}
/**
* Notifies the listener when the sticky header has been updated
*/
public interface OnStickyHeaderChangedListener {
/**
* @param l The view parent
* @param header The new sticky header view.
* @param itemPosition The position of the item within the adapter's data set of
* the item whose header is now sticky.
* @param headerId The id of the new sticky header.
*/
void onStickyHeaderChanged(StickyListHeadersListView l, View header,
int itemPosition, long headerId);
}
/* --- Children --- */
private WrapperViewList mList;
private View mHeader;
/* --- Header state --- */
private Long mHeaderId;
// used to not have to call getHeaderId() all the time
private Integer mHeaderPosition;
private Integer mHeaderOffset;
/* --- Delegates --- */
private OnScrollListener mOnScrollListenerDelegate;
private AdapterWrapper mAdapter;
/* --- Settings --- */
private boolean mAreHeadersSticky = true;
private boolean mClippingToPadding = true;
private boolean mIsDrawingListUnderStickyHeader = true;
private int mStickyHeaderTopOffset = 0;
private int mPaddingLeft = 0;
private int mPaddingTop = 0;
private int mPaddingRight = 0;
private int mPaddingBottom = 0;
/* --- Other --- */
private OnHeaderClickListener mOnHeaderClickListener;
private OnStickyHeaderOffsetChangedListener mOnStickyHeaderOffsetChangedListener;
private OnStickyHeaderChangedListener mOnStickyHeaderChangedListener;
private AdapterWrapperDataSetObserver mDataSetObserver;
private Drawable mDivider;
private int mDividerHeight;
public StickyListHeadersListView(Context context) {
this(context, null);
}
public StickyListHeadersListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public StickyListHeadersListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Initialize the wrapped list
mList = new WrapperViewList(context);
// null out divider, dividers are handled by adapter so they look good with headers
mDivider = mList.getDivider();
mDividerHeight = mList.getDividerHeight();
mList.setDivider(null);
mList.setDividerHeight(0);
if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.StickyListHeadersListView, 0, 0);
try {
// -- View attributes --
int padding = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_padding, 0);
mPaddingLeft = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingLeft, padding);
mPaddingTop = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingTop, padding);
mPaddingRight = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingRight, padding);
mPaddingBottom = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingBottom, padding);
setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
// Set clip to padding on the list and reset value to default on
// wrapper
mClippingToPadding = a.getBoolean(R.styleable.StickyListHeadersListView_android_clipToPadding, true);
super.setClipToPadding(true);
mList.setClipToPadding(mClippingToPadding);
// scrollbars
final int scrollBars = a.getInt(R.styleable.StickyListHeadersListView_android_scrollbars, 0x00000200);
mList.setVerticalScrollBarEnabled((scrollBars & 0x00000200) != 0);
mList.setHorizontalScrollBarEnabled((scrollBars & 0x00000100) != 0);
// overscroll
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mList.setOverScrollMode(a.getInt(R.styleable.StickyListHeadersListView_android_overScrollMode, 0));
}
// -- ListView attributes --
mList.setFadingEdgeLength(a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_fadingEdgeLength,
mList.getVerticalFadingEdgeLength()));
final int fadingEdge = a.getInt(R.styleable.StickyListHeadersListView_android_requiresFadingEdge, 0);
if (fadingEdge == 0x00001000) {
mList.setVerticalFadingEdgeEnabled(false);
mList.setHorizontalFadingEdgeEnabled(true);
} else if (fadingEdge == 0x00002000) {
mList.setVerticalFadingEdgeEnabled(true);
mList.setHorizontalFadingEdgeEnabled(false);
} else {
mList.setVerticalFadingEdgeEnabled(false);
mList.setHorizontalFadingEdgeEnabled(false);
}
mList.setCacheColorHint(a
.getColor(R.styleable.StickyListHeadersListView_android_cacheColorHint, mList.getCacheColorHint()));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mList.setChoiceMode(a.getInt(R.styleable.StickyListHeadersListView_android_choiceMode,
mList.getChoiceMode()));
}
mList.setDrawSelectorOnTop(a.getBoolean(R.styleable.StickyListHeadersListView_android_drawSelectorOnTop, false));
mList.setFastScrollEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_fastScrollEnabled,
mList.isFastScrollEnabled()));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mList.setFastScrollAlwaysVisible(a.getBoolean(
R.styleable.StickyListHeadersListView_android_fastScrollAlwaysVisible,
mList.isFastScrollAlwaysVisible()));
}
mList.setScrollBarStyle(a.getInt(R.styleable.StickyListHeadersListView_android_scrollbarStyle, 0));
if (a.hasValue(R.styleable.StickyListHeadersListView_android_listSelector)) {
mList.setSelector(a.getDrawable(R.styleable.StickyListHeadersListView_android_listSelector));
}
mList.setScrollingCacheEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_scrollingCache,
mList.isScrollingCacheEnabled()));
if (a.hasValue(R.styleable.StickyListHeadersListView_android_divider)) {
mDivider = a.getDrawable(R.styleable.StickyListHeadersListView_android_divider);
}
mDividerHeight = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_dividerHeight,
mDividerHeight);
mList.setTranscriptMode(a.getInt(R.styleable.StickyListHeadersListView_android_transcriptMode,
ListView.TRANSCRIPT_MODE_DISABLED));
mList.setStackFromBottom(a.getBoolean(R.styleable.StickyListHeadersListView_android_stackFromBottom, false));
// -- StickyListHeaders attributes --
mAreHeadersSticky = a.getBoolean(R.styleable.StickyListHeadersListView_hasStickyHeaders, true);
mIsDrawingListUnderStickyHeader = a.getBoolean(
R.styleable.StickyListHeadersListView_isDrawingListUnderStickyHeader,
true);
} finally {
a.recycle();
}
}
// attach some listeners to the wrapped list
mList.setLifeCycleListener(new WrapperViewListLifeCycleListener());
mList.setOnScrollListener(new WrapperListScrollListener());
addView(mList);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureHeader(mHeader);
}
private void ensureHeaderHasCorrectLayoutParams(View header) {
ViewGroup.LayoutParams lp = header.getLayoutParams();
if (lp == null) {
lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
header.setLayoutParams(lp);
} else if (lp.height == LayoutParams.MATCH_PARENT || lp.width == LayoutParams.WRAP_CONTENT) {
lp.height = LayoutParams.WRAP_CONTENT;
lp.width = LayoutParams.MATCH_PARENT;
header.setLayoutParams(lp);
}
}
private void measureHeader(View header) {
if (header != null) {
final int width = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
final int parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
final int parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
measureChild(header, parentWidthMeasureSpec,
parentHeightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mList.layout(0, 0, mList.getMeasuredWidth(), getHeight());
if (mHeader != null) {
MarginLayoutParams lp = (MarginLayoutParams) mHeader.getLayoutParams();
int headerTop = lp.topMargin + stickyHeaderTop();
mHeader.layout(mPaddingLeft, headerTop, mHeader.getMeasuredWidth()
+ mPaddingLeft, headerTop + mHeader.getMeasuredHeight());
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
// Only draw the list here.
// The header should be drawn right after the lists children are drawn.
// This is done so that the header is above the list items
// but below the list decorators (scroll bars etc).
if (mList.getVisibility() == VISIBLE || mList.getAnimation() != null) {
drawChild(canvas, mList, 0);
}
}
// Reset values tied the header. also remove header form layout
// This is called in response to the data set or the adapter changing
private void clearHeader() {
if (mHeader != null) {
removeView(mHeader);
mHeader = null;
mHeaderId = null;
mHeaderPosition = null;
mHeaderOffset = null;
// reset the top clipping length
mList.setTopClippingLength(0);
updateHeaderVisibilities();
}
}
private void updateOrClearHeader(int firstVisiblePosition) {
final int adapterCount = mAdapter == null ? 0 : mAdapter.getCount();
if (adapterCount == 0 || !mAreHeadersSticky) {
return;
}
final int headerViewCount = mList.getHeaderViewsCount();
int headerPosition = firstVisiblePosition - headerViewCount;
if (mList.getChildCount() > 0) {
View firstItem = mList.getChildAt(0);
if (firstItem.getBottom() < stickyHeaderTop()) {
headerPosition++;
}
}
// It is not a mistake to call getFirstVisiblePosition() here.
// Most of the time getFixedFirstVisibleItem() should be called
// but that does not work great together with getChildAt()
final boolean doesListHaveChildren = mList.getChildCount() != 0;
final boolean isFirstViewBelowTop = doesListHaveChildren
&& mList.getFirstVisiblePosition() == 0
&& mList.getChildAt(0).getTop() >= stickyHeaderTop();
final boolean isHeaderPositionOutsideAdapterRange = headerPosition > adapterCount - 1
|| headerPosition < 0;
if (!doesListHaveChildren || isHeaderPositionOutsideAdapterRange || isFirstViewBelowTop) {
clearHeader();
return;
}
updateHeader(headerPosition);
}
private void updateHeader(int headerPosition) {
// check if there is a new header should be sticky
if (mHeaderPosition == null || mHeaderPosition != headerPosition) {
mHeaderPosition = headerPosition;
final long headerId = mAdapter.getHeaderId(headerPosition);
if (mHeaderId == null || mHeaderId != headerId) {
mHeaderId = headerId;
final View header = mAdapter.getHeaderView(mHeaderPosition, mHeader, this);
if (mHeader != header) {
if (header == null) {
throw new NullPointerException("header may not be null");
}
swapHeader(header);
}
ensureHeaderHasCorrectLayoutParams(mHeader);
measureHeader(mHeader);
if(mOnStickyHeaderChangedListener != null) {
mOnStickyHeaderChangedListener.onStickyHeaderChanged(this, mHeader, headerPosition, mHeaderId);
}
// Reset mHeaderOffset to null ensuring
// that it will be set on the header and
// not skipped for performance reasons.
mHeaderOffset = null;
}
}
int headerOffset = 0;
// Calculate new header offset
// Skip looking at the first view. it never matters because it always
// results in a headerOffset = 0
int headerBottom = mHeader.getMeasuredHeight() + stickyHeaderTop();
for (int i = 0; i < mList.getChildCount(); i++) {
final View child = mList.getChildAt(i);
final boolean doesChildHaveHeader = child instanceof WrapperView && ((WrapperView) child).hasHeader();
final boolean isChildFooter = mList.containsFooterView(child);
if (child.getTop() >= stickyHeaderTop() && (doesChildHaveHeader || isChildFooter)) {
headerOffset = Math.min(child.getTop() - headerBottom, 0);
break;
}
}
setHeaderOffet(headerOffset);
if (!mIsDrawingListUnderStickyHeader) {
mList.setTopClippingLength(mHeader.getMeasuredHeight()
+ mHeaderOffset);
}
updateHeaderVisibilities();
}
private void swapHeader(View newHeader) {
if (mHeader != null) {
removeView(mHeader);
}
mHeader = newHeader;
addView(mHeader);
if (mOnHeaderClickListener != null) {
mHeader.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mOnHeaderClickListener.onHeaderClick(
StickyListHeadersListView.this, mHeader,
mHeaderPosition, mHeaderId, true);
}
});
}
mHeader.setClickable(true);
}
// hides the headers in the list under the sticky header.
// Makes sure the other ones are showing
private void updateHeaderVisibilities() {
int top;
if (mHeader != null) {
top = mHeader.getMeasuredHeight() + (mHeaderOffset != null ? mHeaderOffset : 0) + mStickyHeaderTopOffset;
} else {
top = stickyHeaderTop();
}
int childCount = mList.getChildCount();
for (int i = 0; i < childCount; i++) {
// ensure child is a wrapper view
View child = mList.getChildAt(i);
if (!(child instanceof WrapperView)) {
continue;
}
// ensure wrapper view child has a header
WrapperView wrapperViewChild = (WrapperView) child;
if (!wrapperViewChild.hasHeader()) {
continue;
}
// update header views visibility
View childHeader = wrapperViewChild.mHeader;
if (wrapperViewChild.getTop() < top) {
if (childHeader.getVisibility() != View.INVISIBLE) {
childHeader.setVisibility(View.INVISIBLE);
}
} else {
if (childHeader.getVisibility() != View.VISIBLE) {
childHeader.setVisibility(View.VISIBLE);
}
}
}
}
// Wrapper around setting the header offset in different ways depending on
// the API version
@SuppressLint("NewApi")
private void setHeaderOffet(int offset) {
if (mHeaderOffset == null || mHeaderOffset != offset) {
mHeaderOffset = offset;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mHeader.setTranslationY(mHeaderOffset);
} else {
MarginLayoutParams params = (MarginLayoutParams) mHeader.getLayoutParams();
params.topMargin = mHeaderOffset;
mHeader.setLayoutParams(params);
}
if (mOnStickyHeaderOffsetChangedListener != null) {
mOnStickyHeaderOffsetChangedListener.onStickyHeaderOffsetChanged(this, mHeader, -mHeaderOffset);
}
}
}
private class AdapterWrapperDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
clearHeader();
}
@Override
public void onInvalidated() {
clearHeader();
}
}
private class WrapperListScrollListener implements OnScrollListener {
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (mOnScrollListenerDelegate != null) {
mOnScrollListenerDelegate.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
updateOrClearHeader(mList.getFixedFirstVisibleItem());
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mOnScrollListenerDelegate != null) {
mOnScrollListenerDelegate.onScrollStateChanged(view,
scrollState);
}
}
}
private class WrapperViewListLifeCycleListener implements LifeCycleListener {
@Override
public void onDispatchDrawOccurred(Canvas canvas) {
// onScroll is not called often at all before froyo
// therefor we need to update the header here as well.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
updateOrClearHeader(mList.getFixedFirstVisibleItem());
}
if (mHeader != null) {
if (mClippingToPadding) {
canvas.save();
canvas.clipRect(0, mPaddingTop, getRight(), getBottom());
drawChild(canvas, mHeader, 0);
canvas.restore();
} else {
drawChild(canvas, mHeader, 0);
}
}
}
}
private class AdapterWrapperHeaderClickHandler implements
AdapterWrapper.OnHeaderClickListener {
@Override
public void onHeaderClick(View header, int itemPosition, long headerId) {
mOnHeaderClickListener.onHeaderClick(
StickyListHeadersListView.this, header, itemPosition,
headerId, false);
}
}
private boolean isStartOfSection(int position) {
return position == 0 || mAdapter.getHeaderId(position) != mAdapter.getHeaderId(position - 1);
}
public int getHeaderOverlap(int position) {
boolean isStartOfSection = isStartOfSection(Math.max(0, position - getHeaderViewsCount()));
if (!isStartOfSection) {
View header = mAdapter.getHeaderView(position, null, mList);
if (header == null) {
throw new NullPointerException("header may not be null");
}
ensureHeaderHasCorrectLayoutParams(header);
measureHeader(header);
return header.getMeasuredHeight();
}
return 0;
}
private int stickyHeaderTop() {
return mStickyHeaderTopOffset + (mClippingToPadding ? mPaddingTop : 0);
}
/* ---------- StickyListHeaders specific API ---------- */
public void setAreHeadersSticky(boolean areHeadersSticky) {
mAreHeadersSticky = areHeadersSticky;
if (!areHeadersSticky) {
clearHeader();
} else {
updateOrClearHeader(mList.getFixedFirstVisibleItem());
}
// invalidating the list will trigger dispatchDraw()
mList.invalidate();
}
public boolean areHeadersSticky() {
return mAreHeadersSticky;
}
/**
* Use areHeadersSticky() method instead
*/
@Deprecated
public boolean getAreHeadersSticky() {
return areHeadersSticky();
}
/**
*
* @param stickyHeaderTopOffset
* The offset of the sticky header fom the top of the view
*/
public void setStickyHeaderTopOffset(int stickyHeaderTopOffset) {
mStickyHeaderTopOffset = stickyHeaderTopOffset;
updateOrClearHeader(mList.getFixedFirstVisibleItem());
}
public int getStickyHeaderTopOffset() {
return mStickyHeaderTopOffset;
}
public void setDrawingListUnderStickyHeader(
boolean drawingListUnderStickyHeader) {
mIsDrawingListUnderStickyHeader = drawingListUnderStickyHeader;
// reset the top clipping length
mList.setTopClippingLength(0);
}
public boolean isDrawingListUnderStickyHeader() {
return mIsDrawingListUnderStickyHeader;
}
public void setOnHeaderClickListener(OnHeaderClickListener listener) {
mOnHeaderClickListener = listener;
if (mAdapter != null) {
if (mOnHeaderClickListener != null) {
mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
if (mHeader != null) {
mHeader.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mOnHeaderClickListener.onHeaderClick(
StickyListHeadersListView.this, mHeader,
mHeaderPosition, mHeaderId, true);
}
});
}
} else {
mAdapter.setOnHeaderClickListener(null);
}
}
}
public void setOnStickyHeaderOffsetChangedListener(OnStickyHeaderOffsetChangedListener listener) {
mOnStickyHeaderOffsetChangedListener = listener;
}
public void setOnStickyHeaderChangedListener(OnStickyHeaderChangedListener listener) {
mOnStickyHeaderChangedListener = listener;
}
public View getListChildAt(int index) {
return mList.getChildAt(index);
}
public int getListChildCount() {
return mList.getChildCount();
}
/**
* Use the method with extreme caution!! Changing any values on the
* underlying ListView might break everything.
*
* @return the ListView backing this view.
*/
public ListView getWrappedList() {
return mList;
}
private boolean requireSdkVersion(int versionCode) {
if (Build.VERSION.SDK_INT < versionCode) {
Log.e("StickyListHeaders", "Api lvl must be at least "+versionCode+" to call this method");
return false;
}
return true;
}
/* ---------- ListView delegate methods ---------- */
public void setAdapter(StickyListHeadersAdapter adapter) {
if (adapter == null) {
mList.setAdapter(null);
clearHeader();
return;
}
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
if (adapter instanceof SectionIndexer) {
mAdapter = new SectionIndexerAdapterWrapper(getContext(), adapter);
} else {
mAdapter = new AdapterWrapper(getContext(), adapter);
}
mDataSetObserver = new AdapterWrapperDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
if (mOnHeaderClickListener != null) {
mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
} else {
mAdapter.setOnHeaderClickListener(null);
}
mAdapter.setDivider(mDivider, mDividerHeight);
mList.setAdapter(mAdapter);
clearHeader();
}
public StickyListHeadersAdapter getAdapter() {
return mAdapter == null ? null : mAdapter.mDelegate;
}
public void setDivider(Drawable divider) {
mDivider = divider;
if (mAdapter != null) {
mAdapter.setDivider(mDivider, mDividerHeight);
}
}
public void setDividerHeight(int dividerHeight) {
mDividerHeight = dividerHeight;
if (mAdapter != null) {
mAdapter.setDivider(mDivider, mDividerHeight);
}
}
public Drawable getDivider() {
return mDivider;
}
public int getDividerHeight() {
return mDividerHeight;
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
mOnScrollListenerDelegate = onScrollListener;
}
@Override
public void setOnTouchListener(final OnTouchListener l) {
if (l != null) {
mList.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return l.onTouch(StickyListHeadersListView.this, event);
}
});
} else {
mList.setOnTouchListener(null);
}
}
public void setOnItemClickListener(OnItemClickListener listener) {
mList.setOnItemClickListener(listener);
}
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
mList.setOnItemLongClickListener(listener);
}
public void addHeaderView(View v, Object data, boolean isSelectable) {
mList.addHeaderView(v, data, isSelectable);
}
public void addHeaderView(View v) {
mList.addHeaderView(v);
}
public void removeHeaderView(View v) {
mList.removeHeaderView(v);
}
public int getHeaderViewsCount() {
return mList.getHeaderViewsCount();
}
public void addFooterView(View v, Object data, boolean isSelectable) {
mList.addFooterView(v, data, isSelectable);
}
public void addFooterView(View v) {
mList.addFooterView(v);
}
public void removeFooterView(View v) {
mList.removeFooterView(v);
}
public int getFooterViewsCount() {
return mList.getFooterViewsCount();
}
public void setEmptyView(View v) {
mList.setEmptyView(v);
}
public View getEmptyView() {
return mList.getEmptyView();
}
@Override
public boolean isVerticalScrollBarEnabled() {
return mList.isVerticalScrollBarEnabled();
}
@Override
public boolean isHorizontalScrollBarEnabled() {
return mList.isHorizontalScrollBarEnabled();
}
@Override
public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) {
mList.setVerticalScrollBarEnabled(verticalScrollBarEnabled);
}
@Override
public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
mList.setHorizontalScrollBarEnabled(horizontalScrollBarEnabled);
}
@Override
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public int getOverScrollMode() {
if (requireSdkVersion(Build.VERSION_CODES.GINGERBREAD)) {
return mList.getOverScrollMode();
}
return 0;
}
@Override
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public void setOverScrollMode(int mode) {
if (requireSdkVersion(Build.VERSION_CODES.GINGERBREAD)) {
if (mList != null) {
mList.setOverScrollMode(mode);
}
}
}
@TargetApi(Build.VERSION_CODES.FROYO)
public void smoothScrollBy(int distance, int duration) {
if (requireSdkVersion(Build.VERSION_CODES.FROYO)) {
mList.smoothScrollBy(distance, duration);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void smoothScrollByOffset(int offset) {
if (requireSdkVersion(Build.VERSION_CODES.HONEYCOMB)) {
mList.smoothScrollByOffset(offset);
}
}
@SuppressLint("NewApi")
@TargetApi(Build.VERSION_CODES.FROYO)
public void smoothScrollToPosition(int position) {
if (requireSdkVersion(Build.VERSION_CODES.FROYO)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
mList.smoothScrollToPosition(position);
} else {
int offset = mAdapter == null ? 0 : getHeaderOverlap(position);
offset -= mClippingToPadding ? 0 : mPaddingTop;
mList.smoothScrollToPositionFromTop(position, offset);
}
}
}
@TargetApi(Build.VERSION_CODES.FROYO)
public void smoothScrollToPosition(int position, int boundPosition) {
if (requireSdkVersion(Build.VERSION_CODES.FROYO)) {
mList.smoothScrollToPosition(position, boundPosition);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void smoothScrollToPositionFromTop(int position, int offset) {
if (requireSdkVersion(Build.VERSION_CODES.HONEYCOMB)) {
offset += mAdapter == null ? 0 : getHeaderOverlap(position);
offset -= mClippingToPadding ? 0 : mPaddingTop;
mList.smoothScrollToPositionFromTop(position, offset);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void smoothScrollToPositionFromTop(int position, int offset,
int duration) {
if (requireSdkVersion(Build.VERSION_CODES.HONEYCOMB)) {
offset += mAdapter == null ? 0 : getHeaderOverlap(position);
offset -= mClippingToPadding ? 0 : mPaddingTop;
mList.smoothScrollToPositionFromTop(position, offset, duration);
}
}
public void setSelection(int position) {
setSelectionFromTop(position, 0);
}
public void setSelectionAfterHeaderView() {
mList.setSelectionAfterHeaderView();
}
public void setSelectionFromTop(int position, int y) {
y += mAdapter == null ? 0 : getHeaderOverlap(position);
y -= mClippingToPadding ? 0 : mPaddingTop;
mList.setSelectionFromTop(position, y);
}
public void setSelector(Drawable sel) {
mList.setSelector(sel);
}
public void setSelector(int resID) {
mList.setSelector(resID);
}
public int getFirstVisiblePosition() {
return mList.getFirstVisiblePosition();
}
public int getLastVisiblePosition() {
return mList.getLastVisiblePosition();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void setChoiceMode(int choiceMode) {
mList.setChoiceMode(choiceMode);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void setItemChecked(int position, boolean value) {
mList.setItemChecked(position, value);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public int getCheckedItemCount() {
if (requireSdkVersion(Build.VERSION_CODES.HONEYCOMB)) {
return mList.getCheckedItemCount();
}
return 0;
}
@TargetApi(Build.VERSION_CODES.FROYO)
public long[] getCheckedItemIds() {
if (requireSdkVersion(Build.VERSION_CODES.FROYO)) {
return mList.getCheckedItemIds();
}
return null;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public int getCheckedItemPosition() {
return mList.getCheckedItemPosition();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public SparseBooleanArray getCheckedItemPositions() {
return mList.getCheckedItemPositions();
}
public int getCount() {
return mList.getCount();
}
public Object getItemAtPosition(int position) {
return mList.getItemAtPosition(position);
}
public long getItemIdAtPosition(int position) {
return mList.getItemIdAtPosition(position);
}
@Override
public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
mList.setOnCreateContextMenuListener(l);
}
@Override
public boolean showContextMenu() {
return mList.showContextMenu();
}
public void invalidateViews() {
mList.invalidateViews();
}
@Override
public void setClipToPadding(boolean clipToPadding) {
if (mList != null) {
mList.setClipToPadding(clipToPadding);
}
mClippingToPadding = clipToPadding;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
mPaddingLeft = left;
mPaddingTop = top;
mPaddingRight = right;
mPaddingBottom = bottom;
if (mList != null) {
mList.setPadding(left, top, right, bottom);
}
super.setPadding(0, 0, 0, 0);
requestLayout();
}
/*
* Overrides an @hide method in View
*/
protected void recomputePadding() {
setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
}
@Override
public int getPaddingLeft() {
return mPaddingLeft;
}
@Override
public int getPaddingTop() {
return mPaddingTop;
}
@Override
public int getPaddingRight() {
return mPaddingRight;
}
@Override
public int getPaddingBottom() {
return mPaddingBottom;
}
public void setFastScrollEnabled(boolean fastScrollEnabled) {
mList.setFastScrollEnabled(fastScrollEnabled);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void setFastScrollAlwaysVisible(boolean alwaysVisible) {
if (requireSdkVersion(Build.VERSION_CODES.HONEYCOMB)) {
mList.setFastScrollAlwaysVisible(alwaysVisible);
}
}
/**
* @return true if the fast scroller will always show. False on pre-Honeycomb devices.
* @see android.widget.AbsListView#isFastScrollAlwaysVisible()
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public boolean isFastScrollAlwaysVisible() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return false;
}
return mList.isFastScrollAlwaysVisible();
}
public void setScrollBarStyle(int style) {
mList.setScrollBarStyle(style);
}
public int getScrollBarStyle() {
return mList.getScrollBarStyle();
}
public int getPositionForView(View view) {
return mList.getPositionForView(view);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
if (requireSdkVersion(Build.VERSION_CODES.HONEYCOMB)) {
mList.setMultiChoiceModeListener(listener);
}
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
if (superState != BaseSavedState.EMPTY_STATE) {
throw new IllegalStateException("Handling non empty state of parent class is not implemented");
}
return mList.onSaveInstanceState();
}
@Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(BaseSavedState.EMPTY_STATE);
mList.onRestoreInstanceState(state);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public boolean canScrollVertically(int direction) {
return mList.canScrollVertically(direction);
}
public void setTranscriptMode (int mode) {
mList.setTranscriptMode(mode);
}
public void setBlockLayoutChildren(boolean blockLayoutChildren) {
mList.setBlockLayoutChildren(blockLayoutChildren);
}
public void setStackFromBottom(boolean stackFromBottom){
mList.setStackFromBottom(stackFromBottom);
}
}