可以添加Footer和Header的GridView

来源:互联网 发布:网络主播评选 编辑:程序博客网 时间:2024/04/29 16:11

最近开发需求中用到类似需要添加Header的GridView,本来想使用ListView一行显示两个来替代,但是Item中会有不同的类型,这样使用ListView就不大方便了,getViewType(int position)方法不能使用了。

于是就在网上搜了搜,发现有高人已经写出来了。哈哈,这次比葫芦画瓢,实现了GridView添加Footer,不过暂时Footer仅支持添加一个,多个的话,在普通Item不能刚好够整行的情况下有些小问题,后续再优化

一个类完成任务,使用方便,大家在使用过程中发现什么问题及时贴出来,大家一起学习

/* * 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.wz.moon.widget;import java.util.ArrayList;import android.annotation.SuppressLint;import android.content.Context;import android.database.DataSetObservable;import android.database.DataSetObserver;import android.graphics.Color;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.view.ViewGroup.LayoutParams;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.ListView;import android.widget.WrapperListAdapter;/** * A {@link GridView} that supports adding header rows or footer rows in a * very similar way to {@link ListView}. * See {@link SuperGridView#addHeaderView(View, Object, boolean)} and * See {@link SuperGridView#addHeaderView(View, Object, boolean)} */public class SuperGridView extends GridView {    private static final String TAG = "SuperGridView";    /**     * 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 ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>();        private void initHeaderGridView() {        super.setClipChildren(false);    }    public SuperGridView(Context context) {        super(context);        initHeaderGridView();    }    public SuperGridView(Context context, AttributeSet attrs) {        super(context, attrs);        initHeaderGridView();    }    public SuperGridView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initHeaderGridView();    }    @SuppressLint("NewApi")@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 bottom of the grid. If addFooterView 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 addFooterView(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;        mFooterViewInfos.add(info);        // in the case of re-adding a footer 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, null, true);    }    public int getHeaderViewCount() {        return mHeaderViewInfos.size();    }        /**     * Add a fixed view to appear at the top of the grid. If addFooterView 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 addFooterView(View v) {        addFooterView(v, null, true);    }    public int getFooterViewCount() {        return mFooterViewInfos.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;    }        /**     * 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 removeFooterView(View v) {        if (mFooterViewInfos.size() > 0) {            boolean result = false;            ListAdapter adapter = getAdapter();            if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) {                result = true;            }            removeFixedViewInfo(v, mFooterViewInfos);            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;            }        }    }    @SuppressLint("NewApi")@Override    public void setAdapter(ListAdapter adapter) {        if (mHeaderViewInfos.size() > 0) {            HeaderViewGridAdapter hadapter = new HeaderViewGridAdapter(mHeaderViewInfos,mFooterViewInfos,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 = SuperGridView.this.getMeasuredWidth()                    - SuperGridView.this.getPaddingLeft()                    - SuperGridView.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;     // This ArrayList is assumed to NOT be null.        ArrayList<FixedViewInfo> mFooterViewInfos;        boolean mAreAllFixedViewsSelectable;        private final boolean mIsFilterable;                private int mNormalItemHeight;        private int mFooterPosition = INVALID_POSITION;                public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos,ArrayList<FixedViewInfo> footerViewInfos, ListAdapter adapter) {            mAdapter = adapter;            mIsFilterable = adapter instanceof Filterable;            if (headerViewInfos == null) {                throw new IllegalArgumentException("headerViewInfos cannot be null");            }            if (footerViewInfos == null) {                throw new IllegalArgumentException("footerViewInfos cannot be null");            }            mHeaderViewInfos = headerViewInfos;            mFooterViewInfos = footerViewInfos;            mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);        }        public int getHeadersCount() {            return mHeaderViewInfos.size();        }                public int getFootersCount() {            return mFooterViewInfos.size();        }                @Override        public boolean isEmpty() {            return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 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;        }                public boolean removeFooter(View v) {            for (int i = 0; i < mFooterViewInfos.size(); i++) {                FixedViewInfo info = mFooterViewInfos.get(i);                if (info.view == v) {                mFooterViewInfos.remove(i);                    mAreAllFixedViewsSelectable = areAllListInfosSelectable(mFooterViewInfos);                    mDataSetObservable.notifyChanged();                    return true;                }            }            return false;        }                @Override        public int getCount() {            if (mAdapter != null) {                return getHeadersCount() * mNumColumns + getFootersCount() * mNumColumns + mAdapter.getCount();            } else {                return getHeadersCount() * mNumColumns + getFootersCount() * 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;            int normalCount = mAdapter == null ? 0 : mAdapter.getCount();            int numFootersAndPlaceholders = getHeadersCount() * mNumColumns + normalCount;            if (position < numHeadersAndPlaceholders) {                return (position % mNumColumns == 0)                        && mHeaderViewInfos.get(position / mNumColumns).isSelectable;            }                        if (position >= numFootersAndPlaceholders) {                return ((position) % mNumColumns == 0)                        && mFooterViewInfos.get((position - numFootersAndPlaceholders) / 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;            int normalCount = mAdapter == null ? 0 : mAdapter.getCount();            int numFootersAndPlaceholders = getHeadersCount() * mNumColumns + normalCount;            if (position < numHeadersAndPlaceholders) {                if (position % mNumColumns == 0) {                    return mHeaderViewInfos.get(position / mNumColumns).data;                }                return null;            }                        if (position >= numFootersAndPlaceholders) {                if ((position) % mNumColumns == 0) {                    return mFooterViewInfos.get((position - numFootersAndPlaceholders) / 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;            int normalCount = mAdapter == null ? 0 : mAdapter.getCount();            int numFootersAndPlaceholders = getHeadersCount() * mNumColumns + normalCount;            if (mAdapter != null && position >= numHeadersAndPlaceholders && position < numFootersAndPlaceholders) {                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;            int normalCount = mAdapter == null ? 0 : mAdapter.getCount();            int numFootersAndPlaceholders = getHeadersCount() * mNumColumns + normalCount;            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;                }            }                        if (position >= numFootersAndPlaceholders) {                View footerViewContainer = mFooterViewInfos                        .get((position - numFootersAndPlaceholders) / mNumColumns).viewContainer;                if ((position) % mNumColumns == 0) {                mFooterPosition = position;                    return footerViewContainer;                } else {                    if (convertView == null) {                    Log.i("info", "创建新的ConvertView " + position);                        convertView = new View(parent.getContext());                        if(mFooterPosition != INVALID_POSITION){                          convertView.setMinimumHeight(footerViewContainer.getHeight());                        }else{                          convertView.setMinimumHeight(mNormalItemHeight);                        }                    }                    // 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.setBackgroundColor(Color.BLUE);                    convertView.setVisibility(View.INVISIBLE);//                    convertView.setMinimumHeight(mNormalItemHeight);                    return convertView;                }            }                        // Adapter            final int adjPosition = position - numHeadersAndPlaceholders;            int adapterCount = 0;            if (mAdapter != null) {                adapterCount = mAdapter.getCount();                if (adjPosition < adapterCount) {                                View view = mAdapter.getView(adjPosition, convertView, parent);                if(mNormalItemHeight == 0){                mNormalItemHeight = view.getHeight();                }                Log.i("info", "单项高度:"+mNormalItemHeight);                    return view;                }            }            throw new ArrayIndexOutOfBoundsException(position);        }        @Override        public int getItemViewType(int position) {            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;            int normalCount = mAdapter == null ? 0 : mAdapter.getCount();            int numFootersAndPlaceholders = getHeadersCount() * mNumColumns + normalCount;            if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) {                // Placeholders get the last view type number                return mAdapter != null ? mAdapter.getViewTypeCount() : 1;            }            if (position >= numFootersAndPlaceholders && (position % mNumColumns != 0)) {            // Placeholders get the last view type number            return mAdapter != null ? mAdapter.getViewTypeCount() + 1 : 2;            }            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() + 2;            }            return 3;        }        @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();        }    }}


0 0
原创粉丝点击