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

        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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
onItemClick方法中添加AlertDialog的步骤如下: 1. 创建AlertDialog.Builder对象 ``` AlertDialog.Builder builder = new AlertDialog.Builder(context); ``` 2. 设置AlertDialog的标题、消息内容和按钮 ``` builder.setTitle("提示"); builder.setMessage("确定要删除该项吗?"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 确定按钮的点击事件 } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 取消按钮的点击事件 } }); ``` 3. 创建AlertDialog对象并显示 ``` AlertDialog dialog = builder.create(); dialog.show(); ``` 完整的示例代码如下: ``` listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("提示"); builder.setMessage("确定要删除该项吗?"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 确定按钮的点击事件 } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 取消按钮的点击事件 } }); AlertDialog dialog = builder.create(); dialog.show(); } }); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值