where) {
218 | int len = where.size();
219 | for (int i = 0; i < len; ++i) {
220 | FixedViewInfo info = where.get(i);
221 | if (info.view == v) {
222 | where.remove(i);
223 | break;
224 | }
225 | }
226 | }
227 |
228 | @Override
229 | public void setAdapter(ListAdapter adapter) {
230 | if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
231 | HeaderViewGridAdapter hadapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
232 | int numColumns = getNumColumns();
233 | if (numColumns > 1) {
234 | hadapter.setNumColumns(numColumns);
235 | }
236 | super.setAdapter(hadapter);
237 | } else {
238 | super.setAdapter(adapter);
239 | }
240 | }
241 |
242 | private class FullWidthFixedViewLayout extends FrameLayout {
243 | public FullWidthFixedViewLayout(Context context) {
244 | super(context);
245 | }
246 |
247 | @Override
248 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
249 | int targetWidth = HeaderGridView.this.getMeasuredWidth()
250 | - HeaderGridView.this.getPaddingLeft()
251 | - HeaderGridView.this.getPaddingRight();
252 | widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,
253 | MeasureSpec.getMode(widthMeasureSpec));
254 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
255 | }
256 | }
257 |
258 | /**
259 | * ListAdapter used when a HeaderGridView has header views. This ListAdapter
260 | * wraps another one and also keeps track of the header views and their
261 | * associated data objects.
262 | * This is intended as a base class; you will probably not need to
263 | * use this class directly in your own code.
264 | */
265 | private static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable {
266 |
267 | // This is used to notify the container of updates relating to number of columns
268 | // or headers changing, which changes the number of placeholders needed
269 | private final DataSetObservable mDataSetObservable = new DataSetObservable();
270 |
271 | private final ListAdapter mAdapter;
272 | private int mNumColumns = 1;
273 |
274 | // This ArrayList is assumed to NOT be null.
275 | ArrayList mHeaderViewInfos;
276 | ArrayList mFooterViewInfos;
277 |
278 | boolean mAreAllFixedViewsSelectable;
279 |
280 | private final boolean mIsFilterable;
281 |
282 | public HeaderViewGridAdapter(ArrayList headerViewInfos, ArrayList footerViewInfos, ListAdapter adapter) {
283 | mAdapter = adapter;
284 | mIsFilterable = adapter instanceof Filterable;
285 |
286 | if (headerViewInfos == null) {
287 | throw new IllegalArgumentException("headerViewInfos cannot be null");
288 | }
289 | mHeaderViewInfos = headerViewInfos;
290 | mFooterViewInfos = footerViewInfos;
291 |
292 | mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos) & areAllListInfosSelectable(mFooterViewInfos);
293 | }
294 |
295 | public int getHeadersCount() {
296 | return mHeaderViewInfos.size();
297 | }
298 |
299 | public int getFootersCount() {
300 | return mFooterViewInfos.size();
301 | }
302 |
303 | @Override
304 | public boolean isEmpty() {
305 | return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0;
306 | }
307 |
308 | public void setNumColumns(int numColumns) {
309 | if (numColumns < 1) {
310 | throw new IllegalArgumentException("Number of columns must be 1 or more");
311 | }
312 | if (mNumColumns != numColumns) {
313 | mNumColumns = numColumns;
314 | notifyDataSetChanged();
315 | }
316 | }
317 |
318 | private boolean areAllListInfosSelectable(ArrayList infos) {
319 | if (infos != null) {
320 | for (FixedViewInfo info : infos) {
321 | if (!info.isSelectable) {
322 | return false;
323 | }
324 | }
325 | }
326 | return true;
327 | }
328 |
329 | public boolean removeHeader(View v) {
330 | for (int i = 0; i < mHeaderViewInfos.size(); i++) {
331 | FixedViewInfo info = mHeaderViewInfos.get(i);
332 | if (info.view == v) {
333 | mHeaderViewInfos.remove(i);
334 |
335 | mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);
336 |
337 | mDataSetObservable.notifyChanged();
338 | return true;
339 | }
340 | }
341 |
342 | return false;
343 | }
344 |
345 | public boolean removeFooter(View v) {
346 | for (int i = 0; i < mFooterViewInfos.size(); i++) {
347 | FixedViewInfo info = mFooterViewInfos.get(i);
348 | if (info.view == v) {
349 | mFooterViewInfos.remove(i);
350 |
351 | mAreAllFixedViewsSelectable = areAllListInfosSelectable(mFooterViewInfos);
352 |
353 | mDataSetObservable.notifyChanged();
354 | return true;
355 | }
356 | }
357 |
358 | return false;
359 | }
360 |
361 | @Override
362 | public int getCount() {
363 | if (mAdapter != null) {
364 | return getHeadersCount() * mNumColumns + getFootersCount() * mNumColumns + mAdapter.getCount();
365 | } else {
366 | return getHeadersCount() * mNumColumns + getFootersCount() * mNumColumns;
367 | }
368 | }
369 |
370 | @Override
371 | public boolean areAllItemsEnabled() {
372 | if (mAdapter != null) {
373 | return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
374 | } else {
375 | return true;
376 | }
377 | }
378 |
379 | @Override
380 | public boolean isEnabled(int position) {
381 | // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
382 | int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
383 | if (position < numHeadersAndPlaceholders) {
384 | return (position % mNumColumns == 0)
385 | && mHeaderViewInfos.get(position / mNumColumns).isSelectable;
386 | }
387 |
388 | if (position >= ((mAdapter == null ? 0 : mAdapter.getCount()) + getHeadersCount() * mNumColumns)) {
389 | return (position % mNumColumns == 0)
390 | && mFooterViewInfos.get((position - (mAdapter == null ? 0 : mAdapter.getCount()) - getHeadersCount() * mNumColumns) / mNumColumns).isSelectable;
391 | }
392 |
393 | // Adapter
394 | final int adjPosition = position - numHeadersAndPlaceholders;
395 | int adapterCount = 0;
396 | if (mAdapter != null) {
397 | adapterCount = mAdapter.getCount();
398 | if (adjPosition < adapterCount) {
399 | return mAdapter.isEnabled(adjPosition);
400 | }
401 | }
402 |
403 | throw new ArrayIndexOutOfBoundsException(position);
404 | }
405 |
406 | @Override
407 | public Object getItem(int position) {
408 | // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
409 | int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
410 | if (position < numHeadersAndPlaceholders) {
411 | if (position % mNumColumns == 0) {
412 | return mHeaderViewInfos.get(position / mNumColumns).data;
413 | }
414 | return null;
415 | }
416 |
417 | if (position >= (mAdapter == null ? 0 : mAdapter.getCount()) + getHeadersCount() * mNumColumns) {
418 | if (position % mNumColumns == 0) {
419 | return mFooterViewInfos.get((position - (mAdapter == null ? 0 : mAdapter.getCount()) - getHeadersCount() * mNumColumns) / mNumColumns).data;
420 | }
421 | return null;
422 | }
423 |
424 | // Adapter
425 | final int adjPosition = position - numHeadersAndPlaceholders;
426 | int adapterCount = 0;
427 | if (mAdapter != null) {
428 | adapterCount = mAdapter.getCount();
429 | if (adjPosition < adapterCount) {
430 | return mAdapter.getItem(adjPosition);
431 | }
432 | }
433 |
434 | throw new ArrayIndexOutOfBoundsException(position);
435 | }
436 |
437 | @Override
438 | public long getItemId(int position) {
439 | int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
440 | if (mAdapter != null && position >= numHeadersAndPlaceholders) {
441 | int adjPosition = position - numHeadersAndPlaceholders;
442 | int adapterCount = mAdapter.getCount();
443 | if (adjPosition < adapterCount) {
444 | return mAdapter.getItemId(adjPosition);
445 | }
446 | }
447 | return -1;
448 | }
449 |
450 | @Override
451 | public boolean hasStableIds() {
452 | if (mAdapter != null) {
453 | return mAdapter.hasStableIds();
454 | }
455 | return false;
456 | }
457 |
458 | @Override
459 | public View getView(int position, View convertView, ViewGroup parent) {
460 | // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
461 | int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
462 | if (position < numHeadersAndPlaceholders) {
463 | View headerViewContainer = mHeaderViewInfos
464 | .get(position / mNumColumns).viewContainer;
465 | if (position % mNumColumns == 0) {
466 | return headerViewContainer;
467 | } else {
468 | if (convertView == null) {
469 | convertView = new View(parent.getContext());
470 | }
471 | // We need to do this because GridView uses the height of the last item
472 | // in a row to determine the height for the entire row.
473 | convertView.setVisibility(View.INVISIBLE);
474 | convertView.setMinimumHeight(headerViewContainer.getHeight());
475 | return convertView;
476 | }
477 | }
478 |
479 | if (position >= (mAdapter == null ? 0 : mAdapter.getCount()) + getHeadersCount() * mNumColumns) {
480 | View footerViewContainer = mFooterViewInfos
481 | .get((position - (mAdapter == null ? 0 : mAdapter.getCount()) - getHeadersCount() * mNumColumns) / mNumColumns).viewContainer;
482 | if (position % mNumColumns == 0) {
483 | return footerViewContainer;
484 | } else {
485 | if (convertView == null) {
486 | convertView = new View(parent.getContext());
487 | }
488 | // We need to do this because GridView uses the height of the last item
489 | // in a row to determine the height for the entire row.
490 | convertView.setVisibility(View.INVISIBLE);
491 | convertView.setMinimumHeight(footerViewContainer.getHeight());
492 | return convertView;
493 | }
494 | }
495 |
496 | // Adapter
497 | final int adjPosition = position - numHeadersAndPlaceholders;
498 | int adapterCount = 0;
499 | if (mAdapter != null) {
500 | adapterCount = mAdapter.getCount();
501 | if (adjPosition < adapterCount) {
502 | return mAdapter.getView(adjPosition, convertView, parent);
503 | }
504 | }
505 |
506 | throw new ArrayIndexOutOfBoundsException(position);
507 | }
508 |
509 | @Override
510 | public int getItemViewType(int position) {
511 | int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
512 | if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) {
513 | // Placeholders get the last view type number
514 | return mAdapter != null ? mAdapter.getViewTypeCount() : 1;
515 | }
516 | if (mAdapter != null && position >= numHeadersAndPlaceholders) {
517 | int adjPosition = position - numHeadersAndPlaceholders;
518 | int adapterCount = mAdapter.getCount();
519 | if (adjPosition < adapterCount) {
520 | return mAdapter.getItemViewType(adjPosition);
521 | }
522 | }
523 |
524 | if (position >= (mAdapter == null ? 0 : mAdapter.getCount()) + getHeadersCount() * mNumColumns && position % mNumColumns != 0) {
525 | // Placeholders get the last view type number
526 | return mAdapter != null ? mAdapter.getViewTypeCount() : 1;
527 | }
528 |
529 | return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
530 | }
531 |
532 | @Override
533 | public int getViewTypeCount() {
534 | if (mAdapter != null) {
535 | return mAdapter.getViewTypeCount() + 1;
536 | }
537 | return 2;
538 | }
539 |
540 | @Override
541 | public void registerDataSetObserver(DataSetObserver observer) {
542 | mDataSetObservable.registerObserver(observer);
543 | if (mAdapter != null) {
544 | mAdapter.registerDataSetObserver(observer);
545 | }
546 | }
547 |
548 | @Override
549 | public void unregisterDataSetObserver(DataSetObserver observer) {
550 | mDataSetObservable.unregisterObserver(observer);
551 | if (mAdapter != null) {
552 | mAdapter.unregisterDataSetObserver(observer);
553 | }
554 | }
555 |
556 | @Override
557 | public Filter getFilter() {
558 | if (mIsFilterable) {
559 | return ((Filterable) mAdapter).getFilter();
560 | }
561 | return null;
562 | }
563 |
564 | @Override
565 | public ListAdapter getWrappedAdapter() {
566 | return mAdapter;
567 | }
568 |
569 | public void notifyDataSetChanged() {
570 | mDataSetObservable.notifyChanged();
571 | }
572 | }
573 | }
--------------------------------------------------------------------------------