正确处理listview的position
当ListView包含有HeaderView或FooterView时,传入getView或者onItemClick的position是怎样的,这是个值得探讨的问题
先列出错误的用法
定义:
private MyAdapter mAdapter; /**
* 包含数据的list
*/
private List<String> mDataList1 = new ArrayList<String>();
错误用法一:
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String item = (String) mDataList1.get(position);
// doSomething...
}
错误用法二:
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String item = (String) mAdapter.getItem(position);
// doSomething...
}
当ListView没有包含HeaderView和FooterView的时候,上面的用法没有问题,一旦包含,那么获取的数据项可能不准。因为此时传入的position是包含了HeaderView和FooterView的索引的:
mListView.addHeaderView(headerView);
mListView.addFooterView(footerView); mAdapter = new MyAdapter();
mAdapter.setDataList1(mDataList1);
mListView.setAdapter(mAdapter); mListView.setOnItemClickListener(this);
... @Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String item = (String) mAdapter.getItem(position);
// 当position=1的时候,取出的item是处在索引0位置的数据
}
如果按照上面的方式编码,则点击列表中的任意一项,获取的数据项始终是position-1项。即这里的position其实是一个包含了HeaderViews和FooterViews,以及我们的DataList的大List中的索引。
那么正确获取数据项的方法是:
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String item = (String) adapterView.getAdapter().getItem(position);
// doSomething...
}
当然你可以用判断position==0,但是如果包含有多个HeaderView或者FooterView,这样判断既麻烦也容易出错。按照上面的方法做,无需关心position值是什么,都可以正确获取数据项,Android已经帮我们处理了所有的情况。
看起来AdapterView.getAdapter().getItem()与Adapter.getItem()没什么不同,但实际上,当ListView包含了HeaderView的时候,AdapterView.getAdapter()获取的Adapter不是我们定义的Adapter。
为了避免下面各种adapter的混淆,命名我们的adapter为myAdapter。
来看下ListView.setAdapter的源码,看一下Android对我们的myAdapter做了什么:
// ListView.java
...
/**
* 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 (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
} resetList();
mRecycler.clear(); if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
...
...
可以很清楚的看到,当调用ListView.setAdapter的时候,会先判断是否已经包含了HeaderView和FooterView,如果包含,则ListView新建一个包装类HeaderViewListAdapter,包含myAdapter,然后ListView内部的另一个adapter引用(AbsListView.mAdapter)指向这个对象,myAdapter并没有被真的改变。
那么当ListView包含了HeaderView的时候,调用的getItem方法又有什么不同?来看看HeaderViewListAdapter.getItem(),源码如下:
// HeaderViewListAdapter.java
... private final ListAdapter mAdapter; ... 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);
} ... public Object getItem(int position) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).data;
} // Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItem(adjPosition);
}
} // Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).data;
} ...
该方法对position的各种情况做了判断,如果包含有HeaderViews,则会先从position减掉HeaderView的size。看这一句:
return mAdapter.getItem(adjPosition);
这里的mAdapter,通过构造函数HeaderViewListAdapter赋值,结合ListView.setAdapter()源码可以知道就是myAdapter,所以此时的mAdapter.getItem=myAdapter.getItem,传入的position范围是0~DataList.size()。
需要注意的是AdapterView.getCount()返回的数据是包含有HeaderView和FooterView的个数的:
public int getCount() {
if (mAdapter != null) {
return getFootersCount() + getHeadersCount() + mAdapter.getCount();
} else {
return getFootersCount() + getHeadersCount();
}
}
那么,在myAdapter中的getView,以及getItem传入的position为什么没有受到影响呢?原因是类似的。
ListView最终在渲染item布局的时候(具体流程不在这里解释),会调用mAdapter.getView,此处的mAdapter,包含HeaderView的时候是HeaderViewListAdapter,所以还是直接看HeaderViewListAdapter.getView的源码:
// HeaderViewListAdapter.java
...
public View getView(int position, View convertView, ViewGroup parent) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).view;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getView(adjPosition, convertView, parent);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).view;
}
对于position的处理同getItem(),所以原因也很明了了。
了解了position与HeaderView之间的关系后,在编写这部分代码的时候就应当特别注意一点:addHeaderView与addFooterView必须在setAdapter之前被调用。因为setAdapter中要对headers和footers做判断的!
不过即使你粗心了,Android也抛异常会提醒你:
Caused
by: java.lang.IllegalStateException: Cannot add header view to list — setAdapter has already been called.
正确处理listview的position的更多相关文章
- ListView.setSelection(position)不起作用
选择同事列表页面,在Adapter里设置复选框背景时调用了notifyDataSetChanged(),阻碍了UI线程,因此在设置ListView.setSelection(position)时不起作 ...
- ListView OnItemClickListener position 索引不正确
在使用ListView添加如下代码时 listview.setOnItemClickListener(new OnItemClickListener() { @Override public void ...
- ListView 的position和id的区别
我们在使用ListView的时候,一般都会为ListView添加一个响应事件android.widget.AdapterView.OnItemClickListener.本文主要在于对OnItemCl ...
- 【转】android中ListView的定位:使用setSelectionFromTop实现ListView的position的保持
如果一个ListView太长,有时我们希望ListView在从其他界面返回的时候能够恢复上次查看的位置,这就涉及到ListView的定位问题: 解决的办法如下: 1 2 3 4 5 6 7 // 保存 ...
- ListView的position的保持
需求场景: 一个ListView页面,滑动阅读到某一位置,然后退出页面,下次再进入页面的时候,想要直接滑动到上次阅读的位置. 方案1: 页面退出的时候,ListView.getFirstVisible ...
- ScrollView与ListView合用(正确计算Listview的高度)的问题解决
最近做项目中用到ScrollView和ListView一起使用的问题,显示的时候ListView不能完全正确的显示,查了好多资料终于成功解决: 首先,ListView不能直接用,要自定义一个,然后 ...
- listview 模仿用户点击事件。
正确的方法 gvFlow.post(new Runnable() { @Override public void run() { gvFlow.performItemClick(gvFlow.getC ...
- listview优化 汇总
1,listview加载性能优化ViewHolder 转自: http://blog.csdn.net/jacman/article/details/7087995 在android开发中Listvi ...
- ListView原理
表明转载自http://blog.csdn.net/iispring/article/details/50967445 在自己定义Adapter时,我们经常会重写Adapter的getView方法,该 ...
随机推荐
- CSS重排和重绘
一.什么是重绘Repaint和重排 (回流 reflow) 重绘:当元素的一部分属性发生改变,如外观.背景.颜色等不会引起布局变化,只需要浏览器根据元素的新属性重新绘制 ,使元素呈现新的外观叫做重绘. ...
- MonkeyRunner——Mac
1. MonkeyRunner介绍: Android的SDK中集成了三个可用来进行自动化测试的工具:Monkey.MonkeyRunner和Robotium.这三个测试工具都是基于黑盒测试. Monk ...
- 在oracle中存入date类型数据遇到的问题及其解决方法(利用java.sql.date和Timestamp)
转自:https://blog.csdn.net/ShadowerWArden/article/details/80652377 1. 使用JDBC操作Oracle数据库时,使用java.sql.Da ...
- iOS UIView视图的图层控制
1.添加视图 insertSubview:atIndex: //放在子视图数组的具体索引位置 insertSubview:aboveSubview: //某个子视图前面 insertSubvi ...
- jmeter学习笔记(三)配置元件之HTTP信息头管理
使用jmeter模拟发送http请求时,有些请求是需要带上HTTP请求头里面的信息.比如页面需要登录信息的,那个就需要用户登录信息authorization.这个时候是需要使用到HTTP信息头管理器. ...
- FreeRTOS config开始的宏
FreeRTOSConfig.h系统配置文件中可以自定义,FreeRTOS.h中定义默认值 configAPPLICATION_ALLOCATED_HEAP 默认情况下FreeRTOS的堆内存是由编译 ...
- STM8 LED
时钟分频寄存器(CLK_CKDIVR) 举例 int main() { CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); //高速内部时钟1分频 GPIO_ ...
- github发布版本
github发布版本后再pacakgist能拉取到需要发布版本 本地 mac@macdeMacBook-Pro:/www/tool$ git add . mac@macdeMacBook-Pro:/w ...
- vue---父调子 $refs (把父组件的数据传给子组件)
ps:App.vue 父组件 Hello.vue 子组件 App.vue : <template> <div id="app"> <input t ...
- WebAPI ModelValidata(模型验证)——DataAnnotations 解析
爱做一个新的项目,在该项目中的 WebAPI 中对数据的验证用到了 ModelValidata, 以前也没有用到过,也不是很熟悉,在查看了一些资料和代码后稍有了解,这里记录下来. 这里主要介绍的是 S ...