// hides the headers in the list under the sticky header. // Makes sure the other ones are showing private void updateHeaderVisibilities() { int 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); } } } }
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(); }
// 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++) { View child = mList.getChildAt(i); if (child instanceof WrapperView) { WrapperView wrapperViewChild = (WrapperView) child; if (wrapperViewChild.hasHeader()) { 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); } } } } } }
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() > 0; final boolean isFirstVisibleItemOutsideAdapterRange = realFirstVisibleItem > adapterCount - 1 || realFirstVisibleItem < 0; if (!doesListHaveChildren || isFirstVisibleItemOutsideAdapterRange || isFirstViewBelowTop) { clearHeader(); return; } updateHeader(realFirstVisibleItem); }
public void setAreHeadersSticky(boolean areHeadersSticky) { mAreHeadersSticky = areHeadersSticky; if (!areHeadersSticky) { clearHeader(); } else { updateOrClearHeader(mList.getFixedFirstVisibleItem()); } // invalidating the list will trigger dispatchDraw() mList.invalidate(); }
@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); } }
@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); mList.smoothScrollToPositionFromTop(position, offset); } }
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(); }
@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; mHeader.layout( mPaddingLeft, headerTop, mHeader.getMeasuredWidth() + mPaddingLeft, headerTop + mHeader.getMeasuredHeight()); } }
@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); } }
@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()); } }
/** * @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(); }
@Override public void setClipToPadding(boolean clipToPadding) { if (mList != null) { mList.setClipToPadding(clipToPadding); } mClippingToPadding = clipToPadding; }
@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 getCheckedItemCount() { if (requireSdkVersion(Build.VERSION_CODES.HONEYCOMB)) { return mList.getCheckedItemCount(); } return 0; }
@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); } }
@Override @TargetApi(Build.VERSION_CODES.GINGERBREAD) public int getOverScrollMode() { if (requireSdkVersion(Build.VERSION_CODES.GINGERBREAD)) { return mList.getOverScrollMode(); } return 0; }
@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 @TargetApi(Build.VERSION_CODES.GINGERBREAD) public void setOverScrollMode(int mode) { if (requireSdkVersion(Build.VERSION_CODES.GINGERBREAD)) { if (mList != null) { mList.setOverScrollMode(mode); } } }
@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(); }
// 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(); } }
@Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getAction() & MotionEvent.ACTION_MASK; if (action == MotionEvent.ACTION_DOWN) { mDownY = ev.getY(); mHeaderOwnsTouch = mHeader != null && mDownY <= mHeader.getHeight() + mHeaderOffset; } boolean handled; if (mHeaderOwnsTouch) { if (mHeader != null && Math.abs(mDownY - ev.getY()) <= mTouchSlop) { handled = mHeader.dispatchTouchEvent(ev); } else { if (mHeader != null) { MotionEvent cancelEvent = MotionEvent.obtain(ev); cancelEvent.setAction(MotionEvent.ACTION_CANCEL); mHeader.dispatchTouchEvent(cancelEvent); cancelEvent.recycle(); } MotionEvent downEvent = MotionEvent.obtain( ev.getDownTime(), ev.getEventTime(), ev.getAction(), ev.getX(), mDownY, ev.getMetaState()); downEvent.setAction(MotionEvent.ACTION_DOWN); handled = mList.dispatchTouchEvent(downEvent); downEvent.recycle(); mHeaderOwnsTouch = false; } } else { handled = mList.dispatchTouchEvent(ev); } return handled; }
public int getPositionForView(View view) { return mList.getPositionForView(view); }
public void setFastScrollEnabled(boolean fastScrollEnabled) { mList.setFastScrollEnabled(fastScrollEnabled); }
@TargetApi(Build.VERSION_CODES.HONEYCOMB) public StickyListHeadersListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Initialize the list mList = new WrapperViewList(context); mDivider = mList.getDivider(); mDividerHeight = mList.getDividerHeight(); // null out divider, dividers are handled by adapter so they look good // with headers mList.setDivider(null); mList.setDividerHeight(0); mList.setLifeCycleListener(new WrapperViewListLifeCycleListener()); mList.setOnScrollListener(new WrapperListScrollListener()); addView(mList); if (attrs != null) { TypedArray a = context .getTheme() .obtainStyledAttributes(attrs, R.styleable.StickyListHeadersListView, 0, 0); try { // Android attributes if (a.hasValue(R.styleable.StickyListHeadersListView_android_padding)) { int padding = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_padding, 0); mPaddingLeft = padding; mPaddingTop = padding; mPaddingRight = padding; mPaddingBottom = padding; } else { mPaddingLeft = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingLeft, 0); mPaddingTop = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingTop, 0); mPaddingRight = a.getDimensionPixelSize( R.styleable.StickyListHeadersListView_android_paddingRight, 0); mPaddingBottom = a.getDimensionPixelSize( R.styleable.StickyListHeadersListView_android_paddingBottom, 0); } 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())); mList.setScrollBarStyle( a.getInt(R.styleable.StickyListHeadersListView_android_scrollbarStyle, 0)); final Drawable selector = a.getDrawable(R.styleable.StickyListHeadersListView_android_listSelector); if (selector != null) { mList.setSelector(selector); } mList.setScrollingCacheEnabled( a.getBoolean( R.styleable.StickyListHeadersListView_android_scrollingCache, mList.isScrollingCacheEnabled())); final Drawable divider = a.getDrawable(R.styleable.StickyListHeadersListView_android_divider); if (divider != null) { mDivider = 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(); } } }
public void invalidateViews() { mList.invalidateViews(); }
public boolean showContextMenu() { return mList.showContextMenu(); }
public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) { mList.setOnCreateContextMenuListener(l); }
public long getItemIdAtPosition(int position) { return mList.getItemIdAtPosition(position); }
public Object getItemAtPosition(int position) { return mList.getItemAtPosition(position); }