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();