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