3 * Copyright (C) 2013 The Android Open Source Project
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package third_parties
.in.srain
.cube
;
20 import android
.annotation
.TargetApi
;
21 import android
.content
.Context
;
22 import android
.database
.DataSetObservable
;
23 import android
.database
.DataSetObserver
;
24 import android
.os
.Build
;
25 import android
.util
.AttributeSet
;
26 import android
.util
.Log
;
27 import android
.view
.View
;
28 import android
.view
.ViewGroup
;
29 import android
.widget
.AdapterView
;
30 import android
.widget
.Filter
;
31 import android
.widget
.Filterable
;
32 import android
.widget
.FrameLayout
;
33 import android
.widget
.GridView
;
34 import android
.widget
.ListAdapter
;
35 import android
.widget
.WrapperListAdapter
;
37 import java
.lang
.reflect
.Field
;
38 import java
.util
.ArrayList
;
41 * A {@link android.widget.GridView} that supports adding header rows in a
42 * very similar way to {@link android.widget.ListView}.
43 * See {@link GridViewWithHeaderAndFooter#addHeaderView(View, Object, boolean)}
44 * See {@link GridViewWithHeaderAndFooter#addFooterView(View, Object, boolean)}
46 public class GridViewWithHeaderAndFooter
extends GridView
{
48 public static boolean DEBUG
= false
;
51 * A class that represents a fixed view in a list, for example a header at the top
52 * or a footer at the bottom.
54 private static class FixedViewInfo
{
56 * The view to add to the grid
59 public ViewGroup viewContainer
;
61 * The data backing the view. This is returned from {@link android.widget.ListAdapter#getItem(int)}.
65 * <code>true</code> if the fixed view should be selectable in the grid
67 public boolean isSelectable
;
70 private int mNumColumns
= AUTO_FIT
;
71 private View mViewForMeasureRowHeight
= null
;
72 private int mRowHeight
= -1;
73 private static final String LOG_TAG
= "grid-view-with-header-and-footer";
75 private ArrayList
<FixedViewInfo
> mHeaderViewInfos
= new ArrayList
<FixedViewInfo
>();
76 private ArrayList
<FixedViewInfo
> mFooterViewInfos
= new ArrayList
<FixedViewInfo
>();
78 private void initHeaderGridView() {
81 public GridViewWithHeaderAndFooter(Context context
) {
86 public GridViewWithHeaderAndFooter(Context context
, AttributeSet attrs
) {
87 super(context
, attrs
);
91 public GridViewWithHeaderAndFooter(Context context
, AttributeSet attrs
, int defStyle
) {
92 super(context
, attrs
, defStyle
);
97 protected void onMeasure(int widthMeasureSpec
, int heightMeasureSpec
) {
98 super.onMeasure(widthMeasureSpec
, heightMeasureSpec
);
99 ListAdapter adapter
= getAdapter();
100 if (adapter
!= null
&& adapter
instanceof HeaderViewGridAdapter
) {
101 ((HeaderViewGridAdapter
) adapter
).setNumColumns(getNumColumnsCompatible());
102 ((HeaderViewGridAdapter
) adapter
).setRowHeight(getRowHeight());
107 public void setClipChildren(boolean clipChildren
) {
108 // Ignore, since the header rows depend on not being clipped
112 * Do not call this method unless you know how it works.
114 * @param clipChildren
116 public void setClipChildrenSupper(boolean clipChildren
) {
117 super.setClipChildren(false
);
121 * Add a fixed view to appear at the top of the grid. If addHeaderView is
122 * called more than once, the views will appear in the order they were
123 * added. Views added using this call can take focus if they want.
125 * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
126 * the supplied cursor with one that will also account for header views.
128 * @param v The view to add.
130 public void addHeaderView(View v
) {
131 addHeaderView(v
, null
, true
);
135 * Add a fixed view to appear at the top of the grid. If addHeaderView is
136 * called more than once, the views will appear in the order they were
137 * added. Views added using this call can take focus if they want.
139 * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
140 * the supplied cursor with one that will also account for header views.
142 * @param v The view to add.
143 * @param data Data to associate with this view
144 * @param isSelectable whether the item is selectable
146 public void addHeaderView(View v
, Object data
, boolean isSelectable
) {
147 ListAdapter adapter
= getAdapter();
148 if (adapter
!= null
&& !(adapter
instanceof HeaderViewGridAdapter
)) {
149 throw new IllegalStateException(
150 "Cannot add header view to grid -- setAdapter has already been called.");
153 ViewGroup
.LayoutParams lyp
= v
.getLayoutParams();
155 FixedViewInfo info
= new FixedViewInfo();
156 FrameLayout fl
= new FullWidthFixedViewLayout(getContext());
159 v
.setLayoutParams(new FrameLayout
.LayoutParams(lyp
.width
, lyp
.height
));
160 fl
.setLayoutParams(new LayoutParams(lyp
.width
, lyp
.height
));
164 info
.viewContainer
= fl
;
166 info
.isSelectable
= isSelectable
;
167 mHeaderViewInfos
.add(info
);
168 // in the case of re-adding a header view, or adding one later on,
169 // we need to notify the observer
170 if (adapter
!= null
) {
171 ((HeaderViewGridAdapter
) adapter
).notifyDataSetChanged();
175 public void addFooterView(View v
) {
176 addFooterView(v
, null
, true
);
179 public void addFooterView(View v
, Object data
, boolean isSelectable
) {
180 ListAdapter mAdapter
= getAdapter();
181 if (mAdapter
!= null
&& !(mAdapter
instanceof HeaderViewGridAdapter
)) {
182 throw new IllegalStateException(
183 "Cannot add header view to grid -- setAdapter has already been called.");
186 ViewGroup
.LayoutParams lyp
= v
.getLayoutParams();
188 FixedViewInfo info
= new FixedViewInfo();
189 FrameLayout fl
= new FullWidthFixedViewLayout(getContext());
192 v
.setLayoutParams(new FrameLayout
.LayoutParams(lyp
.width
, lyp
.height
));
193 fl
.setLayoutParams(new LayoutParams(lyp
.width
, lyp
.height
));
197 info
.viewContainer
= fl
;
199 info
.isSelectable
= isSelectable
;
200 mFooterViewInfos
.add(info
);
202 if (mAdapter
!= null
) {
203 ((HeaderViewGridAdapter
) mAdapter
).notifyDataSetChanged();
207 public int getHeaderViewCount() {
208 return mHeaderViewInfos
.size();
211 public int getFooterViewCount() {
212 return mFooterViewInfos
.size();
216 * Removes a previously-added header view.
218 * @param v The view to remove
219 * @return true if the view was removed, false if the view was not a header
222 public boolean removeHeaderView(View v
) {
223 if (mHeaderViewInfos
.size() > 0) {
224 boolean result
= false
;
225 ListAdapter adapter
= getAdapter();
226 if (adapter
!= null
&& ((HeaderViewGridAdapter
) adapter
).removeHeader(v
)) {
229 removeFixedViewInfo(v
, mHeaderViewInfos
);
236 * Removes a previously-added footer view.
238 * @param v The view to remove
239 * @return true if the view was removed, false if the view was not a header
242 public boolean removeFooterView(View v
) {
243 if (mFooterViewInfos
.size() > 0) {
244 boolean result
= false
;
245 ListAdapter adapter
= getAdapter();
246 if (adapter
!= null
&& ((HeaderViewGridAdapter
) adapter
).removeFooter(v
)) {
249 removeFixedViewInfo(v
, mFooterViewInfos
);
255 private void removeFixedViewInfo(View v
, ArrayList
<FixedViewInfo
> where
) {
256 int len
= where
.size();
257 for (int i
= 0; i
< len
; ++i
) {
258 FixedViewInfo info
= where
.get(i
);
259 if (info
.view
== v
) {
267 private int getNumColumnsCompatible() {
268 if (Build
.VERSION
.SDK_INT
>= 11) {
269 return super.getNumColumns();
272 Field numColumns
= getClass().getSuperclass().getDeclaredField("mNumColumns");
273 numColumns
.setAccessible(true
);
274 return numColumns
.getInt(this);
275 } catch (Exception e
) {
276 if (mNumColumns
!= -1) {
279 throw new RuntimeException("Can not determine the mNumColumns for this API platform, please call setNumColumns to set it.");
285 private int getColumnWidthCompatible() {
286 if (Build
.VERSION
.SDK_INT
>= 16) {
287 return super.getColumnWidth();
290 Field numColumns
= getClass().getSuperclass().getDeclaredField("mColumnWidth");
291 numColumns
.setAccessible(true
);
292 return numColumns
.getInt(this);
293 } catch (NoSuchFieldException e
) {
294 throw new RuntimeException(e
);
295 } catch (IllegalAccessException e
) {
296 throw new RuntimeException(e
);
302 protected void onDetachedFromWindow() {
303 super.onDetachedFromWindow();
304 mViewForMeasureRowHeight
= null
;
307 public void invalidateRowHeight() {
311 public int getRowHeight() {
312 if (mRowHeight
> 0) {
315 ListAdapter adapter
= getAdapter();
316 int numColumns
= getNumColumnsCompatible();
318 // adapter has not been set or has no views in it;
319 if (adapter
== null
|| adapter
.getCount() <= numColumns
* (mHeaderViewInfos
.size() + mFooterViewInfos
.size())) {
322 int mColumnWidth
= getColumnWidthCompatible();
323 View view
= getAdapter().getView(numColumns
* mHeaderViewInfos
.size(), mViewForMeasureRowHeight
, this);
324 LayoutParams p
= (LayoutParams
) view
.getLayoutParams();
326 p
= new LayoutParams(-1, -2, 0);
327 view
.setLayoutParams(p
);
329 int childHeightSpec
= getChildMeasureSpec(
330 MeasureSpec
.makeMeasureSpec(0, MeasureSpec
.UNSPECIFIED
), 0, p
.height
);
331 int childWidthSpec
= getChildMeasureSpec(
332 MeasureSpec
.makeMeasureSpec(mColumnWidth
, MeasureSpec
.EXACTLY
), 0, p
.width
);
333 view
.measure(childWidthSpec
, childHeightSpec
);
334 mViewForMeasureRowHeight
= view
;
335 mRowHeight
= view
.getMeasuredHeight();
340 public void tryToScrollToBottomSmoothly() {
341 int lastPos
= getAdapter().getCount() - 1;
342 if (Build
.VERSION
.SDK_INT
>= 11) {
343 smoothScrollToPositionFromTop(lastPos
, 0);
345 setSelection(lastPos
);
350 public void tryToScrollToBottomSmoothly(int duration
) {
351 int lastPos
= getAdapter().getCount() - 1;
352 if (Build
.VERSION
.SDK_INT
>= 11) {
353 smoothScrollToPositionFromTop(lastPos
, 0, duration
);
355 setSelection(lastPos
);
360 public void setAdapter(ListAdapter adapter
) {
361 if (mHeaderViewInfos
.size() > 0 || mFooterViewInfos
.size() > 0) {
362 HeaderViewGridAdapter headerViewGridAdapter
= new HeaderViewGridAdapter(mHeaderViewInfos
, mFooterViewInfos
, adapter
);
363 int numColumns
= getNumColumnsCompatible();
364 if (numColumns
> 1) {
365 headerViewGridAdapter
.setNumColumns(numColumns
);
367 headerViewGridAdapter
.setRowHeight(getRowHeight());
368 super.setAdapter(headerViewGridAdapter
);
370 super.setAdapter(adapter
);
377 private class FullWidthFixedViewLayout
extends FrameLayout
{
379 public FullWidthFixedViewLayout(Context context
) {
384 protected void onLayout(boolean changed
, int left
, int top
, int right
, int bottom
) {
385 int realLeft
= GridViewWithHeaderAndFooter
.this.getPaddingLeft() + getPaddingLeft();
386 // Try to make where it should be, from left, full width
387 if (realLeft
!= left
) {
388 offsetLeftAndRight(realLeft
- left
);
390 super.onLayout(changed
, left
, top
, right
, bottom
);
394 protected void onMeasure(int widthMeasureSpec
, int heightMeasureSpec
) {
395 int targetWidth
= GridViewWithHeaderAndFooter
.this.getMeasuredWidth()
396 - GridViewWithHeaderAndFooter
.this.getPaddingLeft()
397 - GridViewWithHeaderAndFooter
.this.getPaddingRight();
398 widthMeasureSpec
= MeasureSpec
.makeMeasureSpec(targetWidth
,
399 MeasureSpec
.getMode(widthMeasureSpec
));
400 super.onMeasure(widthMeasureSpec
, heightMeasureSpec
);
405 public void setNumColumns(int numColumns
) {
406 super.setNumColumns(numColumns
);
407 mNumColumns
= numColumns
;
408 ListAdapter adapter
= getAdapter();
409 if (adapter
!= null
&& adapter
instanceof HeaderViewGridAdapter
) {
410 ((HeaderViewGridAdapter
) adapter
).setNumColumns(numColumns
);
415 * ListAdapter used when a HeaderGridView has header views. This ListAdapter
416 * wraps another one and also keeps track of the header views and their
417 * associated data objects.
418 * <p>This is intended as a base class; you will probably not need to
419 * use this class directly in your own code.
421 private static class HeaderViewGridAdapter
implements WrapperListAdapter
, Filterable
{
422 // This is used to notify the container of updates relating to number of columns
423 // or headers changing, which changes the number of placeholders needed
424 private final DataSetObservable mDataSetObservable
= new DataSetObservable();
425 private final ListAdapter mAdapter
;
426 static final ArrayList
<FixedViewInfo
> EMPTY_INFO_LIST
=
427 new ArrayList
<FixedViewInfo
>();
429 // This ArrayList is assumed to NOT be null.
430 ArrayList
<FixedViewInfo
> mHeaderViewInfos
;
431 ArrayList
<FixedViewInfo
> mFooterViewInfos
;
432 private int mNumColumns
= 1;
433 private int mRowHeight
= -1;
434 boolean mAreAllFixedViewsSelectable
;
435 private final boolean mIsFilterable
;
436 private boolean mCachePlaceHoldView
= true
;
437 // From Recycle Bin or calling getView, this a question...
438 private boolean mCacheFirstHeaderView
= false
;
440 public HeaderViewGridAdapter(ArrayList
<FixedViewInfo
> headerViewInfos
, ArrayList
<FixedViewInfo
> footViewInfos
, ListAdapter adapter
) {
442 mIsFilterable
= adapter
instanceof Filterable
;
443 if (headerViewInfos
== null
) {
444 mHeaderViewInfos
= EMPTY_INFO_LIST
;
446 mHeaderViewInfos
= headerViewInfos
;
449 if (footViewInfos
== null
) {
450 mFooterViewInfos
= EMPTY_INFO_LIST
;
452 mFooterViewInfos
= footViewInfos
;
454 mAreAllFixedViewsSelectable
= areAllListInfosSelectable(mHeaderViewInfos
)
455 && areAllListInfosSelectable(mFooterViewInfos
);
458 public void setNumColumns(int numColumns
) {
459 if (numColumns
< 1) {
462 if (mNumColumns
!= numColumns
) {
463 mNumColumns
= numColumns
;
464 notifyDataSetChanged();
468 public void setRowHeight(int height
) {
472 public int getHeadersCount() {
473 return mHeaderViewInfos
.size();
476 public int getFootersCount() {
477 return mFooterViewInfos
.size();
481 public boolean isEmpty() {
482 return (mAdapter
== null
|| mAdapter
.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0;
485 private boolean areAllListInfosSelectable(ArrayList
<FixedViewInfo
> infos
) {
487 for (FixedViewInfo info
: infos
) {
488 if (!info
.isSelectable
) {
496 public boolean removeHeader(View v
) {
497 for (int i
= 0; i
< mHeaderViewInfos
.size(); i
++) {
498 FixedViewInfo info
= mHeaderViewInfos
.get(i
);
499 if (info
.view
== v
) {
500 mHeaderViewInfos
.remove(i
);
501 mAreAllFixedViewsSelectable
=
502 areAllListInfosSelectable(mHeaderViewInfos
) && areAllListInfosSelectable(mFooterViewInfos
);
503 mDataSetObservable
.notifyChanged();
510 public boolean removeFooter(View v
) {
511 for (int i
= 0; i
< mFooterViewInfos
.size(); i
++) {
512 FixedViewInfo info
= mFooterViewInfos
.get(i
);
513 if (info
.view
== v
) {
514 mFooterViewInfos
.remove(i
);
515 mAreAllFixedViewsSelectable
=
516 areAllListInfosSelectable(mHeaderViewInfos
) && areAllListInfosSelectable(mFooterViewInfos
);
517 mDataSetObservable
.notifyChanged();
525 public int getCount() {
526 if (mAdapter
!= null
) {
527 return (getFootersCount() + getHeadersCount()) * mNumColumns
+ getAdapterAndPlaceHolderCount();
529 return (getFootersCount() + getHeadersCount()) * mNumColumns
;
534 public boolean areAllItemsEnabled() {
535 if (mAdapter
!= null
) {
536 return mAreAllFixedViewsSelectable
&& mAdapter
.areAllItemsEnabled();
542 private int getAdapterAndPlaceHolderCount() {
543 final int adapterCount
= (int) (Math
.ceil(1f
* mAdapter
.getCount() / mNumColumns
) * mNumColumns
);
548 public boolean isEnabled(int position
) {
549 // Header (negative positions will throw an IndexOutOfBoundsException)
550 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
551 if (position
< numHeadersAndPlaceholders
) {
552 return position
% mNumColumns
== 0
553 && mHeaderViewInfos
.get(position
/ mNumColumns
).isSelectable
;
557 final int adjPosition
= position
- numHeadersAndPlaceholders
;
558 int adapterCount
= 0;
559 if (mAdapter
!= null
) {
560 adapterCount
= getAdapterAndPlaceHolderCount();
561 if (adjPosition
< adapterCount
) {
562 return adjPosition
< mAdapter
.getCount() && mAdapter
.isEnabled(adjPosition
);
566 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
567 final int footerPosition
= adjPosition
- adapterCount
;
568 return footerPosition
% mNumColumns
== 0
569 && mFooterViewInfos
.get(footerPosition
/ mNumColumns
).isSelectable
;
573 public Object
getItem(int position
) {
574 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
575 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
576 if (position
< numHeadersAndPlaceholders
) {
577 if (position
% mNumColumns
== 0) {
578 return mHeaderViewInfos
.get(position
/ mNumColumns
).data
;
584 final int adjPosition
= position
- numHeadersAndPlaceholders
;
585 int adapterCount
= 0;
586 if (mAdapter
!= null
) {
587 adapterCount
= getAdapterAndPlaceHolderCount();
588 if (adjPosition
< adapterCount
) {
589 if (adjPosition
< mAdapter
.getCount()) {
590 return mAdapter
.getItem(adjPosition
);
597 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
598 final int footerPosition
= adjPosition
- adapterCount
;
599 if (footerPosition
% mNumColumns
== 0) {
600 return mFooterViewInfos
.get(footerPosition
).data
;
607 public long getItemId(int position
) {
608 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
609 if (mAdapter
!= null
&& position
>= numHeadersAndPlaceholders
) {
610 int adjPosition
= position
- numHeadersAndPlaceholders
;
611 int adapterCount
= mAdapter
.getCount();
612 if (adjPosition
< adapterCount
) {
613 return mAdapter
.getItemId(adjPosition
);
620 public boolean hasStableIds() {
621 if (mAdapter
!= null
) {
622 return mAdapter
.hasStableIds();
628 public View
getView(int position
, View convertView
, ViewGroup parent
) {
630 Log
.d(LOG_TAG
, String
.format("getView: %s, reused: %s", position
, convertView
== null
));
632 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
633 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
634 if (position
< numHeadersAndPlaceholders
) {
635 View headerViewContainer
= mHeaderViewInfos
636 .get(position
/ mNumColumns
).viewContainer
;
637 if (position
% mNumColumns
== 0) {
638 return headerViewContainer
;
640 if (convertView
== null
) {
641 convertView
= new View(parent
.getContext());
643 // We need to do this because GridView uses the height of the last item
644 // in a row to determine the height for the entire row.
645 convertView
.setVisibility(View
.INVISIBLE
);
646 convertView
.setMinimumHeight(headerViewContainer
.getHeight());
651 final int adjPosition
= position
- numHeadersAndPlaceholders
;
652 int adapterCount
= 0;
653 if (mAdapter
!= null
) {
654 adapterCount
= getAdapterAndPlaceHolderCount();
655 if (adjPosition
< adapterCount
) {
656 if (adjPosition
< mAdapter
.getCount()) {
657 View view
= mAdapter
.getView(adjPosition
, convertView
, parent
);
660 if (convertView
== null
) {
661 convertView
= new View(parent
.getContext());
663 convertView
.setVisibility(View
.INVISIBLE
);
664 convertView
.setMinimumHeight(mRowHeight
);
670 final int footerPosition
= adjPosition
- adapterCount
;
671 if (footerPosition
< getCount()) {
672 View footViewContainer
= mFooterViewInfos
673 .get(footerPosition
/ mNumColumns
).viewContainer
;
674 if (position
% mNumColumns
== 0) {
675 return footViewContainer
;
677 if (convertView
== null
) {
678 convertView
= new View(parent
.getContext());
680 // We need to do this because GridView uses the height of the last item
681 // in a row to determine the height for the entire row.
682 convertView
.setVisibility(View
.INVISIBLE
);
683 convertView
.setMinimumHeight(footViewContainer
.getHeight());
687 throw new ArrayIndexOutOfBoundsException(position
);
691 public int getItemViewType(int position
) {
693 final int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
694 final int adapterViewTypeStart
= mAdapter
== null ?
0 : mAdapter
.getViewTypeCount() - 1;
695 int type
= AdapterView
.ITEM_VIEW_TYPE_HEADER_OR_FOOTER
;
696 if (mCachePlaceHoldView
) {
698 if (position
< numHeadersAndPlaceholders
) {
700 if (mCacheFirstHeaderView
) {
701 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + mFooterViewInfos
.size() + 1 + 1;
704 if (position
% mNumColumns
!= 0) {
705 type
= adapterViewTypeStart
+ (position
/ mNumColumns
+ 1);
711 final int adjPosition
= position
- numHeadersAndPlaceholders
;
712 int adapterCount
= 0;
713 if (mAdapter
!= null
) {
714 adapterCount
= getAdapterAndPlaceHolderCount();
715 if (adjPosition
>= 0 && adjPosition
< adapterCount
) {
716 if (adjPosition
< mAdapter
.getCount()) {
717 type
= mAdapter
.getItemViewType(adjPosition
);
719 if (mCachePlaceHoldView
) {
720 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + 1;
726 if (mCachePlaceHoldView
) {
728 final int footerPosition
= adjPosition
- adapterCount
;
729 if (footerPosition
>= 0 && footerPosition
< getCount() && (footerPosition
% mNumColumns
) != 0) {
730 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + 1 + (footerPosition
/ mNumColumns
+ 1);
734 Log
.d(LOG_TAG
, String
.format("getItemViewType: pos: %s, result: %s", position
, type
, mCachePlaceHoldView
, mCacheFirstHeaderView
));
740 * content view, content view holder, header[0], header and footer placeholder(s)
745 public int getViewTypeCount() {
746 int count
= mAdapter
== null ?
1 : mAdapter
.getViewTypeCount();
747 if (mCachePlaceHoldView
) {
748 int offset
= mHeaderViewInfos
.size() + 1 + mFooterViewInfos
.size();
749 if (mCacheFirstHeaderView
) {
755 Log
.d(LOG_TAG
, String
.format("getViewTypeCount: %s", count
));
761 public void registerDataSetObserver(DataSetObserver observer
) {
762 mDataSetObservable
.registerObserver(observer
);
763 if (mAdapter
!= null
) {
764 mAdapter
.registerDataSetObserver(observer
);
769 public void unregisterDataSetObserver(DataSetObserver observer
) {
770 mDataSetObservable
.unregisterObserver(observer
);
771 if (mAdapter
!= null
) {
772 mAdapter
.unregisterDataSetObserver(observer
);
777 public Filter
getFilter() {
779 return ((Filterable
) mAdapter
).getFilter();
785 public ListAdapter
getWrappedAdapter() {
789 public void notifyDataSetChanged() {
790 mDataSetObservable
.notifyChanged();
796 * Sets the selected item and positions the selection y pixels from the top edge of the ListView.
797 * (If in touch mode, the item will not be selected but it will still be positioned appropriately.)
799 * @param position Index (starting at 0) of the data item to be selected.
800 * @param y The distance from the top edge of the ListView (plus padding)
801 * that the item will be positioned.
803 * @see <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/widget/ListView.java#ListView.setSelectionFromTop%28int%2Cint%29">Original code</a>
805 public void setSelectionFromTop(int position
, int y
) {
806 if (getAdapter() == null
) {
810 setSelection(position
);
811 //setSelectionInt(position);
813 /*if (!isInTouchMode()) {
814 position = super.lookForSelectablePosition(position, true);
816 setNextSelectedPositionInt(position);
819 mResurrectToPosition = position;
824 mLayoutMode = LAYOUT_SPECIFIC;
825 mSpecificTop = mListPadding.top + y;
828 mSyncPosition = position;
829 mSyncRowId = getAdapter().getItemId(position);
832 if (mPositionScroller != null) {
833 mPositionScroller.stop();