aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java')
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java925
1 files changed, 925 insertions, 0 deletions
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
new file mode 100644
index 000000000..476f6cfad
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
@@ -0,0 +1,925 @@
+package se.emilsjolander.stickylistheaders;
+
+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.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+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 it is called a ListView. This
+ * is because of 2 reasons. 1. It acts like as ListView 2. It used to be a
+ * ListView subclass and i did not was to change to name causing compatibility
+ * errors.
+ *
+ * @author Emil Sjölander
+ */
+public class StickyListHeadersListView extends FrameLayout {
+
+ public interface OnHeaderClickListener {
+ public 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.
+ */
+ public void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset);
+ }
+
+ /* --- 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 mPaddingLeft = 0;
+ private int mPaddingTop = 0;
+ private int mPaddingRight = 0;
+ private int mPaddingBottom = 0;
+
+ /* --- Other --- */
+ private OnHeaderClickListener mOnHeaderClickListener;
+ private OnStickyHeaderOffsetChangedListener mOnStickyHeaderOffsetChangedListener;
+ 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);
+
+ mList.setVerticalScrollBarEnabled(isVerticalScrollBarEnabled());
+ mList.setHorizontalScrollBarEnabled(isHorizontalScrollBarEnabled());
+
+ 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);
+
+ // -- 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);
+
+ // -- 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);
+ } else if (lp.height == LayoutParams.MATCH_PARENT) {
+ lp.height = LayoutParams.WRAP_CONTENT;
+ }
+ 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
+ + (mClippingToPadding ? mPaddingTop : 0);
+ // The left parameter must for some reason be set to 0.
+ // I think it should be set to mPaddingLeft but apparently not
+ 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).
+ 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();
+ final int realFirstVisibleItem = firstVisiblePosition - headerViewCount;
+
+ // 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() > (mClippingToPadding ? mPaddingTop : 0);
+ final boolean isFirstVisibleItemOutsideAdapterRange = realFirstVisibleItem > adapterCount - 1
+ || realFirstVisibleItem < 0;
+ if (!doesListHaveChildren || isFirstVisibleItemOutsideAdapterRange
+ || isFirstViewBelowTop) {
+ clearHeader();
+ return;
+ }
+
+ updateHeader(realFirstVisibleItem);
+ }
+
+ private void updateHeader(int firstVisiblePosition) {
+
+ // check if there is a new header should be sticky
+ if (mHeaderPosition == null || mHeaderPosition != firstVisiblePosition) {
+ mHeaderPosition = firstVisiblePosition;
+ final long headerId = mAdapter.getHeaderId(firstVisiblePosition);
+ 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);
+
+ // 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()
+ + (mClippingToPadding ? mPaddingTop : 0);
+ 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() >= (mClippingToPadding ? mPaddingTop : 0)
+ && (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);
+ mHeader.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mOnHeaderClickListener != null) {
+ mOnHeaderClickListener.onHeaderClick(
+ StickyListHeadersListView.this, mHeader,
+ mHeaderPosition, mHeaderId, 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);
+ } else {
+ top = mClippingToPadding ? mPaddingTop : 0;
+ }
+ 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);
+ }
+
+ private int getHeaderOverlap(int position) {
+ boolean isStartOfSection = isStartOfSection(position);
+ 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;
+ }
+
+ /* ---------- 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();
+ }
+
+ 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());
+ } else {
+ mAdapter.setOnHeaderClickListener(null);
+ }
+ }
+ }
+
+ public void setOnStickyHeaderOffsetChangedListener(OnStickyHeaderOffsetChangedListener listener) {
+ mOnStickyHeaderOffsetChangedListener = 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;
+ }
+
+ /* ---------- 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;
+ }
+
+ 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) {
+ 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 void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) {
+ mList.setVerticalScrollBarEnabled(verticalScrollBarEnabled);
+ }
+
+ @Override
+ public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
+ mList.setHorizontalScrollBarEnabled(horizontalScrollBarEnabled);
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollBy(int distance, int duration) {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ mList.smoothScrollBy(distance, duration);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollByOffset(int offset) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ mList.smoothScrollByOffset(offset);
+ }
+
+ @SuppressLint("NewApi")
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollToPosition(int position) {
+ 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) {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ mList.smoothScrollToPosition(position, boundPosition);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollToPositionFromTop(int position, int offset) {
+ 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) {
+ 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();
+ }
+
+ public void setChoiceMode(int choiceMode) {
+ mList.setChoiceMode(choiceMode);
+ }
+
+ public void setItemChecked(int position, boolean value) {
+ mList.setItemChecked(position, value);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public int getCheckedItemCount() {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ return mList.getCheckedItemCount();
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public long[] getCheckedItemIds() {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ return mList.getCheckedItemIds();
+ }
+
+ public int getCheckedItemPosition() {
+ return mList.getCheckedItemPosition();
+ }
+
+ 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);
+ }
+
+ /**
+ * @throws ApiLevelTooLowException on pre-Honeycomb device.
+ * @see android.widget.AbsListView#setFastScrollAlwaysVisible(boolean)
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void setFastScrollAlwaysVisible(boolean alwaysVisible) {
+ 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();
+ }
+
+ private void requireSdkVersion(int versionCode) {
+ if (Build.VERSION.SDK_INT < versionCode) {
+ throw new ApiLevelTooLowException(versionCode);
+ }
+ }
+
+ public int getPositionForView(View view) {
+ return mList.getPositionForView(view);
+ }
+
+}