从源码看ListView有HeaderView时onItemClick里的position错位的问题

来源:互联网 发布:宁波百度推广公司php 编辑:程序博客网 时间:2024/05/29 04:16
        listView = (ListView) findViewById(R.id.test_lv);        headerView = LayoutInflater.from(this).inflate(R.layout.activity_position_test_headerview, null);        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, datas);        listView.addHeaderView(headerView);        listView.setAdapter(adapter);        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {                Toast.makeText(ListViewPositionTestActivity.this, "Position is : " + i, Toast.LENGTH_SHORT).show();            }        });

如上述代码,给listView添加了一个headerView,运行出来之后的position如下图所示:


会发现数据的第一项position变成了1.

我们再来看一段代码:

        listView = (ListView) findViewById(R.id.test_lv);        headerView = LayoutInflater.from(this).inflate(R.layout.activity_position_test_headerview, null);        headerView1 = LayoutInflater.from(this).inflate(R.layout.activity_position_test_headerview, null);        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, datas);        listView.addHeaderView(headerView);        listView.addHeaderView(headerView1);        listView.setAdapter(adapter);        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {                Toast.makeText(ListViewPositionTestActivity.this, "Position is : " + i, Toast.LENGTH_SHORT).show();            }        });

会发现此时数据的第一项position变成了2.(由此也可以总结出,ListView是可以添加多个HeaderView的)


接下来我们分析分析,来看看addHeaderView(View view)的源码:

    /**     * Add a fixed view to appear at the top of the list. If this method 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: When first introduced, this method could only be called before     * setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with     * {@link android.os.Build.VERSION_CODES#KITKAT}, this method may be     * called at any time. If the ListView's adapter does not extend     * {@link HeaderViewListAdapter}, it will be wrapped with a supporting     * instance of {@link WrapperListAdapter}.     *     * @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) {        final FixedViewInfo info = new FixedViewInfo();        info.view = v;        info.data = data;        info.isSelectable = isSelectable;        mHeaderViewInfos.add(info);        mAreAllItemsSelectable &= isSelectable;        // Wrap the adapter if it wasn't already wrapped.        if (mAdapter != null) {            if (!(mAdapter instanceof HeaderViewListAdapter)) {                wrapHeaderListAdapterInternal();            }            // In the case of re-adding a header view, or adding one later on,            // we need to notify the observer.            if (mDataSetObserver != null) {                mDataSetObserver.onChanged();            }        }    }

当mAdapter不为空时,执行wrapHeaderListAdapterInternal()方法,我们来接着看看源码:

    /** @hide */    protected HeaderViewListAdapter wrapHeaderListAdapterInternal(            ArrayList<ListView.FixedViewInfo> headerViewInfos,            ArrayList<ListView.FixedViewInfo> footerViewInfos,            ListAdapter adapter) {        return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter);    }    /** @hide */    protected void wrapHeaderListAdapterInternal() {        mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);    }
可以看到,mAdapter变成了HeaderViewListAdapter的对象,那么HeaderViewListAdapter又是什么呢?

    public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,                                 ArrayList<ListView.FixedViewInfo> footerViewInfos,                                 ListAdapter adapter) {        mAdapter = adapter;        mIsFilterable = adapter instanceof Filterable;        if (headerViewInfos == null) {            mHeaderViewInfos = EMPTY_INFO_LIST;        } else {            mHeaderViewInfos = headerViewInfos;        }        if (footerViewInfos == null) {            mFooterViewInfos = EMPTY_INFO_LIST;        } else {            mFooterViewInfos = footerViewInfos;        }        mAreAllFixedViewsSelectable =                areAllListInfosSelectable(mHeaderViewInfos)                && areAllListInfosSelectable(mFooterViewInfos);    }
上面的HeaderViewListAdapter的构造方法,将headerViewInfos、footerViewInfos和之前的adapter传了进来,接下来我们看看这个类中的getCount()方法:

    public int getCount() {        if (mAdapter != null) {            return getFootersCount() + getHeadersCount() + mAdapter.getCount();        } else {            return getFootersCount() + getHeadersCount();        }    }

    public int getHeadersCount() {        return mHeaderViewInfos.size();    }    public int getFootersCount() {        return mFooterViewInfos.size();    }
发现了什么?上面的mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);

方法返回的mAdapter的getCount()是  原先adapter的getCount() 和 HeaderView的总数 FooterView的总数 之和!

这也就解释了为什么上面的两张图的position错位的原因。同理FooterView也是要占据position的值哦~



这里有几点要注意:

1. addHeaderView(View v, Object data, boolean isSelectable) 中有一个参数isSelectable,当这个值设置为true的时候,该HeaderView不能点击,点击无反应。FooterView同理。

2. 看addHeaderView(View v, Object data, boolean isSelectable)方法的注释,你会发现,在Android4.3(即KITKAT,API 19)之前,addHeaderView方法必须在setAdapter方法之前调用,而在API 19之后,addHeaderView方法可以在任何时间调用。所以在项目中要注意这一点。



阅读全文
0 0
原创粉丝点击