1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.widget;
18 
19 import android.database.DataSetObserver;
20 import android.view.View;
21 import android.view.ViewGroup;
22 
23 import java.util.ArrayList;
24 
25 /**
26  * ListAdapter used when a ListView has header views. This ListAdapter
27  * wraps another one and also keeps track of the header views and their
28  * associated data objects.
29  *<p>This is intended as a base class; you will probably not need to
30  * use this class directly in your own code.
31  */
32 public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
33 
34     private final ListAdapter mAdapter;
35 
36     // These two ArrayList are assumed to NOT be null.
37     // They are indeed created when declared in ListView and then shared.
38     ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
39     ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
40 
41     // Used as a placeholder in case the provided info views are indeed null.
42     // Currently only used by some CTS tests, which may be removed.
43     static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
44         new ArrayList<ListView.FixedViewInfo>();
45 
46     boolean mAreAllFixedViewsSelectable;
47 
48     private final boolean mIsFilterable;
49 
HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos, ArrayList<ListView.FixedViewInfo> footerViewInfos, ListAdapter adapter)50     public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
51                                  ArrayList<ListView.FixedViewInfo> footerViewInfos,
52                                  ListAdapter adapter) {
53         mAdapter = adapter;
54         mIsFilterable = adapter instanceof Filterable;
55 
56         if (headerViewInfos == null) {
57             mHeaderViewInfos = EMPTY_INFO_LIST;
58         } else {
59             mHeaderViewInfos = headerViewInfos;
60         }
61 
62         if (footerViewInfos == null) {
63             mFooterViewInfos = EMPTY_INFO_LIST;
64         } else {
65             mFooterViewInfos = footerViewInfos;
66         }
67 
68         mAreAllFixedViewsSelectable =
69                 areAllListInfosSelectable(mHeaderViewInfos)
70                 && areAllListInfosSelectable(mFooterViewInfos);
71     }
72 
getHeadersCount()73     public int getHeadersCount() {
74         return mHeaderViewInfos.size();
75     }
76 
getFootersCount()77     public int getFootersCount() {
78         return mFooterViewInfos.size();
79     }
80 
isEmpty()81     public boolean isEmpty() {
82         return mAdapter == null || mAdapter.isEmpty();
83     }
84 
areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> infos)85     private boolean areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> infos) {
86         if (infos != null) {
87             for (ListView.FixedViewInfo info : infos) {
88                 if (!info.isSelectable) {
89                     return false;
90                 }
91             }
92         }
93         return true;
94     }
95 
removeHeader(View v)96     public boolean removeHeader(View v) {
97         for (int i = 0; i < mHeaderViewInfos.size(); i++) {
98             ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
99             if (info.view == v) {
100                 mHeaderViewInfos.remove(i);
101 
102                 mAreAllFixedViewsSelectable =
103                         areAllListInfosSelectable(mHeaderViewInfos)
104                         && areAllListInfosSelectable(mFooterViewInfos);
105 
106                 return true;
107             }
108         }
109 
110         return false;
111     }
112 
removeFooter(View v)113     public boolean removeFooter(View v) {
114         for (int i = 0; i < mFooterViewInfos.size(); i++) {
115             ListView.FixedViewInfo info = mFooterViewInfos.get(i);
116             if (info.view == v) {
117                 mFooterViewInfos.remove(i);
118 
119                 mAreAllFixedViewsSelectable =
120                         areAllListInfosSelectable(mHeaderViewInfos)
121                         && areAllListInfosSelectable(mFooterViewInfos);
122 
123                 return true;
124             }
125         }
126 
127         return false;
128     }
129 
getCount()130     public int getCount() {
131         if (mAdapter != null) {
132             return getFootersCount() + getHeadersCount() + mAdapter.getCount();
133         } else {
134             return getFootersCount() + getHeadersCount();
135         }
136     }
137 
areAllItemsEnabled()138     public boolean areAllItemsEnabled() {
139         if (mAdapter != null) {
140             return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
141         } else {
142             return true;
143         }
144     }
145 
isEnabled(int position)146     public boolean isEnabled(int position) {
147         // Header (negative positions will throw an IndexOutOfBoundsException)
148         int numHeaders = getHeadersCount();
149         if (position < numHeaders) {
150             return mHeaderViewInfos.get(position).isSelectable;
151         }
152 
153         // Adapter
154         final int adjPosition = position - numHeaders;
155         int adapterCount = 0;
156         if (mAdapter != null) {
157             adapterCount = mAdapter.getCount();
158             if (adjPosition < adapterCount) {
159                 return mAdapter.isEnabled(adjPosition);
160             }
161         }
162 
163         // Footer (off-limits positions will throw an IndexOutOfBoundsException)
164         return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
165     }
166 
getItem(int position)167     public Object getItem(int position) {
168         // Header (negative positions will throw an IndexOutOfBoundsException)
169         int numHeaders = getHeadersCount();
170         if (position < numHeaders) {
171             return mHeaderViewInfos.get(position).data;
172         }
173 
174         // Adapter
175         final int adjPosition = position - numHeaders;
176         int adapterCount = 0;
177         if (mAdapter != null) {
178             adapterCount = mAdapter.getCount();
179             if (adjPosition < adapterCount) {
180                 return mAdapter.getItem(adjPosition);
181             }
182         }
183 
184         // Footer (off-limits positions will throw an IndexOutOfBoundsException)
185         return mFooterViewInfos.get(adjPosition - adapterCount).data;
186     }
187 
getItemId(int position)188     public long getItemId(int position) {
189         int numHeaders = getHeadersCount();
190         if (mAdapter != null && position >= numHeaders) {
191             int adjPosition = position - numHeaders;
192             int adapterCount = mAdapter.getCount();
193             if (adjPosition < adapterCount) {
194                 return mAdapter.getItemId(adjPosition);
195             }
196         }
197         return -1;
198     }
199 
hasStableIds()200     public boolean hasStableIds() {
201         if (mAdapter != null) {
202             return mAdapter.hasStableIds();
203         }
204         return false;
205     }
206 
getView(int position, View convertView, ViewGroup parent)207     public View getView(int position, View convertView, ViewGroup parent) {
208         // Header (negative positions will throw an IndexOutOfBoundsException)
209         int numHeaders = getHeadersCount();
210         if (position < numHeaders) {
211             return mHeaderViewInfos.get(position).view;
212         }
213 
214         // Adapter
215         final int adjPosition = position - numHeaders;
216         int adapterCount = 0;
217         if (mAdapter != null) {
218             adapterCount = mAdapter.getCount();
219             if (adjPosition < adapterCount) {
220                 return mAdapter.getView(adjPosition, convertView, parent);
221             }
222         }
223 
224         // Footer (off-limits positions will throw an IndexOutOfBoundsException)
225         return mFooterViewInfos.get(adjPosition - adapterCount).view;
226     }
227 
getItemViewType(int position)228     public int getItemViewType(int position) {
229         int numHeaders = getHeadersCount();
230         if (mAdapter != null && position >= numHeaders) {
231             int adjPosition = position - numHeaders;
232             int adapterCount = mAdapter.getCount();
233             if (adjPosition < adapterCount) {
234                 return mAdapter.getItemViewType(adjPosition);
235             }
236         }
237 
238         return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
239     }
240 
getViewTypeCount()241     public int getViewTypeCount() {
242         if (mAdapter != null) {
243             return mAdapter.getViewTypeCount();
244         }
245         return 1;
246     }
247 
registerDataSetObserver(DataSetObserver observer)248     public void registerDataSetObserver(DataSetObserver observer) {
249         if (mAdapter != null) {
250             mAdapter.registerDataSetObserver(observer);
251         }
252     }
253 
unregisterDataSetObserver(DataSetObserver observer)254     public void unregisterDataSetObserver(DataSetObserver observer) {
255         if (mAdapter != null) {
256             mAdapter.unregisterDataSetObserver(observer);
257         }
258     }
259 
getFilter()260     public Filter getFilter() {
261         if (mIsFilterable) {
262             return ((Filterable) mAdapter).getFilter();
263         }
264         return null;
265     }
266 
getWrappedAdapter()267     public ListAdapter getWrappedAdapter() {
268         return mAdapter;
269     }
270 }
271