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
.*;
31 import java
.lang
.reflect
.Field
;
32 import java
.util
.ArrayList
;
35 * A {@link GridView} that supports adding header rows in a
36 * very similar way to {@link android.widget.ListView}.
37 * See {@link GridViewWithHeaderAndFooter#addHeaderView(View, Object, boolean)}
38 * See {@link GridViewWithHeaderAndFooter#addFooterView(View, Object, boolean)}
40 public class GridViewWithHeaderAndFooter
extends GridView
{
42 public static boolean DEBUG
= false
;
45 * A class that represents a fixed view in a list, for example a header at the top
46 * or a footer at the bottom.
48 private static class FixedViewInfo
{
50 * The view to add to the grid
53 public ViewGroup viewContainer
;
55 * The data backing the view. This is returned from {@link ListAdapter#getItem(int)}.
59 * <code>true</code> if the fixed view should be selectable in the grid
61 public boolean isSelectable
;
64 private int mNumColumns
= AUTO_FIT
;
65 private View mViewForMeasureRowHeight
= null
;
66 private int mRowHeight
= -1;
67 private static final String LOG_TAG
= "grid-view-with-header-and-footer";
69 private ArrayList
<FixedViewInfo
> mHeaderViewInfos
= new ArrayList
<FixedViewInfo
>();
70 private ArrayList
<FixedViewInfo
> mFooterViewInfos
= new ArrayList
<FixedViewInfo
>();
72 private void initHeaderGridView() {
75 public GridViewWithHeaderAndFooter(Context context
) {
80 public GridViewWithHeaderAndFooter(Context context
, AttributeSet attrs
) {
81 super(context
, attrs
);
85 public GridViewWithHeaderAndFooter(Context context
, AttributeSet attrs
, int defStyle
) {
86 super(context
, attrs
, defStyle
);
91 protected void onMeasure(int widthMeasureSpec
, int heightMeasureSpec
) {
92 super.onMeasure(widthMeasureSpec
, heightMeasureSpec
);
93 ListAdapter adapter
= getAdapter();
94 if (adapter
!= null
&& adapter
instanceof HeaderViewGridAdapter
) {
95 ((HeaderViewGridAdapter
) adapter
).setNumColumns(getNumColumnsCompatible());
96 ((HeaderViewGridAdapter
) adapter
).setRowHeight(getRowHeight());
101 public void setClipChildren(boolean clipChildren
) {
102 // Ignore, since the header rows depend on not being clipped
106 * Do not call this method unless you know how it works.
108 * @param clipChildren
110 public void setClipChildrenSupper(boolean clipChildren
) {
111 super.setClipChildren(false
);
115 * Add a fixed view to appear at the top of the grid. If addHeaderView is
116 * called more than once, the views will appear in the order they were
117 * added. Views added using this call can take focus if they want.
119 * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
120 * the supplied cursor with one that will also account for header views.
122 * @param v The view to add.
124 public void addHeaderView(View v
) {
125 addHeaderView(v
, null
, true
);
129 * Add a fixed view to appear at the top of the grid. If addHeaderView is
130 * called more than once, the views will appear in the order they were
131 * added. Views added using this call can take focus if they want.
133 * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
134 * the supplied cursor with one that will also account for header views.
136 * @param v The view to add.
137 * @param data Data to associate with this view
138 * @param isSelectable whether the item is selectable
140 public void addHeaderView(View v
, Object data
, boolean isSelectable
) {
141 ListAdapter adapter
= getAdapter();
142 if (adapter
!= null
&& !(adapter
instanceof HeaderViewGridAdapter
)) {
143 throw new IllegalStateException(
144 "Cannot add header view to grid -- setAdapter has already been called.");
147 ViewGroup
.LayoutParams lyp
= v
.getLayoutParams();
149 FixedViewInfo info
= new FixedViewInfo();
150 FrameLayout fl
= new FullWidthFixedViewLayout(getContext());
153 v
.setLayoutParams(new FrameLayout
.LayoutParams(lyp
.width
, lyp
.height
));
154 fl
.setLayoutParams(new AbsListView
.LayoutParams(lyp
.width
, lyp
.height
));
158 info
.viewContainer
= fl
;
160 info
.isSelectable
= isSelectable
;
161 mHeaderViewInfos
.add(info
);
162 // in the case of re-adding a header view, or adding one later on,
163 // we need to notify the observer
164 if (adapter
!= null
) {
165 ((HeaderViewGridAdapter
) adapter
).notifyDataSetChanged();
169 public void addFooterView(View v
) {
170 addFooterView(v
, null
, true
);
173 public void addFooterView(View v
, Object data
, boolean isSelectable
) {
174 ListAdapter mAdapter
= getAdapter();
175 if (mAdapter
!= null
&& !(mAdapter
instanceof HeaderViewGridAdapter
)) {
176 throw new IllegalStateException(
177 "Cannot add header view to grid -- setAdapter has already been called.");
180 ViewGroup
.LayoutParams lyp
= v
.getLayoutParams();
182 FixedViewInfo info
= new FixedViewInfo();
183 FrameLayout fl
= new FullWidthFixedViewLayout(getContext());
186 v
.setLayoutParams(new FrameLayout
.LayoutParams(lyp
.width
, lyp
.height
));
187 fl
.setLayoutParams(new AbsListView
.LayoutParams(lyp
.width
, lyp
.height
));
191 info
.viewContainer
= fl
;
193 info
.isSelectable
= isSelectable
;
194 mFooterViewInfos
.add(info
);
196 if (mAdapter
!= null
) {
197 ((HeaderViewGridAdapter
) mAdapter
).notifyDataSetChanged();
201 public int getHeaderViewCount() {
202 return mHeaderViewInfos
.size();
205 public int getFooterViewCount() {
206 return mFooterViewInfos
.size();
210 * Removes a previously-added header view.
212 * @param v The view to remove
213 * @return true if the view was removed, false if the view was not a header
216 public boolean removeHeaderView(View v
) {
217 if (mHeaderViewInfos
.size() > 0) {
218 boolean result
= false
;
219 ListAdapter adapter
= getAdapter();
220 if (adapter
!= null
&& ((HeaderViewGridAdapter
) adapter
).removeHeader(v
)) {
223 removeFixedViewInfo(v
, mHeaderViewInfos
);
230 * Removes a previously-added footer view.
232 * @param v The view to remove
233 * @return true if the view was removed, false if the view was not a header
236 public boolean removeFooterView(View v
) {
237 if (mFooterViewInfos
.size() > 0) {
238 boolean result
= false
;
239 ListAdapter adapter
= getAdapter();
240 if (adapter
!= null
&& ((HeaderViewGridAdapter
) adapter
).removeFooter(v
)) {
243 removeFixedViewInfo(v
, mFooterViewInfos
);
249 private void removeFixedViewInfo(View v
, ArrayList
<FixedViewInfo
> where
) {
250 int len
= where
.size();
251 for (int i
= 0; i
< len
; ++i
) {
252 FixedViewInfo info
= where
.get(i
);
253 if (info
.view
== v
) {
261 private int getNumColumnsCompatible() {
262 if (Build
.VERSION
.SDK_INT
>= 11) {
263 return super.getNumColumns();
266 Field numColumns
= getClass().getSuperclass().getDeclaredField("mNumColumns");
267 numColumns
.setAccessible(true
);
268 return numColumns
.getInt(this);
269 } catch (Exception e
) {
270 if (mNumColumns
!= -1) {
273 throw new RuntimeException("Can not determine the mNumColumns for this API platform, please call setNumColumns to set it.");
279 private int getColumnWidthCompatible() {
280 if (Build
.VERSION
.SDK_INT
>= 16) {
281 return super.getColumnWidth();
284 Field numColumns
= getClass().getSuperclass().getDeclaredField("mColumnWidth");
285 numColumns
.setAccessible(true
);
286 return numColumns
.getInt(this);
287 } catch (NoSuchFieldException e
) {
288 throw new RuntimeException(e
);
289 } catch (IllegalAccessException e
) {
290 throw new RuntimeException(e
);
296 protected void onDetachedFromWindow() {
297 super.onDetachedFromWindow();
298 mViewForMeasureRowHeight
= null
;
301 public void invalidateRowHeight() {
305 public int getRowHeight() {
306 if (mRowHeight
> 0) {
309 ListAdapter adapter
= getAdapter();
310 int numColumns
= getNumColumnsCompatible();
312 // adapter has not been set or has no views in it;
313 if (adapter
== null
|| adapter
.getCount() <= numColumns
* (mHeaderViewInfos
.size() + mFooterViewInfos
.size())) {
316 int mColumnWidth
= getColumnWidthCompatible();
317 View view
= getAdapter().getView(numColumns
* mHeaderViewInfos
.size(), mViewForMeasureRowHeight
, this);
318 AbsListView
.LayoutParams p
= (AbsListView
.LayoutParams
) view
.getLayoutParams();
320 p
= new AbsListView
.LayoutParams(-1, -2, 0);
321 view
.setLayoutParams(p
);
323 int childHeightSpec
= getChildMeasureSpec(
324 MeasureSpec
.makeMeasureSpec(0, MeasureSpec
.UNSPECIFIED
), 0, p
.height
);
325 int childWidthSpec
= getChildMeasureSpec(
326 MeasureSpec
.makeMeasureSpec(mColumnWidth
, MeasureSpec
.EXACTLY
), 0, p
.width
);
327 view
.measure(childWidthSpec
, childHeightSpec
);
328 mViewForMeasureRowHeight
= view
;
329 mRowHeight
= view
.getMeasuredHeight();
334 public void tryToScrollToBottomSmoothly() {
335 int lastPos
= getAdapter().getCount() - 1;
336 if (Build
.VERSION
.SDK_INT
>= 11) {
337 smoothScrollToPositionFromTop(lastPos
, 0);
339 setSelection(lastPos
);
344 public void tryToScrollToBottomSmoothly(int duration
) {
345 int lastPos
= getAdapter().getCount() - 1;
346 if (Build
.VERSION
.SDK_INT
>= 11) {
347 smoothScrollToPositionFromTop(lastPos
, 0, duration
);
349 setSelection(lastPos
);
354 public void setAdapter(ListAdapter adapter
) {
355 if (mHeaderViewInfos
.size() > 0 || mFooterViewInfos
.size() > 0) {
356 HeaderViewGridAdapter headerViewGridAdapter
= new HeaderViewGridAdapter(mHeaderViewInfos
, mFooterViewInfos
, adapter
);
357 int numColumns
= getNumColumnsCompatible();
358 if (numColumns
> 1) {
359 headerViewGridAdapter
.setNumColumns(numColumns
);
361 headerViewGridAdapter
.setRowHeight(getRowHeight());
362 super.setAdapter(headerViewGridAdapter
);
364 super.setAdapter(adapter
);
371 private class FullWidthFixedViewLayout
extends FrameLayout
{
373 public FullWidthFixedViewLayout(Context context
) {
378 protected void onLayout(boolean changed
, int left
, int top
, int right
, int bottom
) {
379 int realLeft
= GridViewWithHeaderAndFooter
.this.getPaddingLeft() + getPaddingLeft();
380 // Try to make where it should be, from left, full width
381 if (realLeft
!= left
) {
382 offsetLeftAndRight(realLeft
- left
);
384 super.onLayout(changed
, left
, top
, right
, bottom
);
388 protected void onMeasure(int widthMeasureSpec
, int heightMeasureSpec
) {
389 int targetWidth
= GridViewWithHeaderAndFooter
.this.getMeasuredWidth()
390 - GridViewWithHeaderAndFooter
.this.getPaddingLeft()
391 - GridViewWithHeaderAndFooter
.this.getPaddingRight();
392 widthMeasureSpec
= MeasureSpec
.makeMeasureSpec(targetWidth
,
393 MeasureSpec
.getMode(widthMeasureSpec
));
394 super.onMeasure(widthMeasureSpec
, heightMeasureSpec
);
399 public void setNumColumns(int numColumns
) {
400 super.setNumColumns(numColumns
);
401 mNumColumns
= numColumns
;
402 ListAdapter adapter
= getAdapter();
403 if (adapter
!= null
&& adapter
instanceof HeaderViewGridAdapter
) {
404 ((HeaderViewGridAdapter
) adapter
).setNumColumns(numColumns
);
409 * ListAdapter used when a HeaderGridView has header views. This ListAdapter
410 * wraps another one and also keeps track of the header views and their
411 * associated data objects.
412 * <p>This is intended as a base class; you will probably not need to
413 * use this class directly in your own code.
415 private static class HeaderViewGridAdapter
implements WrapperListAdapter
, Filterable
{
416 // This is used to notify the container of updates relating to number of columns
417 // or headers changing, which changes the number of placeholders needed
418 private final DataSetObservable mDataSetObservable
= new DataSetObservable();
419 private final ListAdapter mAdapter
;
420 static final ArrayList
<FixedViewInfo
> EMPTY_INFO_LIST
=
421 new ArrayList
<FixedViewInfo
>();
423 // This ArrayList is assumed to NOT be null.
424 ArrayList
<FixedViewInfo
> mHeaderViewInfos
;
425 ArrayList
<FixedViewInfo
> mFooterViewInfos
;
426 private int mNumColumns
= 1;
427 private int mRowHeight
= -1;
428 boolean mAreAllFixedViewsSelectable
;
429 private final boolean mIsFilterable
;
430 private boolean mCachePlaceHoldView
= true
;
431 // From Recycle Bin or calling getView, this a question...
432 private boolean mCacheFirstHeaderView
= false
;
434 public HeaderViewGridAdapter(ArrayList
<FixedViewInfo
> headerViewInfos
, ArrayList
<FixedViewInfo
> footViewInfos
, ListAdapter adapter
) {
436 mIsFilterable
= adapter
instanceof Filterable
;
437 if (headerViewInfos
== null
) {
438 mHeaderViewInfos
= EMPTY_INFO_LIST
;
440 mHeaderViewInfos
= headerViewInfos
;
443 if (footViewInfos
== null
) {
444 mFooterViewInfos
= EMPTY_INFO_LIST
;
446 mFooterViewInfos
= footViewInfos
;
448 mAreAllFixedViewsSelectable
= areAllListInfosSelectable(mHeaderViewInfos
)
449 && areAllListInfosSelectable(mFooterViewInfos
);
452 public void setNumColumns(int numColumns
) {
453 if (numColumns
< 1) {
456 if (mNumColumns
!= numColumns
) {
457 mNumColumns
= numColumns
;
458 notifyDataSetChanged();
462 public void setRowHeight(int height
) {
466 public int getHeadersCount() {
467 return mHeaderViewInfos
.size();
470 public int getFootersCount() {
471 return mFooterViewInfos
.size();
475 public boolean isEmpty() {
476 return (mAdapter
== null
|| mAdapter
.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0;
479 private boolean areAllListInfosSelectable(ArrayList
<FixedViewInfo
> infos
) {
481 for (FixedViewInfo info
: infos
) {
482 if (!info
.isSelectable
) {
490 public boolean removeHeader(View v
) {
491 for (int i
= 0; i
< mHeaderViewInfos
.size(); i
++) {
492 FixedViewInfo info
= mHeaderViewInfos
.get(i
);
493 if (info
.view
== v
) {
494 mHeaderViewInfos
.remove(i
);
495 mAreAllFixedViewsSelectable
=
496 areAllListInfosSelectable(mHeaderViewInfos
) && areAllListInfosSelectable(mFooterViewInfos
);
497 mDataSetObservable
.notifyChanged();
504 public boolean removeFooter(View v
) {
505 for (int i
= 0; i
< mFooterViewInfos
.size(); i
++) {
506 FixedViewInfo info
= mFooterViewInfos
.get(i
);
507 if (info
.view
== v
) {
508 mFooterViewInfos
.remove(i
);
509 mAreAllFixedViewsSelectable
=
510 areAllListInfosSelectable(mHeaderViewInfos
) && areAllListInfosSelectable(mFooterViewInfos
);
511 mDataSetObservable
.notifyChanged();
519 public int getCount() {
520 if (mAdapter
!= null
) {
521 return (getFootersCount() + getHeadersCount()) * mNumColumns
+ getAdapterAndPlaceHolderCount();
523 return (getFootersCount() + getHeadersCount()) * mNumColumns
;
528 public boolean areAllItemsEnabled() {
529 if (mAdapter
!= null
) {
530 return mAreAllFixedViewsSelectable
&& mAdapter
.areAllItemsEnabled();
536 private int getAdapterAndPlaceHolderCount() {
537 final int adapterCount
= (int) (Math
.ceil(1f
* mAdapter
.getCount() / mNumColumns
) * mNumColumns
);
542 public boolean isEnabled(int position
) {
543 // Header (negative positions will throw an IndexOutOfBoundsException)
544 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
545 if (position
< numHeadersAndPlaceholders
) {
546 return position
% mNumColumns
== 0
547 && mHeaderViewInfos
.get(position
/ mNumColumns
).isSelectable
;
551 final int adjPosition
= position
- numHeadersAndPlaceholders
;
552 int adapterCount
= 0;
553 if (mAdapter
!= null
) {
554 adapterCount
= getAdapterAndPlaceHolderCount();
555 if (adjPosition
< adapterCount
) {
556 return adjPosition
< mAdapter
.getCount() && mAdapter
.isEnabled(adjPosition
);
560 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
561 final int footerPosition
= adjPosition
- adapterCount
;
562 return footerPosition
% mNumColumns
== 0
563 && mFooterViewInfos
.get(footerPosition
/ mNumColumns
).isSelectable
;
567 public Object
getItem(int position
) {
568 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
569 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
570 if (position
< numHeadersAndPlaceholders
) {
571 if (position
% mNumColumns
== 0) {
572 return mHeaderViewInfos
.get(position
/ mNumColumns
).data
;
578 final int adjPosition
= position
- numHeadersAndPlaceholders
;
579 int adapterCount
= 0;
580 if (mAdapter
!= null
) {
581 adapterCount
= getAdapterAndPlaceHolderCount();
582 if (adjPosition
< adapterCount
) {
583 if (adjPosition
< mAdapter
.getCount()) {
584 return mAdapter
.getItem(adjPosition
);
591 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
592 final int footerPosition
= adjPosition
- adapterCount
;
593 if (footerPosition
% mNumColumns
== 0) {
594 return mFooterViewInfos
.get(footerPosition
).data
;
601 public long getItemId(int position
) {
602 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
603 if (mAdapter
!= null
&& position
>= numHeadersAndPlaceholders
) {
604 int adjPosition
= position
- numHeadersAndPlaceholders
;
605 int adapterCount
= mAdapter
.getCount();
606 if (adjPosition
< adapterCount
) {
607 return mAdapter
.getItemId(adjPosition
);
614 public boolean hasStableIds() {
615 if (mAdapter
!= null
) {
616 return mAdapter
.hasStableIds();
622 public View
getView(int position
, View convertView
, ViewGroup parent
) {
624 Log
.d(LOG_TAG
, String
.format("getView: %s, reused: %s", position
, convertView
== null
));
626 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
627 int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
628 if (position
< numHeadersAndPlaceholders
) {
629 View headerViewContainer
= mHeaderViewInfos
630 .get(position
/ mNumColumns
).viewContainer
;
631 if (position
% mNumColumns
== 0) {
632 return headerViewContainer
;
634 if (convertView
== null
) {
635 convertView
= new View(parent
.getContext());
637 // We need to do this because GridView uses the height of the last item
638 // in a row to determine the height for the entire row.
639 convertView
.setVisibility(View
.INVISIBLE
);
640 convertView
.setMinimumHeight(headerViewContainer
.getHeight());
645 final int adjPosition
= position
- numHeadersAndPlaceholders
;
646 int adapterCount
= 0;
647 if (mAdapter
!= null
) {
648 adapterCount
= getAdapterAndPlaceHolderCount();
649 if (adjPosition
< adapterCount
) {
650 if (adjPosition
< mAdapter
.getCount()) {
651 View view
= mAdapter
.getView(adjPosition
, convertView
, parent
);
654 if (convertView
== null
) {
655 convertView
= new View(parent
.getContext());
657 convertView
.setVisibility(View
.INVISIBLE
);
658 convertView
.setMinimumHeight(mRowHeight
);
664 final int footerPosition
= adjPosition
- adapterCount
;
665 if (footerPosition
< getCount()) {
666 View footViewContainer
= mFooterViewInfos
667 .get(footerPosition
/ mNumColumns
).viewContainer
;
668 if (position
% mNumColumns
== 0) {
669 return footViewContainer
;
671 if (convertView
== null
) {
672 convertView
= new View(parent
.getContext());
674 // We need to do this because GridView uses the height of the last item
675 // in a row to determine the height for the entire row.
676 convertView
.setVisibility(View
.INVISIBLE
);
677 convertView
.setMinimumHeight(footViewContainer
.getHeight());
681 throw new ArrayIndexOutOfBoundsException(position
);
685 public int getItemViewType(int position
) {
687 final int numHeadersAndPlaceholders
= getHeadersCount() * mNumColumns
;
688 final int adapterViewTypeStart
= mAdapter
== null ?
0 : mAdapter
.getViewTypeCount() - 1;
689 int type
= AdapterView
.ITEM_VIEW_TYPE_HEADER_OR_FOOTER
;
690 if (mCachePlaceHoldView
) {
692 if (position
< numHeadersAndPlaceholders
) {
694 if (mCacheFirstHeaderView
) {
695 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + mFooterViewInfos
.size() + 1 + 1;
698 if (position
% mNumColumns
!= 0) {
699 type
= adapterViewTypeStart
+ (position
/ mNumColumns
+ 1);
705 final int adjPosition
= position
- numHeadersAndPlaceholders
;
706 int adapterCount
= 0;
707 if (mAdapter
!= null
) {
708 adapterCount
= getAdapterAndPlaceHolderCount();
709 if (adjPosition
>= 0 && adjPosition
< adapterCount
) {
710 if (adjPosition
< mAdapter
.getCount()) {
711 type
= mAdapter
.getItemViewType(adjPosition
);
713 if (mCachePlaceHoldView
) {
714 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + 1;
720 if (mCachePlaceHoldView
) {
722 final int footerPosition
= adjPosition
- adapterCount
;
723 if (footerPosition
>= 0 && footerPosition
< getCount() && (footerPosition
% mNumColumns
) != 0) {
724 type
= adapterViewTypeStart
+ mHeaderViewInfos
.size() + 1 + (footerPosition
/ mNumColumns
+ 1);
728 Log
.d(LOG_TAG
, String
.format("getItemViewType: pos: %s, result: %s", position
, type
, mCachePlaceHoldView
, mCacheFirstHeaderView
));
734 * content view, content view holder, header[0], header and footer placeholder(s)
739 public int getViewTypeCount() {
740 int count
= mAdapter
== null ?
1 : mAdapter
.getViewTypeCount();
741 if (mCachePlaceHoldView
) {
742 int offset
= mHeaderViewInfos
.size() + 1 + mFooterViewInfos
.size();
743 if (mCacheFirstHeaderView
) {
749 Log
.d(LOG_TAG
, String
.format("getViewTypeCount: %s", count
));
755 public void registerDataSetObserver(DataSetObserver observer
) {
756 mDataSetObservable
.registerObserver(observer
);
757 if (mAdapter
!= null
) {
758 mAdapter
.registerDataSetObserver(observer
);
763 public void unregisterDataSetObserver(DataSetObserver observer
) {
764 mDataSetObservable
.unregisterObserver(observer
);
765 if (mAdapter
!= null
) {
766 mAdapter
.unregisterDataSetObserver(observer
);
771 public Filter
getFilter() {
773 return ((Filterable
) mAdapter
).getFilter();
779 public ListAdapter
getWrappedAdapter() {
783 public void notifyDataSetChanged() {
784 mDataSetObservable
.notifyChanged();