为GridView添加头布局

    xiaoxiao2026-02-26  4

    实现这个功能一般有两种思路,一种思路是使用ScrollView+GridView,第二种思路是使用ListView来实现GridView的效果。

    第一种思路的具体实现是把HeaderView和GridView都放到ScrollView里面,这里要解决的问题是ScrollView和GridView滑动手势的冲突问题,解决办法是让GridView充满ScrollView,不让GridView滑动而只让ScrollView滑动。具题做法是重载GridView的onMeasure()方法。

    [java]  view plain  copy   public class MyGridView extends GridView {        public MyGridView(Context context, AttributeSet attrs) {            super(context, attrs);        }           public MyGridView(Context context) {            super(context);        }           public MyGridView(Context context, AttributeSet attrs, int defStyle) {            super(context, attrs, defStyle);        }           @Override        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {               int expandSpec = MeasureSpec.makeMeasureSpec(                    Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);            super.onMeasure(widthMeasureSpec, expandSpec);        }    }   

    这种方法的不足是GridView中的View没有复用,如果内容较多将比较消耗内存。

    第二种思路的具体实现是使用ListView的addHeaderView()来添加HeaderView,而ListView的每一行都放一个LinearLayout来保存一行的item,这里要注意的是item的个数和ListView行数的关系。

    后来在StackOverFlow上又看到了第三中方法。原来Google已经用GridView实现了,而且很巧妙,下面把代码贴出来

    [java]  view plain  copy   /*   * Copyright (C) 2013 The Android Open Source Project   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License.   * You may obtain a copy of the License at   *   *      http://www.apache.org/licenses/LICENSE-2.0   *   * Unless required by applicable law or agreed to in writing, software   * distributed under the License is distributed on an "AS IS" BASIS,   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   * See the License for the specific language governing permissions and   * limitations under the License.   */   package com.android.photos.views;   import android.content.Context;   import android.database.DataSetObservable;   import android.database.DataSetObserver;   import android.util.AttributeSet;   import android.view.View;   import android.view.ViewGroup;   import android.widget.AdapterView;   import android.widget.Filter;   import android.widget.Filterable;   import android.widget.FrameLayout;   import android.widget.GridView;   import android.widget.ListAdapter;   import android.widget.WrapperListAdapter;   import java.util.ArrayList;   /**   * A {@link GridView} that supports adding header rows in a   * very similar way to {@link ListView}.   * See {@link HeaderGridView#addHeaderView(View, Object, boolean)}   */   public class HeaderGridView extends GridView {       private static final String TAG = "HeaderGridView";       /**       * A class that represents a fixed view in a list, for example a header at the top       * or a footer at the bottom.       */       private static class FixedViewInfo {           /** The view to add to the grid */           public View view;           public ViewGroup viewContainer;           /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */           public Object data;           /** <code>true</code> if the fixed view should be selectable in the grid */           public boolean isSelectable;       }       private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();       private void initHeaderGridView() {           super.setClipChildren(false);       }       public HeaderGridView(Context context) {           super(context);           initHeaderGridView();       }       public HeaderGridView(Context context, AttributeSet attrs) {           super(context, attrs);           initHeaderGridView();       }       public HeaderGridView(Context context, AttributeSet attrs, int defStyle) {           super(context, attrs, defStyle);           initHeaderGridView();       }       @Override       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {           super.onMeasure(widthMeasureSpec, heightMeasureSpec);           ListAdapter adapter = getAdapter();           if (adapter != null && adapter instanceof HeaderViewGridAdapter) {               ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumns());           }       }       @Override       public void setClipChildren(boolean clipChildren) {          // Ignore, since the header rows depend on not being clipped       }       /**       * Add a fixed view to appear at the top of the grid. If addHeaderView is       * called more than once, the views will appear in the order they were       * added. Views added using this call can take focus if they want.       * <p>       * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap       * the supplied cursor with one that will also account for header views.       *       * @param v The view to add.       * @param data Data to associate with this view       * @param isSelectable whether the item is selectable       */       public void addHeaderView(View v, Object data, boolean isSelectable) {           ListAdapter adapter = getAdapter();           if (adapter != null && ! (adapter instanceof HeaderViewGridAdapter)) {               throw new IllegalStateException(                       "Cannot add header view to grid -- setAdapter has already been called.");           }           FixedViewInfo info = new FixedViewInfo();           FrameLayout fl = new FullWidthFixedViewLayout(getContext());           fl.addView(v);           info.view = v;           info.viewContainer = fl;           info.data = data;           info.isSelectable = isSelectable;           mHeaderViewInfos.add(info);           // in the case of re-adding a header view, or adding one later on,           // we need to notify the observer           if (adapter != null) {               ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();           }       }       /**       * Add a fixed view to appear at the top of the grid. If addHeaderView is       * called more than once, the views will appear in the order they were       * added. Views added using this call can take focus if they want.       * <p>       * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap       * the supplied cursor with one that will also account for header views.       *       * @param v The view to add.       */       public void addHeaderView(View v) {           addHeaderView(v, nulltrue);       }       public int getHeaderViewCount() {           return mHeaderViewInfos.size();       }       /**       * Removes a previously-added header view.       *       * @param v The view to remove       * @return true if the view was removed, false if the view was not a header       *         view       */       public boolean removeHeaderView(View v) {           if (mHeaderViewInfos.size() > 0) {               boolean result = false;               ListAdapter adapter = getAdapter();               if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {                   result = true;               }               removeFixedViewInfo(v, mHeaderViewInfos);               return result;           }           return false;       }       private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {           int len = where.size();           for (int i = 0; i < len; ++i) {               FixedViewInfo info = where.get(i);               if (info.view == v) {                   where.remove(i);                   break;               }           }       }       @Override       public void setAdapter(ListAdapter adapter) {           if (mHeaderViewInfos.size() > 0) {               HeaderViewGridAdapter hadapter = new HeaderViewGridAdapter(mHeaderViewInfos, adapter);               int numColumns = getNumColumns();               if (numColumns > 1) {                   hadapter.setNumColumns(numColumns);               }               super.setAdapter(hadapter);           } else {               super.setAdapter(adapter);           }       }       private class FullWidthFixedViewLayout extends FrameLayout {           public FullWidthFixedViewLayout(Context context) {               super(context);           }           @Override           protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {               int targetWidth = HeaderGridView.this.getMeasuredWidth()                       - HeaderGridView.this.getPaddingLeft()                       - HeaderGridView.this.getPaddingRight();               widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,                       MeasureSpec.getMode(widthMeasureSpec));               super.onMeasure(widthMeasureSpec, heightMeasureSpec);           }       }       /**       * ListAdapter used when a HeaderGridView has header views. This ListAdapter       * wraps another one and also keeps track of the header views and their       * associated data objects.       *<p>This is intended as a base class; you will probably not need to       * use this class directly in your own code.       */       private static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable {           // This is used to notify the container of updates relating to number of columns           // or headers changing, which changes the number of placeholders needed           private final DataSetObservable mDataSetObservable = new DataSetObservable();           private final ListAdapter mAdapter;           private int mNumColumns = 1;           // This ArrayList is assumed to NOT be null.           ArrayList<FixedViewInfo> mHeaderViewInfos;           boolean mAreAllFixedViewsSelectable;           private final boolean mIsFilterable;           public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ListAdapter adapter) {               mAdapter = adapter;               mIsFilterable = adapter instanceof Filterable;               if (headerViewInfos == null) {                   throw new IllegalArgumentException("headerViewInfos cannot be null");               }               mHeaderViewInfos = headerViewInfos;               mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);           }           public int getHeadersCount() {               return mHeaderViewInfos.size();           }           @Override           public boolean isEmpty() {               return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0;           }           public void setNumColumns(int numColumns) {               if (numColumns < 1) {                   throw new IllegalArgumentException("Number of columns must be 1 or more");               }               if (mNumColumns != numColumns) {                   mNumColumns = numColumns;                   notifyDataSetChanged();               }           }           private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {               if (infos != null) {                   for (FixedViewInfo info : infos) {                       if (!info.isSelectable) {                           return false;                       }                   }               }               return true;           }           public boolean removeHeader(View v) {               for (int i = 0; i < mHeaderViewInfos.size(); i++) {                   FixedViewInfo info = mHeaderViewInfos.get(i);                   if (info.view == v) {                       mHeaderViewInfos.remove(i);                       mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);                       mDataSetObservable.notifyChanged();                       return true;                   }               }               return false;           }           @Override           public int getCount() {               if (mAdapter != null) {                   return getHeadersCount() * mNumColumns + mAdapter.getCount();               } else {                   return getHeadersCount() * mNumColumns;               }           }           @Override           public boolean areAllItemsEnabled() {               if (mAdapter != null) {                   return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();               } else {                   return true;               }           }           @Override           public boolean isEnabled(int position) {               // Header (negative positions will throw an ArrayIndexOutOfBoundsException)               int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;               if (position < numHeadersAndPlaceholders) {                   return (position % mNumColumns == 0)                           && mHeaderViewInfos.get(position / mNumColumns).isSelectable;               }               // Adapter               final int adjPosition = position - numHeadersAndPlaceholders;               int adapterCount = 0;               if (mAdapter != null) {                   adapterCount = mAdapter.getCount();                   if (adjPosition < adapterCount) {                       return mAdapter.isEnabled(adjPosition);                   }               }               throw new ArrayIndexOutOfBoundsException(position);           }           @Override           public Object getItem(int position) {               // Header (negative positions will throw an ArrayIndexOutOfBoundsException)               int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;               if (position < numHeadersAndPlaceholders) {                   if (position % mNumColumns == 0) {                       return mHeaderViewInfos.get(position / mNumColumns).data;                   }                   return null;               }               // Adapter               final int adjPosition = position - numHeadersAndPlaceholders;               int adapterCount = 0;               if (mAdapter != null) {                   adapterCount = mAdapter.getCount();                   if (adjPosition < adapterCount) {                       return mAdapter.getItem(adjPosition);                   }               }               throw new ArrayIndexOutOfBoundsException(position);           }           @Override           public long getItemId(int position) {               int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;               if (mAdapter != null && position >= numHeadersAndPlaceholders) {                   int adjPosition = position - numHeadersAndPlaceholders;                   int adapterCount = mAdapter.getCount();                   if (adjPosition < adapterCount) {                       return mAdapter.getItemId(adjPosition);                   }               }               return -1;           }           @Override           public boolean hasStableIds() {               if (mAdapter != null) {                   return mAdapter.hasStableIds();               }               return false;           }           @Override           public View getView(int position, View convertView, ViewGroup parent) {               // Header (negative positions will throw an ArrayIndexOutOfBoundsException)               int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns ;               if (position < numHeadersAndPlaceholders) {                   View headerViewContainer = mHeaderViewInfos                           .get(position / mNumColumns).viewContainer;                   if (position % mNumColumns == 0) {                       return headerViewContainer;                   } else {                       if (convertView == null) {                           convertView = new View(parent.getContext());                       }                       // We need to do this because GridView uses the height of the last item                       // in a row to determine the height for the entire row.                       convertView.setVisibility(View.INVISIBLE);                       convertView.setMinimumHeight(headerViewContainer.getHeight());                       return convertView;                   }               }               // Adapter               final int adjPosition = position - numHeadersAndPlaceholders;               int adapterCount = 0;               if (mAdapter != null) {                   adapterCount = mAdapter.getCount();                   if (adjPosition < adapterCount) {                       return mAdapter.getView(adjPosition, convertView, parent);                   }               }               throw new ArrayIndexOutOfBoundsException(position);           }           @Override           public int getItemViewType(int position) {               int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;               if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) {                   // Placeholders get the last view type number                   return mAdapter != null ? mAdapter.getViewTypeCount() : 1;               }               if (mAdapter != null && position >= numHeadersAndPlaceholders) {                   int adjPosition = position - numHeadersAndPlaceholders;                   int adapterCount = mAdapter.getCount();                   if (adjPosition < adapterCount) {                       return mAdapter.getItemViewType(adjPosition);                   }               }               return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;           }           @Override           public int getViewTypeCount() {               if (mAdapter != null) {                   return mAdapter.getViewTypeCount() + 1;               }               return 2;           }           @Override           public void registerDataSetObserver(DataSetObserver observer) {               mDataSetObservable.registerObserver(observer);               if (mAdapter != null) {                   mAdapter.registerDataSetObserver(observer);               }           }           @Override           public void unregisterDataSetObserver(DataSetObserver observer) {               mDataSetObservable.unregisterObserver(observer);               if (mAdapter != null) {                   mAdapter.unregisterDataSetObserver(observer);               }           }           @Override           public Filter getFilter() {               if (mIsFilterable) {                   return ((Filterable) mAdapter).getFilter();               }               return null;           }           @Override           public ListAdapter getWrappedAdapter() {               return mAdapter;           }           public void notifyDataSetChanged() {               mDataSetObservable.notifyChanged();           }       }   }   代码地址: https://android.googlesource.com/platform/packages/apps/Gallery2/+/idea133/src/com/android/photos/views/HeaderGridView.java

    转载请注明原文地址: https://ju.6miu.com/read-1307397.html
    最新回复(0)