Instantly share code, notes, and snippets.
Created
May 7, 2015 14:30
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save xxnjdlys/19ddea469b549cda8b49 to your computer and use it in GitHub Desktop.
something's wrong with gridview item position.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package in.srain.cube.demo.ui; | |
/** | |
* Created by sadieyu | |
* Date: 15-5-7. | |
* Time: 下午10:12 | |
*/ | |
import android.annotation.TargetApi; | |
import android.content.Context; | |
import android.database.DataSetObservable; | |
import android.database.DataSetObserver; | |
import android.os.Build; | |
import android.util.AttributeSet; | |
import android.util.Log; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.AbsListView; | |
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.lang.reflect.Field; | |
import java.util.ArrayList; | |
/* | |
* 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. | |
* | |
* A {@link GridView} that supports adding header rows in a | |
* very similar way to {@link android.widget.ListView}. | |
* See {@link GridViewWithHeaderAndFooter#addHeaderView(View, Object, boolean)} | |
* See {@link GridViewWithHeaderAndFooter#addFooterView(View, Object, boolean)} | |
*/ | |
public class GridViewWithHeaderAndFooter extends GridView implements android.widget.AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener{ | |
public static boolean DEBUG = false; | |
private OnItemClickListener mOnItemClickListener; | |
private OnItemLongClickListener mOnItemLongClickListener; | |
/** | |
* 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 int mNumColumns = AUTO_FIT; | |
private View mViewForMeasureRowHeight = null; | |
private int mRowHeight = -1; | |
private static final String LOG_TAG = "SADIEYU"; | |
private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>(); | |
private ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>(); | |
private void initHeaderGridView() { | |
} | |
public GridViewWithHeaderAndFooter(Context context) { | |
super(context); | |
initHeaderGridView(); | |
} | |
public GridViewWithHeaderAndFooter(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
initHeaderGridView(); | |
} | |
public GridViewWithHeaderAndFooter(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(getNumColumnsCompatible()); | |
((HeaderViewGridAdapter) adapter).setRowHeight(getRowHeight()); | |
} | |
} | |
@Override | |
public void setClipChildren(boolean clipChildren) { | |
// Ignore, since the header rows depend on not being clipped | |
} | |
/** | |
* Do not call this method unless you know how it works. | |
* | |
* @param clipChildren | |
*/ | |
public void setClipChildrenSupper(boolean clipChildren) { | |
super.setClipChildren(false); | |
} | |
/** | |
* 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); | |
} | |
/** | |
* 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."); | |
} | |
ViewGroup.LayoutParams lyp = v.getLayoutParams(); | |
FixedViewInfo info = new FixedViewInfo(); | |
FrameLayout fl = new FullWidthFixedViewLayout(getContext()); | |
if (lyp != null) { | |
v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height)); | |
fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height)); | |
} | |
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(); | |
} | |
} | |
public void addFooterView(View v) { | |
addFooterView(v, null, true); | |
} | |
public void addFooterView(View v, Object data, boolean isSelectable) { | |
ListAdapter mAdapter = getAdapter(); | |
if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) { | |
throw new IllegalStateException( | |
"Cannot add header view to grid -- setAdapter has already been called."); | |
} | |
ViewGroup.LayoutParams lyp = v.getLayoutParams(); | |
FixedViewInfo info = new FixedViewInfo(); | |
FrameLayout fl = new FullWidthFixedViewLayout(getContext()); | |
if (lyp != null) { | |
v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height)); | |
fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height)); | |
} | |
fl.addView(v); | |
info.view = v; | |
info.viewContainer = fl; | |
info.data = data; | |
info.isSelectable = isSelectable; | |
mFooterViewInfos.add(info); | |
if (mAdapter != null) { | |
((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged(); | |
} | |
} | |
public int getHeaderViewCount() { | |
return mHeaderViewInfos.size(); | |
} | |
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 footer 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; | |
} | |
} | |
} | |
@TargetApi(11) | |
private int getNumColumnsCompatible() { | |
if (Build.VERSION.SDK_INT >= 11) { | |
return super.getNumColumns(); | |
} else { | |
try { | |
Field numColumns = GridView.class.getDeclaredField("mNumColumns"); | |
numColumns.setAccessible(true); | |
return numColumns.getInt(this); | |
} catch (Exception e) { | |
if (mNumColumns != -1) { | |
return mNumColumns; | |
} | |
throw new RuntimeException("Can not determine the mNumColumns for this API platform, please call setNumColumns to set it."); | |
} | |
} | |
} | |
@TargetApi(16) | |
private int getColumnWidthCompatible() { | |
if (Build.VERSION.SDK_INT >= 16) { | |
return super.getColumnWidth(); | |
} else { | |
try { | |
Field numColumns = GridView.class.getDeclaredField("mColumnWidth"); | |
numColumns.setAccessible(true); | |
return numColumns.getInt(this); | |
} catch (NoSuchFieldException e) { | |
throw new RuntimeException(e); | |
} catch (IllegalAccessException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} | |
@Override | |
protected void onDetachedFromWindow() { | |
super.onDetachedFromWindow(); | |
mViewForMeasureRowHeight = null; | |
} | |
public void invalidateRowHeight() { | |
mRowHeight = -1; | |
} | |
public int getHeaderHeight(int row) { | |
if (row >= 0) { | |
return mHeaderViewInfos.get(row).view.getMeasuredHeight(); | |
} | |
return 0; | |
} | |
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) | |
public int getVerticalSpacing(){ | |
int value = 0; | |
try { | |
int currentapiVersion = android.os.Build.VERSION.SDK_INT; | |
if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){ | |
Field field = GridView.class.getDeclaredField("mVerticalSpacing"); | |
field.setAccessible(true); | |
value = field.getInt(this); | |
} else{ | |
value = super.getVerticalSpacing(); | |
} | |
}catch (Exception ex){ | |
} | |
return value; | |
} | |
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) | |
public int getHorizontalSpacing(){ | |
int value = 0; | |
try { | |
int currentapiVersion = android.os.Build.VERSION.SDK_INT; | |
if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){ | |
Field field = GridView.class.getDeclaredField("mHorizontalSpacing"); | |
field.setAccessible(true); | |
value = field.getInt(this); | |
} else{ | |
value = super.getHorizontalSpacing(); | |
} | |
}catch (Exception ex){ | |
} | |
return value; | |
} | |
public int getRowHeight() { | |
if (mRowHeight > 0) { | |
return mRowHeight; | |
} | |
ListAdapter adapter = getAdapter(); | |
int numColumns = getNumColumnsCompatible(); | |
// adapter has not been set or has no views in it; | |
if (adapter == null || adapter.getCount() <= numColumns * (mHeaderViewInfos.size() + mFooterViewInfos.size())) { | |
return -1; | |
} | |
int mColumnWidth = getColumnWidthCompatible(); | |
View view = getAdapter().getView(numColumns * mHeaderViewInfos.size(), mViewForMeasureRowHeight, this); | |
AbsListView.LayoutParams p = (AbsListView.LayoutParams) view.getLayoutParams(); | |
if (p == null) { | |
p = new AbsListView.LayoutParams(-1, -2, 0); | |
view.setLayoutParams(p); | |
} | |
int childHeightSpec = getChildMeasureSpec( | |
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height); | |
int childWidthSpec = getChildMeasureSpec( | |
MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width); | |
view.measure(childWidthSpec, childHeightSpec); | |
mViewForMeasureRowHeight = view; | |
mRowHeight = view.getMeasuredHeight(); | |
return mRowHeight; | |
} | |
@TargetApi(11) | |
public void tryToScrollToBottomSmoothly() { | |
int lastPos = getAdapter().getCount() - 1; | |
if (Build.VERSION.SDK_INT >= 11) { | |
smoothScrollToPositionFromTop(lastPos, 0); | |
} else { | |
setSelection(lastPos); | |
} | |
} | |
@TargetApi(11) | |
public void tryToScrollToBottomSmoothly(int duration) { | |
int lastPos = getAdapter().getCount() - 1; | |
if (Build.VERSION.SDK_INT >= 11) { | |
smoothScrollToPositionFromTop(lastPos, 0, duration); | |
} else { | |
setSelection(lastPos); | |
} | |
} | |
@Override | |
public void setAdapter(ListAdapter adapter) { | |
if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) { | |
HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); | |
int numColumns = getNumColumnsCompatible(); | |
if (numColumns > 1) { | |
headerViewGridAdapter.setNumColumns(numColumns); | |
} | |
headerViewGridAdapter.setRowHeight(getRowHeight()); | |
super.setAdapter(headerViewGridAdapter); | |
} else { | |
super.setAdapter(adapter); | |
} | |
} | |
/** | |
* full width | |
*/ | |
private class FullWidthFixedViewLayout extends FrameLayout { | |
public FullWidthFixedViewLayout(Context context) { | |
super(context); | |
} | |
@Override | |
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { | |
int realLeft = GridViewWithHeaderAndFooter.this.getPaddingLeft() + getPaddingLeft(); | |
// Try to make where it should be, from left, full width | |
if (realLeft != left) { | |
offsetLeftAndRight(realLeft - left); | |
} | |
super.onLayout(changed, left, top, right, bottom); | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
int targetWidth = GridViewWithHeaderAndFooter.this.getMeasuredWidth() | |
- GridViewWithHeaderAndFooter.this.getPaddingLeft() | |
- GridViewWithHeaderAndFooter.this.getPaddingRight(); | |
widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, | |
MeasureSpec.getMode(widthMeasureSpec)); | |
super.onMeasure(widthMeasureSpec, heightMeasureSpec); | |
} | |
} | |
@Override | |
public void setNumColumns(int numColumns) { | |
super.setNumColumns(numColumns); | |
mNumColumns = numColumns; | |
ListAdapter adapter = getAdapter(); | |
if (adapter != null && adapter instanceof HeaderViewGridAdapter) { | |
((HeaderViewGridAdapter) adapter).setNumColumns(numColumns); | |
} | |
} | |
/** | |
* 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; | |
static final ArrayList<FixedViewInfo> EMPTY_INFO_LIST = | |
new ArrayList<FixedViewInfo>(); | |
// This ArrayList is assumed to NOT be null. | |
ArrayList<FixedViewInfo> mHeaderViewInfos; | |
ArrayList<FixedViewInfo> mFooterViewInfos; | |
private int mNumColumns = 1; | |
private int mRowHeight = -1; | |
boolean mAreAllFixedViewsSelectable; | |
private final boolean mIsFilterable; | |
private boolean mCachePlaceHoldView = true; | |
// From Recycle Bin or calling getView, this a question... | |
private boolean mCacheFirstHeaderView = false; | |
public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ArrayList<FixedViewInfo> footViewInfos, ListAdapter adapter) { | |
mAdapter = adapter; | |
mIsFilterable = adapter instanceof Filterable; | |
if (headerViewInfos == null) { | |
mHeaderViewInfos = EMPTY_INFO_LIST; | |
} else { | |
mHeaderViewInfos = headerViewInfos; | |
} | |
if (footViewInfos == null) { | |
mFooterViewInfos = EMPTY_INFO_LIST; | |
} else { | |
mFooterViewInfos = footViewInfos; | |
} | |
mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos) | |
&& areAllListInfosSelectable(mFooterViewInfos); | |
} | |
public void setNumColumns(int numColumns) { | |
if (numColumns < 1) { | |
return; | |
} | |
if (mNumColumns != numColumns) { | |
mNumColumns = numColumns; | |
notifyDataSetChanged(); | |
} | |
} | |
public void setRowHeight(int height) { | |
mRowHeight = height; | |
} | |
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; | |
} | |
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) && areAllListInfosSelectable(mFooterViewInfos); | |
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(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); | |
mDataSetObservable.notifyChanged(); | |
return true; | |
} | |
} | |
return false; | |
} | |
@Override | |
public int getCount() { | |
if (mAdapter != null) { | |
return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount(); | |
} else { | |
return (getFootersCount() + getHeadersCount()) * mNumColumns; | |
} | |
} | |
@Override | |
public boolean areAllItemsEnabled() { | |
if (mAdapter != null) { | |
return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled(); | |
} else { | |
return true; | |
} | |
} | |
private int getAdapterAndPlaceHolderCount() { | |
final int adapterCount = (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns); | |
return adapterCount; | |
} | |
@Override | |
public boolean isEnabled(int position) { | |
// Header (negative positions will throw an IndexOutOfBoundsException) | |
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 = getAdapterAndPlaceHolderCount(); | |
if (adjPosition < adapterCount) { | |
return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition); | |
} | |
} | |
// Footer (off-limits positions will throw an IndexOutOfBoundsException) | |
final int footerPosition = adjPosition - adapterCount; | |
return footerPosition % mNumColumns == 0 | |
&& mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable; | |
} | |
@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 = getAdapterAndPlaceHolderCount(); | |
if (adjPosition < adapterCount) { | |
if (adjPosition < mAdapter.getCount()) { | |
return mAdapter.getItem(adjPosition); | |
} else { | |
return null; | |
} | |
} | |
} | |
// Footer (off-limits positions will throw an IndexOutOfBoundsException) | |
final int footerPosition = adjPosition - adapterCount; | |
if (footerPosition % mNumColumns == 0) { | |
return mFooterViewInfos.get(footerPosition).data; | |
} else { | |
return null; | |
} | |
} | |
@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) { | |
if (DEBUG) { | |
Log.d(LOG_TAG, String.format("getView: %s, reused: %s", position, convertView == null)); | |
} | |
// 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 = getAdapterAndPlaceHolderCount(); | |
if (adjPosition < adapterCount) { | |
if (adjPosition < mAdapter.getCount()) { | |
View view = mAdapter.getView(adjPosition, convertView, parent); | |
return view; | |
} else { | |
if (convertView == null) { | |
convertView = new View(parent.getContext()); | |
} | |
convertView.setVisibility(View.INVISIBLE); | |
convertView.setMinimumHeight(mRowHeight); | |
return convertView; | |
} | |
} | |
} | |
// Footer | |
final int footerPosition = adjPosition - adapterCount; | |
if (footerPosition < getCount()) { | |
View footViewContainer = mFooterViewInfos | |
.get(footerPosition / mNumColumns).viewContainer; | |
if (position % mNumColumns == 0) { | |
return footViewContainer; | |
} 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(footViewContainer.getHeight()); | |
return convertView; | |
} | |
} | |
throw new ArrayIndexOutOfBoundsException(position); | |
} | |
@Override | |
public int getItemViewType(int position) { | |
final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; | |
final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1; | |
int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; | |
if (mCachePlaceHoldView) { | |
// Header | |
if (position < numHeadersAndPlaceholders) { | |
if (position == 0) { | |
if (mCacheFirstHeaderView) { | |
type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1; | |
} | |
} | |
if (position % mNumColumns != 0) { | |
type = adapterViewTypeStart + (position / mNumColumns + 1); | |
} | |
} | |
} | |
// Adapter | |
final int adjPosition = position - numHeadersAndPlaceholders; | |
int adapterCount = 0; | |
if (mAdapter != null) { | |
adapterCount = getAdapterAndPlaceHolderCount(); | |
if (adjPosition >= 0 && adjPosition < adapterCount) { | |
if (adjPosition < mAdapter.getCount()) { | |
type = mAdapter.getItemViewType(adjPosition); | |
} else { | |
if (mCachePlaceHoldView) { | |
type = adapterViewTypeStart + mHeaderViewInfos.size() + 1; | |
} | |
} | |
} | |
} | |
if (mCachePlaceHoldView) { | |
// Footer | |
final int footerPosition = adjPosition - adapterCount; | |
if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) { | |
type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1); | |
} | |
} | |
if (DEBUG) { | |
Log.d(LOG_TAG, String.format("getItemViewType: pos: %s, result: %s", position, type, mCachePlaceHoldView, mCacheFirstHeaderView)); | |
} | |
return type; | |
} | |
/** | |
* content view, content view holder, header[0], header and footer placeholder(s) | |
* | |
* @return | |
*/ | |
@Override | |
public int getViewTypeCount() { | |
int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount(); | |
if (mCachePlaceHoldView) { | |
int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size(); | |
if (mCacheFirstHeaderView) { | |
offset += 1; | |
} | |
count += offset; | |
} | |
if (DEBUG) { | |
Log.d(LOG_TAG, String.format("getViewTypeCount: %s", count)); | |
} | |
return count; | |
} | |
@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(); | |
} | |
} | |
@Override | |
public void setOnItemClickListener(OnItemClickListener l) { | |
mOnItemClickListener = l; | |
super.setOnItemClickListener(this); | |
} | |
@Override | |
public void setOnItemLongClickListener(OnItemLongClickListener listener) { | |
mOnItemLongClickListener = listener; | |
super.setOnItemLongClickListener(this); | |
} | |
@Override | |
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { | |
if (mOnItemClickListener != null) { | |
int resPos = position - getHeaderViewCount() * getNumColumnsCompat(); | |
if (resPos >= 0) { | |
mOnItemClickListener.onItemClick(parent, view, resPos, id); | |
} | |
} | |
} | |
@Override | |
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { | |
if (mOnItemLongClickListener != null) { | |
int resPos = position - getHeaderViewCount() * getNumColumnsCompat(); | |
if (resPos >= 0) { | |
mOnItemLongClickListener.onItemLongClick(parent, view, resPos, id); | |
} | |
} | |
return true; | |
} | |
private int getNumColumnsCompat() { | |
if (Build.VERSION.SDK_INT >= 11) { | |
return getNumColumnsCompatFrom11(); | |
} else { | |
try { | |
Field numColumns = getClass().getSuperclass().getDeclaredField("mNumColumns"); | |
numColumns.setAccessible(true); | |
return numColumns.getInt(this); | |
} catch (Exception e) { | |
return 1; | |
} | |
} | |
} | |
@TargetApi(Build.VERSION_CODES.HONEYCOMB) | |
private int getNumColumnsCompatFrom11() { | |
return getNumColumns(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment