在ListView中有时会需要添加addHeaderView或addFootView,或者两者兼或;
首先,我来一份简单的代码
TextView TextView=new TextView(this);自定义一个TextView,让他显示一句话"Hello",然后将它添加到Header中
textView.setText("Hello");
listView.addHeaderView(textView);
这时候添加item点击事件,在点击事件中
PopwindowRightAdapter adapter = (PopwindowRightAdapter) parent.getAdapter();想要获取Adapter,结果却报错
java.lang.ClassCastException: android.widget.HeaderViewListAdapter cannot be cast to
说是HeaderViewListAdapter 不能被转化为我们自定义的那个Adapter,我这里的就是PopwindowRightAdapter
去addHeaderView中看一下源码
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)) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
// 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();
}
}
}
adapter被转化为了 HeaderViewListAdapter,就不是我们之前的那个adapter,
看一下他的构造方法:
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);
}
我也看不太懂,大概意思就是把header放到一个List中,footer放到一个List当中,然后和之前的数据集合到一块整合成一个新的Adapter;不过,位置是不同的,一个放头部,一个放底部
再回到之前的那个报错问题,我在网上查了一下,有人给出如下方案:
地址:http://blog.csdn.net/sunxingzhesunjinbiao/article/details/9952997
ListView listView = getListView();大概意思就是他们遇到的问题是在setAdapter之后才addHeaderView导致的问题
//ConversationListAdapter adapter = (ConversationListAdapter) listView.getAdapter();
ConversationListAdapter adapter = null;
if(listView.getAdapter() instanceof HeaderViewListAdapter) {
HeaderViewListAdapter listAdapter=(HeaderViewListAdapter)listView.getAdapter();
adapter = (ConversationListAdapter) listAdapter.getWrappedAdapter();
} else {
adapter = (ConversationListAdapter) listView.getAdapter();
}
还有一个解决方案:
地址;http://www.xuebuyuan.com/984745.html
cannot be cast to android.widget.HeaderViewListAdapter都是顺序问题,导致发生了Adapter转换;之前的源码能看到
用listview设置header或footerview,通常发生该异常。
如果你没有发生,那是碰巧。但是你可能不知道原因。
如果在listview.setAdapter(adapter)方法之后 添加头或尾view,
即addHeaderView或 addFooterView,
那么在你listView.removeHearderView或removeFooterView时就会报该异常。
在Listview的源码中如果设置了adapter,那么它会强转成HeaderViewListAdapter,所以就会报错。
if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeHeader(v)) {
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
result = true;
}
。
所以你一定要在setAdapter之前调用addHeaderView或addFooterVeiw.
if (mAdapter != null) {mAdapter不为空的时候直接转换了,
if (!(mAdapter instanceof HeaderViewListAdapter)) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
// 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();
}
}
还看到一个转换的blog,地址: http://blog.sina.com.cn/s/blog_6d45d11f01014n1c.html
private OnItemClickListener mItemClickListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
HeaderViewListAdapter ha = (HeaderViewListAdapter) parent.getAdapter();
JobsAdapter ad = (JobsAdapter) ha.getWrappedAdapter();
ad.toggle(position-1);
}
};
HeaderViewListAdapter有个方法getWrappedAdapter,该方法能返回被包装的HeaderViewListAdapter的ListAdapter。
说实话,这句话我没看懂,我还是不知道getWrappedAdapter是干什么用的,看了一下它的API说明
Returns the adapter wrapped by this list adapter.
我理解的意思就是返回当前包装的adapter,大概就是转换成当前的adapter的意思吧,猜的。。。估计差不远
我用面的方式试了一下,除了这个toggle搞不出来,其余的差不多
那个报错解决完之后,还有一个顺序的问题,添加了一个header,他的position就是0了,然后之前的顺移,这样就需要用到那个toggle,不过我失败了,怎么也找不到这方法,然后我就用下面的
JBean pCarea = adapter.getItem(position-1);,这样就可以依次取到之前的item的点击事件了,然后问题又来了,因为header也在,点击她的时候,她的position就是-1了,当时我想的是专门针对这个-1处理一下,做个判断处理一下,随后又感觉到不行,这只是一个header,如果有多个呢,然后又去找了一下资料,发现addView还有另一个重载方法addheadView(),对,就是最开始那个
我最初是用的这个
public void addHeaderView(View v) {
addHeaderView(v, null, true);
}
后来才发现另一个重载方法的
public void addHeaderView(View v, Object data, boolean isSelectable)主要在于最有一个参数,他设置header不被选中,就不会触发点击事件也就是-1也没事;
设置多个addHeaderView的时候就可以给header添加点击事件OncliickListener,用来覆盖onItemClickListener,同时,之前的item获取item的时候,需要用position减去header的个数
然后又有一个问题,那就是item中的子控件焦点之争;
地址:http://blog.csdn.net/iblade/article/details/50958295
当使用itemClickListener的时候,item里面的子控件如button会夺取焦点,这时候就有一个方法:
setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
在ListView的xml中也可以设置
地址:http://www.cnblogs.com/eyu8874521/archive/2012/10/17/2727882.html
android:descendantFocusability它有三个可选值:
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
这样,应该是差不多了。