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 invalidateRowHeight();
103 ((HeaderViewGridAdapter
) adapter
).setRowHeight(getRowHeight());
108 public void setClipChildren(boolean clipChildren
) {
109 // Ignore, since the header rows depend on not being clipped
113 * Do not call this method unless you know how it works.
115 * @param clipChildren
117 public void setClipChildrenSupper(boolean clipChildren
) {
118 super.setClipChildren(false
);
122 * Add a fixed view to appear at the top of the grid. If addHeaderView is
123 * called more than once, the views will appear in the order they were
124 * added. Views added using this call can take focus if they want.
126 * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
127 * the supplied cursor with one that will also account for header views.
129 * @param v The view to add.
131 public void addHeaderView(View v
) {
132 addHeaderView(v
, null
, true
);
136 * Add a fixed view to appear at the top of the grid. If addHeaderView is
137 * called more than once, the views will appear in the order they were
138 * added. Views added using this call can take focus if they want.
140 * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
141 * the supplied cursor with one that will also account for header views.
143 * @param v The view to add.
144 * @param data Data to associate with this view
145 * @param isSelectable whether the item is selectable
147 public void addHeaderView(View v
, Object data
, boolean isSelectable
) {
148 ListAdapter adapter
= getAdapter();
149 if (adapter
!= null
&& !(adapter
instanceof HeaderViewGridAdapter
)) {
150 throw new IllegalStateException(
151 "Cannot add header view to grid -- setAdapter has already been called.");
154 ViewGroup
.LayoutParams lyp
= v
.getLayoutParams();
156 FixedViewInfo info
= new FixedViewInfo();
157 FrameLayout fl
= new FullWidthFixedViewLayout(getContext());
160 v
.setLayoutParams(new FrameLayout
.LayoutParams(lyp
.width
, lyp
.height
));
161 fl
.setLayoutParams(new LayoutParams(lyp
.width
, lyp
.height
));
165 info
.viewContainer
= fl
;
167 info
.isSelectable
= isSelectable
;
168 mHeaderViewInfos
.add(info
);
169 // in the case of re-adding a header view, or adding one later on,
170 // we need to notify the observer
171 if (adapter
!= null
) {
172 ((HeaderViewGridAdapter
) adapter
).notifyDataSetChanged();
176 public void addFooterView(View v
) {
177 addFooterView(v
, null
, true
);
180 public void addFooterView(View v
, Object data
, boolean isSelectable
) {
181 ListAdapter mAdapter
= getAdapter();
182 if (mAdapter
!= null
&& !(mAdapter
instanceof HeaderViewGridAdapter
)) {
183 throw new IllegalStateException(
184 "Cannot add header view to grid -- setAdapter has already been called.");
187 ViewGroup
.LayoutParams lyp
= v
.getLayoutParams();
189 FixedViewInfo info
= new FixedViewInfo();
190 FrameLayout fl
= new FullWidthFixedViewLayout(getContext());
193 v
.setLayoutParams(new FrameLayout
.LayoutParams(lyp
.width
, lyp
.height
));
194 fl
.setLayoutParams(new LayoutParams(lyp
.width
, lyp
.height
));
198 info
.viewContainer
= fl
;
200 info
.isSelectable
= isSelectable
;
201 mFooterViewInfos
.add(info
);
203 if (mAdapter
!= null
) {
204 ((HeaderViewGridAdapter
) mAdapter
).notifyDataSetChanged();
208 public int getHeaderViewCount() {
209 return mHeaderViewInfos
.size();
212 public int getFooterViewCount() {
213 return mFooterViewInfos
.size();
217 * Removes a previously-added header view.
219 * @param v The view to remove
220 * @return true if the view was removed, false if the view was not a header
223 public boolean removeHeaderView(View v
) {
224 if (mHeaderViewInfos
.size() > 0) {
225 boolean result
= false
;
226 ListAdapter adapter
= getAdapter();
227 if (adapter
!= null
&& ((HeaderViewGridAdapter
) adapter
).removeHeader(v
)) {
230 removeFixedViewInfo(v
, mHeaderViewInfos
);
237 * Removes a previously-added footer view.
239 * @param v The view to remove
240 * @return true if the view was removed, false if the view was not a header
243 public boolean removeFooterView(View v
) {
244 if (mFooterViewInfos
.size() > 0) {
245 boolean result
= false
;
246 ListAdapter adapter
= getAdapter();
247 if (adapter
!= null
&& ((HeaderViewGridAdapter
) adapter
).removeFooter(v
)) {
250 removeFixedViewInfo(v
, mFooterViewInfos
);
256 private void removeFixedViewInfo(View v
, ArrayList
<FixedViewInfo
> where
) {
257 int len
= where
.size();
258 for (int i
= 0; i
< len
; ++i
) {
259 FixedViewInfo info
= where
.get(i
);
260 if (info
.view
== v
) {
268 private int getNumColumnsCompatible() {
269 if (Build
.VERSION
.SDK_INT
>= 11) {
270 return super.getNumColumns();
273 Field numColumns
= getClass().getSuperclass().getDeclaredField("mNumColumns");
274 numColumns
.setAccessible(true
);
275 return numColumns
.getInt(this);
276 } catch (Exception e
) {
277 if (mNumColumns
!= -1) {
280 throw new RuntimeException("Can not determine the mNumColumns for this API platform, please call setNumColumns to set it.");
286 private int getColumnWidthCompatible() {
287 if (Build
.VERSION
.SDK_INT
>= 16) {
288 return super.getColumnWidth();
291 Field numColumns
= getClass().getSuperclass().getDeclaredField("mColumnWidth");
292 numColumns
.setAccessible(true
);
293 return numColumns
.getInt(this);
294 } catch (NoSuchFieldException e
) {
295 throw new RuntimeException(e
);
296 } catch (IllegalAccessException e
) {
297 throw new RuntimeException(e
);
303 protected void onDetachedFromWindow() {
304 super.onDetachedFromWindow();
305 mViewForMeasureRowHeight
= null
;
308 public void invalidateRowHeight() {
312 public int getRowHeight() {
313 if (mRowHeight
> 0) {
316 ListAdapter adapter
= getAdapter();
317 int numColumns
= getNumColumnsCompatible();
319 // adapter has not been set or has no views in it;
320 if (adapter
== null
|| adapter
.getCount() <= numColumns
* (mHeaderViewInfos
.size() + mFooterViewInfos
.size())) {
323 int mColumnWidth
= getColumnWidthCompatible();
324 View view
= getAdapter().getView(numColumns
* mHeaderViewInfos
.size(), mViewForMeasureRowHeight
, this);
325 LayoutParams p
= (LayoutParams
) view
.getLayoutParams();
327 p
= new LayoutParams(-1, -2, 0);
328 view
.setLayoutParams(p
);
330 int childHeightSpec
= getChildMeasureSpec(
331 MeasureSpec
.makeMeasureSpec(0, MeasureSpec
.UNSPECIFIED
), 0, p
.height
);
332 int childWidthSpec
= getChildMeasureSpec(
333 MeasureSpec
.makeMeasureSpec(mColumnWidth
, MeasureSpec
.EXACTLY
), 0, p
.width
);
334 view
.measure(childWidthSpec
, childHeightSpec
);
335 mViewForMeasureRowHeight
= view
;
336 mRowHeight
= view
.getMeasuredHeight();
341 public void tryToScrollToBottomSmoothly() {
342 int lastPos
= getAdapter().getCount() - 1;
343 if (Build
.VERSION
.SDK_INT
>= 11) {
344 smoothScrollToPositionFromTop(lastPos
, 0);
346 setSelection(lastPos
);
351 public void tryToScrollToBottomSmoothly(int duration
) {
352 int lastPos
= getAdapter().getCount() - 1;
353 if (Build
.VERSION
.SDK_INT
>= 11) {
354 smoothScrollToPositionFromTop(lastPos
, 0, duration
);
356 setSelection(lastPos
);
361 public void setAdapter(ListAdapter adapter
) {
362 if (mHeaderViewInfos
.size() > 0 || mFooterViewInfos
.size() > 0) {
363 HeaderViewGridAdapter headerViewGridAdapter
= new HeaderViewGridAdapter(mHeaderViewInfos
, mFooterViewInfos
, adapter
);
364 int numColumns
= getNumColumnsCompatible();
365 if (numColumns
> 1) {
366 headerViewGridAdapter
.setNumColumns(numColumns
);
368 headerViewGridAdapter
.setRowHeight(getRowHeight());
369 super.setAdapter(headerViewGridAdapter
);
371 super.setAdapter(adapter
);
378 private class FullWidthFixedViewLayout
extends FrameLayout
{
380 public FullWidthFixedViewLayout(Context context
) {
385 protected void onLayout(boolean changed
, int left
, int top
, int right
, int bottom
) {
386 int realLeft
= GridViewWithHeaderAndFooter
.this.getPaddingLeft() + getPaddingLeft();
387 // Try to make where it should be, from left, full width
388 if (realLeft
!= left
) {
389 offsetLeftAndRight(realLeft
- left
);
391 super.onLayout(changed
, left
, top
, right
, bottom
);
395 protected void onMeasure(int widthMeasureSpec
, int heightMeasureSpec
) {
396 int targetWidth
= GridViewWithHeaderAndFooter
.this.getMeasuredWidth()
397 - GridViewWithHeaderAndFooter
.this.getPaddingLeft()
398 - GridViewWithHeaderAndFooter
.this.getPaddingRight();
399 widthMeasureSpec
= MeasureSpec
.makeMeasureSpec(targetWidth
,
400 MeasureSpec
.getMode(widthMeasureSpec
));
401 super.onMeasure(widthMeasureSpec
, heightMeasureSpec
);
406 public void setNumColumns(int numColumns
) {
407 super.setNumColumns(numColumns
);
408 mNumColumns
= numColumns
;
409 ListAdapter adapter
= getAdapter();
410 if (adapter
!= null
&& adapter
instanceof HeaderViewGridAdapter
) {
411 ((HeaderViewGridAdapter
) adapter
).setNumColumns(numColumns
);
416 * ListAdapter used when a HeaderGridView has header views. This ListAdapter
417 * wraps another one and also keeps track of the header views and their
418 * associated data objects.
419 * <p>This is intended as a base class; you will probably not need to
420 * use this class directly in your own code.
422 private static class HeaderViewGridAdapter
implements WrapperListAdapter
, Filterable
{
423 // This is used to notify the container of updates relating to number of columns
424 // or headers changing, which changes the number of placeholders needed
425 private final DataSetObservable mDataSetObservable
= new DataSetObservable();
426 private final ListAdapter mAdapter
;
427 static final ArrayList
<FixedViewInfo
> EMPTY_INFO_LIST
=
428 new ArrayList
<FixedViewInfo
>();
430 // This ArrayList is assumed to NOT be null.
431 ArrayList
<FixedViewInfo
> mHeaderViewInfos
;
432 ArrayList
<FixedViewInfo
> mFooterViewInfos
;
433 private int mNumColumns
= 1;
434 private int mRowHeight
= -1;
435 boolean mAreAllFixedViewsSelectable
;
436 private final boolean mIsFilterable
;
437 private boolean mCachePlaceHoldView
= true
;
438 // From Recycle Bin or calling getView, this a question...
439 private boolean mCacheFirstHeaderView
= false
;
441 public HeaderViewGridAdapter(ArrayList
<FixedViewInfo
> headerViewInfos
, ArrayList
<FixedViewInfo
> footViewInfos
, ListAdapter adapter
) {
443 mIsFilterable
= adapter
instanceof Filterable
;
444 if (headerViewInfos
== null
) {
445 mHeaderViewInfos
= EMPTY_INFO_LIST
;
447 mHeaderViewInfos
= headerViewInfos
;
450 if (footViewInfos
== null
) {
451 mFooterViewInfos
= EMPTY_INFO_LIST
;
453 mFooterViewInfos
= footViewInfos
;
455 mAreAllFixedViewsSelectable
= areAllListInfosSelectable(mHeaderViewInfos
)
456 && areAllListInfosSelectable(mFooterViewInfos
);
459 public void setNumColumns(int numColumns
) {
460 if (numColumns
< 1) {
463 if (mNumColumns
!= numColumns
) {
464 mNumColumns
= numColumns
;
465 notifyDataSetChanged();
469 public void setRowHeight(int height
) {
473 public int getHeadersCount() {
474 return mHeaderViewInfos
.size();
477 public int getFootersCount() {
478 return mFooterViewInfos
.size();
482 public boolean isEmpty() {
483 return (mAdapter
== null
|| mAdapter
.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0;
486 private boolean areAllListInfosSelectable(ArrayList
<FixedViewInfo
> infos
) {
488 for (FixedViewInfo info
: infos
) {
489 if (!info
.isSelectable
) {
497 public boolean removeHeader(View v
) {
498 for (int i
= 0; i
< mHeaderViewInfos
.size(); i
++) {
499 FixedViewInfo info
= mHeaderViewInfos
.get(i
);
500 if (info
.view
== v
) {
501 mHeaderViewInfos
.remove(i
);
502 mAreAllFixedViewsSelectable
=
503 areAllListInfosSelectable(mHeaderViewInfos
) && areAllListInfosSelectable(mFooterViewInfos
);
504 mDataSetObservable
.notifyChanged();
511 public boolean removeFooter(View v
) {
512 for (int i
= 0; i
< mFooterViewInfos
.size(); i
++) {
513 FixedViewInfo info
= mFooterViewInfos
.get(i
);
514 if (info
.view
== v
) {
515 mFooterViewInfos
.remove(i
);
516 mAreAllFixedViewsSelectable
=
517 areAllListInfosSelectable(mHeaderViewInfos
) && areAllListInfosSelectable(mFooterViewInfos
);
518 mDataSetObservable
.notifyChanged();
526 public int getCount() {
527 if (mAdapter
!= null
) {
528 return (getFootersCount() + getHeadersCount()) * mNumColumns
+ getAdapterAndPlaceHolderCount();
530 return (getFootersCount() + getHeadersCount()) * mNumColumns
;
535 public boolean areAllItemsEnabled() {
536 if (mAdapter
!= null
) {
537 return mAreAllFixedViewsSelectable
&& mAdapter
.areAllItemsEnabled();
543 private int getAdapterAndPlaceHolderCount() {
544 final int adapterCount
= (int) (Math
.ceil(1f
* mAdapter
.getCount() / mNumColumns
) * mNumColumns
);
549 public boolean isEnabled(int position
) {
550 // Header (negative positions will throw an IndexOutOfBoundsException)
551 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
552 if (position
< numHeadersAndPlaceholders
) {
553 return position
% mNumColumns
== 0
554 && mHeaderViewInfos
.get(position
/ mNumColumns
).isSelectable
;
558 final int adjPosition
= position
- numHeadersAndPlaceholders
;
559 int adapterCount
= 0;
560 if (mAdapter
!= null
) {
561 adapterCount
= getAdapterAndPlaceHolderCount();
562 if (adjPosition
< adapterCount
) {
563 return adjPosition
< mAdapter
.getCount() && mAdapter
.isEnabled(adjPosition
);
567 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
568 final int footerPosition
= adjPosition
- adapterCount
;
569 return footerPosition
% mNumColumns
== 0
570 && mFooterViewInfos
.get(footerPosition
/ mNumColumns
).isSelectable
;
574 public Object
getItem(int position
) {
575 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
576 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
577 if (position
< numHeadersAndPlaceholders
) {
578 if (position
% mNumColumns
== 0) {
579 return mHeaderViewInfos
.get(position
/ mNumColumns
).data
;
585 final int adjPosition
= position
- numHeadersAndPlaceholders
;
586 int adapterCount
= 0;
587 if (mAdapter
!= null
) {
588 adapterCount
= getAdapterAndPlaceHolderCount();
589 if (adjPosition
< adapterCount
) {
590 if (adjPosition
< mAdapter
.getCount()) {
591 return mAdapter
.getItem(adjPosition
);
598 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
599 final int footerPosition
= adjPosition
- adapterCount
;
600 if (footerPosition
% mNumColumns
== 0) {
601 return mFooterViewInfos
.get(footerPosition
).data
;
608 public long getItemId(int position
) {
609 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
610 if (mAdapter
!= null
&& position
>= numHeadersAndPlaceholders
) {
611 int adjPosition
= position
- numHeadersAndPlaceholders
;
612 int adapterCount
= mAdapter
.getCount();
613 if (adjPosition
< adapterCount
) {
614 return mAdapter
.getItemId(adjPosition
);
621 public boolean hasStableIds() {
622 if (mAdapter
!= null
) {
623 return mAdapter
.hasStableIds();
629 public View
getView(int position
, View convertView
, ViewGroup parent
) {
631 Log
.d(LOG_TAG
, String
.format("getView: %s, reused: %s", position
, convertView
== null
));
633 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
634 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
635 if (position
< numHeadersAndPlaceholders
) {
636 View headerViewContainer
= mHeaderViewInfos
637 .get(position
/ mNumColumns
).viewContainer
;
638 if (position
% mNumColumns
== 0) {
639 return headerViewContainer
;
641 if (convertView
== null
) {
642 convertView
= new View(parent
.getContext());
644 // We need to do this because GridView uses the height of the last item
645 // in a row to determine the height for the entire row.
646 convertView
.setVisibility(View
.INVISIBLE
);
647 convertView
.setMinimumHeight(headerViewContainer
.getHeight());
652 final int adjPosition
= position
- numHeadersAndPlaceholders
;
653 int adapterCount
= 0;
654 if (mAdapter
!= null
) {
655 adapterCount
= getAdapterAndPlaceHolderCount();
656 if (adjPosition
< adapterCount
) {
657 if (adjPosition
< mAdapter
.getCount()) {
658 View view
= mAdapter
.getView(adjPosition
, convertView
, parent
);
661 if (convertView
== null
) {
662 convertView
= new View(parent
.getContext());
664 convertView
.setVisibility(View
.INVISIBLE
);
665 convertView
.setMinimumHeight(mRowHeight
);
671 final int footerPosition
= adjPosition
- adapterCount
;
672 if (footerPosition
< getCount()) {
673 View footViewContainer
= mFooterViewInfos
674 .get(footerPosition
/ mNumColumns
).viewContainer
;
675 if (position
% mNumColumns
== 0) {
676 return footViewContainer
;
678 if (convertView
== null
) {
679 convertView
= new View(parent
.getContext());
681 // We need to do this because GridView uses the height of the last item
682 // in a row to determine the height for the entire row.
683 convertView
.setVisibility(View
.INVISIBLE
);
684 convertView
.setMinimumHeight(footViewContainer
.getHeight());
688 throw new ArrayIndexOutOfBoundsException(position
);
692 public int getItemViewType(int position
) {
694 final int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
695 final int adapterViewTypeStart
= mAdapter
== null ?
0 : mAdapter
.getViewTypeCount() - 1;
696 int type
= AdapterView
.ITEM_VIEW_TYPE_HEADER_OR_FOOTER
;
697 if (mCachePlaceHoldView
) {
699 if (position
< numHeadersAndPlaceholders
) {
701 if (mCacheFirstHeaderView
) {
702 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + mFooterViewInfos
.size() + 1 + 1;
705 if (position
% mNumColumns
!= 0) {
706 type
= adapterViewTypeStart
+ (position
/ mNumColumns
+ 1);
712 final int adjPosition
= position
- numHeadersAndPlaceholders
;
713 int adapterCount
= 0;
714 if (mAdapter
!= null
) {
715 adapterCount
= getAdapterAndPlaceHolderCount();
716 if (adjPosition
>= 0 && adjPosition
< adapterCount
) {
717 if (adjPosition
< mAdapter
.getCount()) {
718 type
= mAdapter
.getItemViewType(adjPosition
);
720 if (mCachePlaceHoldView
) {
721 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + 1;
727 if (mCachePlaceHoldView
) {
729 final int footerPosition
= adjPosition
- adapterCount
;
730 if (footerPosition
>= 0 && footerPosition
< getCount() && (footerPosition
% mNumColumns
) != 0) {
731 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + 1 + (footerPosition
/ mNumColumns
+ 1);
735 Log
.d(LOG_TAG
, String
.format("getItemViewType: pos: %s, result: %s", position
, type
, mCachePlaceHoldView
, mCacheFirstHeaderView
));
741 * content view, content view holder, header[0], header and footer placeholder(s)
746 public int getViewTypeCount() {
747 int count
= mAdapter
== null ?
1 : mAdapter
.getViewTypeCount();
748 if (mCachePlaceHoldView
) {
749 int offset
= mHeaderViewInfos
.size() + 1 + mFooterViewInfos
.size();
750 if (mCacheFirstHeaderView
) {
756 Log
.d(LOG_TAG
, String
.format("getViewTypeCount: %s", count
));
762 public void registerDataSetObserver(DataSetObserver observer
) {
763 mDataSetObservable
.registerObserver(observer
);
764 if (mAdapter
!= null
) {
765 mAdapter
.registerDataSetObserver(observer
);
770 public void unregisterDataSetObserver(DataSetObserver observer
) {
771 mDataSetObservable
.unregisterObserver(observer
);
772 if (mAdapter
!= null
) {
773 mAdapter
.unregisterDataSetObserver(observer
);
778 public Filter
getFilter() {
780 return ((Filterable
) mAdapter
).getFilter();
786 public ListAdapter
getWrappedAdapter() {
790 public void notifyDataSetChanged() {
791 mDataSetObservable
.notifyChanged();
797 * Sets the selected item and positions the selection y pixels from the top edge of the ListView.
798 * (If in touch mode, the item will not be selected but it will still be positioned appropriately.)
800 * @param position Index (starting at 0) of the data item to be selected.
801 * @param y The distance from the top edge of the ListView (plus padding)
802 * that the item will be positioned.
804 * @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>
806 public void setSelectionFromTop(int position
, int y
) {
807 if (getAdapter() == null
) {
811 setSelection(position
);
812 //setSelectionInt(position);
814 /*if (!isInTouchMode()) {
815 position = super.lookForSelectablePosition(position, true);
817 setNextSelectedPositionInt(position);
820 mResurrectToPosition = position;
825 mLayoutMode = LAYOUT_SPECIFIC;
826 mSpecificTop = mListPadding.top + y;
829 mSyncPosition = position;
830 mSyncRowId = getAdapter().getItemId(position);
833 if (mPositionScroller != null) {
834 mPositionScroller.stop();