addHeaderView的一些注意点

时间:2022-01-03 23:40:41

在ListView中有时会需要添加addHeaderView或addFootView,或者两者兼或;

首先,我来一份简单的代码

TextView TextView=new TextView(this);
textView.setText("Hello");
listView.addHeaderView(textView);
自定义一个TextView,让他显示一句话"Hello",然后将它添加到Header中

这时候添加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();
//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();
}
大概意思就是他们遇到的问题是在setAdapter之后才addHeaderView导致的问题
还有一个解决方案:

地址;http://www.xuebuyuan.com/984745.html

cannot be cast to android.widget.HeaderViewListAdapter
用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.
都是顺序问题,导致发生了Adapter转换;之前的源码能看到

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();
}
}
mAdapter不为空的时候直接转换了,
还看到一个转换的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会覆盖子类控件而直接获得焦点


这样,应该是差不多了。