1

我正在使用列表视图来呈现类似网格视图的活动。有一个 OutOfBoundsException。这是日志。

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)

我的 ListToGridAdapter 如下:

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;
    }

}

在活动中,

ListToGridAdapter mAdapter = new ListToGridAdapter(this, mVideoGridviewAdapter);
mAdapter.setNumColumns(3);

pullToRefreshListview.setAdapter(mAdapter);

mVideoGridviewAdapter 是一个 simpleAdapter。

哪里有问题?似乎是列表视图的页脚,但我不知道如何修复它。

==== 编辑 =====

在活动中,我添加了一些 AsyncTask 来刷新列表视图。在onPostExecute我将一些项目添加到列表视图中,使列表视图更长,并将列表视图的页脚视图从 更改View.VISIBLEView.INVISIBLE,这有关系吗?

4

1 回答 1

2

我自己想通了。

当更改片段(包含列表视图)时,列表视图会先清空自己,然后列表视图会在滚动时记住索引,然后重新加载列表视图时IndexOutOfBoundsException会发生。

所以解决方案是:在destroy时重置listview的适配器,代码片段如下

public void onDestroyView() {
    if (listview != null && mVideoGridviewAdapter != null) {
        ListToGridWrapper<GridviewAdapter> mAdapter = new ListToGridWrapper<GridviewAdapter>(getActivity(), mVideoGridviewAdapter);
        mAdapter.setNumColumns(2);
        listview.setAdapter(mAdapter);
    }
    super.onDestroyView();
}
于 2014-11-24T08:03:13.247 回答