Reputation: 4124
I am using a listview to present a gridview like activity. There is an OutOfBoundsException. here is the log.
03-21 11:47:13.106: E/AndroidRuntime(597): FATAL EXCEPTION: main
03-21 11:47:13.106: E/AndroidRuntime(597): java.lang.IndexOutOfBoundsException: Invalid index 1, size is 1
03-21 11:47:13.106: E/AndroidRuntime(597): at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
03-21 11:47:13.106: E/AndroidRuntime(597): at java.util.ArrayList.get(ArrayList.java:304)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.HeaderViewListAdapter.getView(HeaderViewListAdapter.java:225)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.AbsListView.obtainView(AbsListView.java:2022)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.ListView.measureHeightOfChildren(ListView.java:1244)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.ListView.onMeasure(ListView.java:1155)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.View.measure(View.java:12752)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4698)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.FrameLayout.onMeasure(FrameLayout.java:293)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.View.measure(View.java:12752)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4698)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1369)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.LinearLayout.measureVertical(LinearLayout.java:660)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.LinearLayout.onMeasure(LinearLayout.java:553)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.View.measure(View.java:12752)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.RelativeLayout.measureChild(RelativeLayout.java:579)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:392)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.View.measure(View.java:12752)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4698)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.FrameLayout.onMeasure(FrameLayout.java:293)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.View.measure(View.java:12752)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4698)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1369)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.LinearLayout.measureVertical(LinearLayout.java:660)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.LinearLayout.onMeasure(LinearLayout.java:553)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.View.measure(View.java:12752)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4698)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.widget.FrameLayout.onMeasure(FrameLayout.java:293)
03-21 11:47:13.106: E/AndroidRuntime(597): at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2265)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.View.measure(View.java:12752)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1074)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2462)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.os.Handler.dispatchMessage(Handler.java:99)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.os.Looper.loop(Looper.java:137)
03-21 11:47:13.106: E/AndroidRuntime(597): at android.app.ActivityThread.main(ActivityThread.java:4486)
03-21 11:47:13.106: E/AndroidRuntime(597): at java.lang.reflect.Method.invokeNative(Native Method)
03-21 11:47:13.106: E/AndroidRuntime(597): at java.lang.reflect.Method.invoke(Method.java:511)
03-21 11:47:13.106: E/AndroidRuntime(597): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
03-21 11:47:13.106: E/AndroidRuntime(597): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
03-21 11:47:13.106: E/AndroidRuntime(597): at dalvik.system.NativeStart.main(Native Method)
My ListToGridAdapter is below:
public class ListToGridAdapter extends BaseAdapter implements OnClickListener, OnLongClickListener {
/**
* The wrapper content --> adapter
*/
private SimpleAdapter mAdapter = null;
/**
* The number columns
*/
private int mColumns = 1;
/**
* The real onItemClickListener
*/
private OnGridItemClickListener mGridListener;
/**
* The real onItemLongClickListener
*/
private OnGridItemLongClickListener mGridLongClickListener;
/**
* The Context Object
*/
private Context mContext;
private int sidebarPixals = 0;
private int sideBlankPixals = 0;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (mColumns == 1) {
View child = mAdapter.getView(position, convertView, parent);
setOnItemClickListenerIfNeed(child, position, position);
setOnItemLongClickListenerIfNeed(child, position, position);
// no need
return child;
}
return bindGridView(position, convertView, parent);
}
protected View bindGridView(int position, View convertView, ViewGroup parent) {
ViewGroup root = null;
if (convertView == null || !(convertView instanceof ViewGroup)) {
root = createRoot();
root.setClickable(false);
root.setFocusable(false);
} else {
root = (ViewGroup) convertView;
}
// columns = 3
// pos --> real pos
// 0 --> 0 1 2
// 1 --> 3 4 5
// 2 --> 6 7 8
// so do this
return bindView(root, position);
}
protected ViewGroup bindView(ViewGroup root, int pos) {
final int count = mAdapter.getCount();
final int childCount = root.getChildCount();
for (int i = 0; i < mColumns; i++) {
// real position
int index = mColumns * pos + i;
// 不足一行时,直接返回现有状态
if (index == count) {
// fix bug : root是有可能是系统缓存的root,这里直接返回root将导致如果此行数量
// 不够columns,就会使用缓存的root里的child,为了避免这个问题,必须
// 将缓存的child移除。
removeCacheChild(root, index);
break;
} else if (index > count) {
throw new UnknownError("unknowError");
}
View child = mAdapter.getView(index, root.getChildAt(i), root);
setOnItemLongClickListenerIfNeed(child, pos, index);
setOnItemClickListenerIfNeed(child, pos, index);
if (childCount == mColumns) {
// root已经包含了Item,就没有必要继续添加了。
continue;
} else {
if (i == 0 && childCount != 0) {
// 如果root已经包含item,但是并不全,这里就移除重新添加
// FIXME 也许还有更好的方案
root.removeAllViews();
}
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
// LinearLayout.LayoutParams.WRAP_CONTENT,
(mContext.getResources().getDisplayMetrics().widthPixels - sidebarPixals - sideBlankPixals)
/ mColumns,
LinearLayout.LayoutParams.WRAP_CONTENT);
child.setFocusable(true);
child.setClickable(true);
if (child instanceof ViewGroup) {
((ViewGroup) child)
.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
}
root.addView(child, lp);
}
}
return root;
}
/**
* remove child view in cache of root
*/
private void removeCacheChild(ViewGroup root, int index) {
int realColumns = (index - 1) % mColumns;
int moveCount = (mColumns - 1) - realColumns;
for (int i = 0; i < moveCount; i++) {
final View child = root.getChildAt((mColumns - 1 - i));
if (child != null) {
root.removeView(child);
}
}
}
/**
* create root for item of ListView
*/
protected ViewGroup createRoot() {
LinearLayout root = new LinearLayout(mContext);
root.setOrientation(LinearLayout.HORIZONTAL);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
root.setLayoutParams(lp);
return root;
}
private boolean setOnItemClickListenerIfNeed(View child, int pos,
int realPos) {
if (mGridListener == null) {
return false;
}
child.setOnClickListener(this);
child.setTag(R.id.pos, pos);
child.setTag(R.id.realPos, realPos);
return true;
}
private boolean setOnItemLongClickListenerIfNeed(View child, int pos,
int realPos) {
if (mGridLongClickListener == null) {
return false;
}
child.setOnLongClickListener(this);
child.setTag(R.id.pos, pos);
child.setTag(R.id.realPos, realPos);
return true;
}
public void setOnItemClickListener(OnGridItemClickListener listener) {
this.mGridListener = listener;
}
public void setOnItemLongClickListener(OnGridItemLongClickListener listener){
this.mGridLongClickListener = listener;
}
public void setNumColumns(int columns) {
this.mColumns = columns;
}
public int getNumColumns() {
return mColumns;
}
public ListToGridAdapter(Context context, SimpleAdapter adapter) {
this.mContext = context;
this.mAdapter = adapter;
int model = YoutubeAPI.getKindleFireModel();
if (model == 1){
sideBlankPixals = 10 * 2;
sidebarPixals = 0;
} else if (model == 2){
sideBlankPixals = 10 * 2;
sidebarPixals = 60;
} else if (model == 3){
sideBlankPixals = (int)(10 * 2 * 1.5);
sidebarPixals = 78;
} else if (model == 4){
sideBlankPixals = (int)(10 * 2 * 1.5);
sidebarPixals = 90;
}
}
@Override
public int getCount() {
int count = (int) Math.ceil(mAdapter.getCount() / (double) mColumns);
return count;
}
@Override
public Object getItem(int position) {
return mAdapter.getItem(position);
}
@Override
public long getItemId(int position) {
return mAdapter.getItemId(position);
}
@Override
public boolean hasStableIds() {
return mAdapter.hasStableIds();
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
mAdapter.registerDataSetObserver(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
mAdapter.unregisterDataSetObserver(observer);
}
@Override
public boolean areAllItemsEnabled() {
return mAdapter.areAllItemsEnabled();
}
@Override
public boolean isEnabled(int position) {
return mAdapter.isEnabled(position);
}
@Override
public int getItemViewType(int position) {
return mAdapter.getItemViewType(position);
}
@Override
public int getViewTypeCount() {
return mAdapter.getViewTypeCount();
}
@Override
public boolean isEmpty() {
return mAdapter.isEmpty();
}
public ListAdapter getWrappedAdapter() {
return mAdapter;
}
public static interface OnGridItemClickListener {
public void onItemClick(int pos, int realPos);
}
public static interface OnGridItemLongClickListener{
public void onItemLongClick(int pos, int realPos);
}
@Override
public void onClick(View v) {
if (mGridListener == null) {
// no need to feedback
}
int pos = (Integer) v.getTag(R.id.pos);
int realPos = (Integer) v.getTag(R.id.realPos);
mGridListener.onItemClick(pos, realPos);
}
@Override
public boolean onLongClick(View v) {
if (mGridLongClickListener == null) {
// no need to feedback
}
int pos = (Integer) v.getTag(R.id.pos);
int realPos = (Integer) v.getTag(R.id.realPos);
mGridLongClickListener.onItemLongClick(pos, realPos);
return true;
}
}
in the activity,
ListToGridAdapter mAdapter = new ListToGridAdapter(this, mVideoGridviewAdapter);
mAdapter.setNumColumns(3);
pullToRefreshListview.setAdapter(mAdapter);
the mVideoGridviewAdapter is a simpleAdapter.
Where is the problem? It seems is the listview's footer, But I don't know how to fix it.
==== EDIT =====
In the activity, I add some AsyncTask to refresh the listview. In the onPostExecute
I add some item into the listview, make the listview longer, and change the listview's footer view from View.VISIBLE
to View.INVISIBLE
, does this matter?
Upvotes: 1
Views: 1599
Reputation: 4124
I figured it out myself.
When change the fragment( which contains the listview), the listview will empty itself first, then the listview will remenber the index when it scrolls, then, when reload the listview, IndexOutOfBoundsException
will happen.
So the solution is: reset the listview's adapter when destroy, code snippet is below
public void onDestroyView() {
if (listview != null && mVideoGridviewAdapter != null) {
ListToGridWrapper<GridviewAdapter> mAdapter = new ListToGridWrapper<GridviewAdapter>(getActivity(), mVideoGridviewAdapter);
mAdapter.setNumColumns(2);
listview.setAdapter(mAdapter);
}
super.onDestroyView();
}
Upvotes: 2