当ListView有Header或footer时,onItemClick里的position不正确,可以用getAdapter()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void onCreate(Bundle savedInstanceState) {
     super .onCreate(savedInstanceState);
  
     setContentView(R.layout.home);
  
     mAdapter = new MyAdapter( this );
  
     mListView = (ListView) findViewById(R.id.list);
     mListView.addHeaderView(getLayoutInflater().inflate(R.layout.list_header));
     mListView.setAdapter(mAdapter);
     mListView.setOnClickListener( this );
}
  
@Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
     doSomething(mAdapter.getItem(position));
}

Google了下,发现有个老外issue过一个bug,和我遇到的问题一样,不过这个bug被RomainGuy reject掉了,理由是,你用错了,请用getAdapter。回答的太简洁了,完全没法理解,只好又去仔细研究ListView的代码,终于领会他的意思了。把其中addHeaderViewsetAdapter方法贴下来

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
  * Add a fixed view to appear at the top of the list. 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 ListView can wrap
  * the supplied cursor with one that 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) {
     if (mAdapter != null ) {
         throw new IllegalStateException(
                 "Cannot add header view to list -- setAdapter has already been called." );
     }
  
     FixedViewInfo info = new FixedViewInfo();
     info.view = v;
     info.data = data;
     info.isSelectable = isSelectable;
     mHeaderViewInfos.add(info);
}
  
/**
  * Sets the data behind this ListView.
  *
  * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
  * depending on the ListView features currently in use. For instance, adding
  * headers and/or footers will cause the adapter to be wrapped.
  *
  * @param adapter The ListAdapter which is responsible for maintaining the
  *        data backing this list and for producing a view to represent an
  *        item in that data set.
  *
  * @see #getAdapter()
  */
@Override
public void setAdapter(ListAdapter adapter) {
     if ( null != mAdapter) {
         mAdapter.unregisterDataSetObserver(mDataSetObserver);
     }
  
     resetList();
     mRecycler.clear();
  
     if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0 ) {
         mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
     } else {
         mAdapter = adapter;
     }
  
     //其它的一些代码这里省略之...
}

从代码和注释里都可以很清楚的得知,addHeaderView一定要在setAdapter之前调用,如果不是,addHeaderView会抛出一个异常。Android为什么要这样做?因为,在setAdapter的时候,会针对我遇到的这种情况(也就是说,position不正确)做些特殊的处理。setAdapter在内部判断了当前ListView是否有Header或者Footer,如果没有,就直接使用参数传进来的adapter;如果有,则用一个decorated的HeaderViewListAdapter来替换参数。这个HeaderViewListAdapter的使命,就是排除Header和Footer,让position(当然也包括getItem,getItemId等)正确返回。

分析到这里,解决方案就出来了:不要直接使用我们声明的adapter,而是用ListView里的那个decorated adapter。获取它的方法就是调用getAdapter。当然,如果ListView没有Header和Footer,直接使用声明的adapter也没有问题,不过为了方便、避免以后出错,还是统一使用decorated adapter比较好。

把onItemClick改成下面这样,就可以了

?
1
2
3
4
@Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
     doSomething(parent.getAdapter().getItem(position));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值