关于ListView的 addHeaderView(...) 方法










 关于ListView的 addHeaderView(...) 方法

  (2012-08-27 09:47:28)
标签: 

listview

 

addheaderview

 

杂谈

分类: Android

在代码中使用 listView .addHeaderView(...) 方法可以在ListView组件上方添加上其他组件,并且连结在一起像是一个新组件。如果多次使用 .addHeaderView(...) ,则最先添加的组件在最上方,按添加的先后顺序由上到下罗列。

 

如下图:
[android] <wbr>关于ListView的 <wbr>addHeaderView(...) <wbr>方法


 

此时listView 的 position = 0 的位置对应的是view1,而不再是原来listView中的第一条了。

 

若要对做原来的ListView做不可见设置,可使用将listView的adapter中数据置空的方法,在可见时再还原数据,如:

--隐藏 ListView --

 mStaffAdapter.mDataList=new ArrayList<Map<String,Object>>();
 mStaffAdapter.notifyDataSetChanged();

--展开 ListView --

 mStaffAdapter.mDataList=mStaffDataList;//mStaffDataList是原来的缓存数据
 mStaffAdapter.notifyDataSetChanged();














-------------------------


android listview addHeaderView和addFooterView的注意事项

发表于2年前(2014-03-14 11:37)   阅读( 6099) | 评论( 0)  6人收藏此文章, 我要收藏
1

今天在做项目的时候用了自定义listview以及自定义的item、adapter。现在把其中需要注意的地方记录下来:

1、item内如果有button等控件时,在监听listview的onitemclick事件时,焦点会被item内的button、imagebutton等控件抢走,从而导致在listview设置了onitemclick事件后不会被触发。解决方法是在初始化item的时候屏蔽掉其内部button等控件的焦点获取,具体方法可以在自定义item的根控件中调用:

  setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 

这样就能阻塞字控件抢夺焦点,listview的onitemclick就能被正确触发,同时对item内部的button等控件也没有影响,他们在被点击时照样可以触发自身的点击事件。


2、当listview需要添加headerview时,可以通过调用listview的addHeaderView(headView, null, false) 方法,该方法还有一个重载方法 addHeaderView(headView);这两个方法的区别是前一个方法可以控制header是否可以被selected,如果不想被selected则将第三个参数设置成false;


3、接着上面说的添加header,添加header时调用的addHeaderView方法必须放在listview.setadapter前面,意思很明确就是如果想给listview添加头部则必须在给其绑定adapter前添加,否则会报错。原因是当我们在调用setAdapter方法时会android会判断当前listview是否已经添加header,如果已经添加则会生成一个新的HeaderViewListAdapter,这个新的HeaderViewListAdapter包含我们设置的adapter所有内容以及listview的header和footer。所以当我们在给listview添加了header后在程序中调用listview.getadapter时返回的是tempadapter而不是我们通过setadapter传进去的adapter。如果没有设置adapter则HeaderViewListAdapter与我们自己的adapter是一样的。listview.getadapter().getcount()方法返回值会比我们预期的要大,原因是添加了header。


4、接着上面的HeaderViewListAdapter说,我们自定义adapter里面的getitem方法里面返回的position是不包括header的,是我们自定义adapter中数据position编号从0开始,也就是说与我们传进去的list的位置是一样的。

  • @Override 

  • public View getView(int position, View convertView, ViewGroup parent) { 

  • // TODO Auto-generated method stub 

  • Log.i("adapter", "position:"+position); //这个position就是我们数据的真实位置 



而listview的onitemclick方法中:

public void onItemSelected(AdapterView<?> parent, View view, int position, long id)

position是当前click的位置,这个位置是指在HeaderViewListAdapter中的位置,从0开始如果listview中添加了header则0代表header。


分享到:  1












-------------------------




-------------------------



ListView的addHeaderView和setAdapter的调用顺序

ListView想要添加headerview的话,就要通过addHeaderView这个方法,然后想要为ListView设置数据的话,就要调用setAdapter方法了。但是,在调用addHeaderView和setAdapter的顺序上,有时会爆出java.lang.IllegalStateException: Cannot add header view to list -- setAdapter has already been called.的异常。这是因为我们在addHeaderView之前调用了setAdapter。所以,在这里,建议setAdapter需要在addHeaderView和addfooterView之后调用。这样就安全了。下面,我们来看看源码吧。看看究竟是什么原因造成的。

Android-18(4.3)的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);
 
  // 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();
      }
  }
}
Android-17(4.2)的addHeaderView的源码:
public void addHeaderView(View v, Object data, boolean isSelectable) {
 
  if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {
      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);
 
  // in the case of re-adding a header view, or adding one later on,
  // we need to notify the observer
  if (mAdapter != null && mDataSetObserver != null) {
      mDataSetObserver.onChanged();
  }
}
在上面,我们可以对比出代码中的处理的不同。在17版本中,只要adapter不为空的话,那就直接会抛出异常,而这个异常恰好就是我们文章开头说到的异常。在18版本中,如果adapter不为空的话,则会新建一个adapter,这个adapter会包含了headerview和footerview以及我们传进来的原来的adapter。这是在18版本以后做的一个处理。虽然有了处理,但是建议大家还是按照上面说的调用顺序来使用addHeaderView,addFooterView和setAdapter吧。




--------------------------------------------------









Android listview addHeaderView 和 addFooterView 详解

addHeaderView()方法:主要是向listView的头部添加布局
addFooterView()方法:主要是向listView的底部添加布局

需要注意的是添加布局的时候应该添加从父容器开始添加,而不能直接添加父容器中的子控件。例如:从一个xml布局文件中添加一个button控件,
只能将整个布局xml文件添加进去。而不能单单只添加button控件。

当添加头部和底部布局还有另外一个重载方法就是addHeaderView(headView, null, false) 和addFooterView(footerView, null, false)方法,
这个方法与上面的方法区别在于:当给listView设置点击事件的时候,可以控制添加的布局是否可出发点击事件。区别是前一个方法可以控制header是否可
 以被selected,如果不想被selected则将第三个参数设置成false;

下面以addFooterView()方法为例:
addFooterView()方法必须放在listview.setadapter前面,给listview添加头部必须在绑定adapter前添加,否则会报错。
原因是当我们在调用setAdapter方法时android会判断当前listview是否已经添加header,如果已经添加则会生成一个
 新的tempadapter,这个新的tempadapter包含我们设置的adapter所有内容以及listview的header和footer。所以当我

们在给listview添加了header后在程序中调用listview.getadapter时返回的是tempadapter而不是我们通过setadapter

传进去的adapter。如果没有设置adapter则tempadapter与我们自己的adapter是一样的。
  listview.getadapter().getcount()方法返回值会比我们预期的要大,原因是添加了header。
  我们自定义adapter里面的getitem方法里面返回的position是不包括header的,是我们自定义adapter中数据position编号从0开始,
  也就是说与我们传进去的list的位置是一样的。
  而Activity中listview的onitemclick方法:
  public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3)
  arg2是当前click的位置,这个位置是指在tempadapter中的位置,从0开始如果listview中添加了header则0代表header。
  也就是说当我们在使用点击事件时,listView列中的位置为arg2-1
  
  一般在开发中,为了达到我们想要的动态添加的效果主要的做法是:在listview.setadapter之前添加所需要的控件,然后使用removeFooterView()方法移除控件。
  在这里需要注意的是,每对listView的动态操作都要进行一次removeFooterView()方法移除控件。否则listView会自动添加空白行,从而影响显示的效果。
 ----------------------------------------------------------------------------------------------

android listview addHeaderView和addFooterView的注意事项  :

1、item 内如果有button等控件时,在监听listview的onitemclick事件时,焦点会被item内的button、imagebutton等控 件抢走,
从而导致在listview设置了onitemclick事件后不会被触发。解决方法是在初始化item的时候屏蔽掉其内部button等控件的 焦点获取,
具体方法可以在自定义item的根控件中调用:

 setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 
这样就能阻塞字控件抢夺焦点,listview的onitemclick就能被正确触发,同时对item内部的button等控件也没有影响,他们在被点击时照样可以触发自身的点击事件。
 
2、当listview需要添加headerview时,可以通过调 用listview的addHeaderView(headView, null, false) 方法,
该方法还有一个重载方法 addHeaderView(headView);这两个方法的区别是前一个方法可以控制header是否可以被selected,如果不想被 selected则将第三个参数设置成false 
  
3、接着上面说的添加header,添加header时调用的 addHeaderView方法必须放在listview.setadapter前面,
意思很明确就是如果想给listview添加头部则必须在给其绑定 adapter前添加,否则会报错。
原因是当我们在调用setAdapter方法时会android会判断当前listview是否已经添加 header,
如果已经添加则会生成一个新的HeaderViewListAdapter,这个新的HeaderViewListAdapter包含我们 设置的adapter所有内容以及listview的header和footer。
所以当我们在给listview添加了header后在程序中调用 listview.getadapter时返回的是tempadapter而不是我们通过setadapter传进去的adapter。
如果没有设置 adapter则HeaderViewListAdapter与我们自己的adapter是一样的。 listview.getadapter().getcount()方法返回值会比我们预期的要大,原因是添加了header。

4、接着上面的HeaderViewListAdapter说,我们 自定义adapter里面的getitem方法里面返回的position是不包括header的,是我们自定义adapter中数据position编 号从0开始,也就是说与我们传进去的list的位置是一样的。

 

@Override

public View getView(int position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

Log.i("adapter", "position:"+position); //这个position就是我们数据的真实位置

}

而listview的onitemclick方法中:

public void onItemSelected(AdapterView<?> parent, View view, int position, long id)

position是当前click的位置,这个位置是指在HeaderViewListAdapter中的位置,从0开始如果listview中添加了header则0代表header。4、接着上面的HeaderViewListAdapter说,我们 自定义adapter里面的getitem方法里面返回的position是不包括header的,是我们自定义adapter中数据position编 号从0开始,也就是说与我们传进去的list的位置是一样的。


public View getView(int position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

Log.i("adapter", "position:"+position); //这个position就是我们数据的真实位置

}

而listview的onitemclick方法中:

public void onItemSelected(AdapterView<?> parent, View view, int position, long id)

position是当前click的位置,这个位置是指在HeaderViewListAdapter中的位置,从0开始如果listview中添加了header则0代表header。

 

 关于FooterView的添加和删除
 *   3.1每次总是先remove掉FooterView
 *   3.2若有需求再add上FooterView

 
 
 我们通常在加载数据时,为了省流量不会一次性把数据全部下完,一般是分段下载。
 分段下载一般会在listview最后面放一个进度条表示正在加载数据,当数据加载完时,我们又要清除它。这时候就要注意了。
mLoadingLayout = (FrameLayout) View.inflate(this, R.layout.load, null);
   listView.addFooterView(mLoadingLayout);
   listView.requestFocus();

这是listview尾部添加一个进度条。
listView.removeFooterView(mLoadingLayout);
这是移除尾部的进度条。
有时候在移除时回报空指针,但listview不为null ,mLoadingLayout也不为null,但还是报空指针,原因是因为listview要分为三部分。
一是头部,二是中间部,三是尾部。在设置了头部或尾部时,必须要有中间部才能真正意义上的生效。没生效就去移除就会报空指针错误。
所以在 
listView.removeFooterView(mLoadingLayout);时

必须先调用 listView.setAdapter(adapter);(设置中间部)

adapter可以数据可以为0但不可为null





------------------------------------------------------------------------------------------------






ListView的HeaderView

标签: Androidlistviewpositionhearderview
  12090人阅读  评论(0)  收藏  举报
  分类:
 

目录(?)[+]


 今天突然发现ListView的OnItemClickListener监听事件中的position返回是从1开始的,一直觉得很奇怪,在群里问了后,可能是headerView的问题,特意去查了一番,原来是如此:

特此记过!

(1)添加HeaderView之后尺寸布局被忽略。

通常添加头部的方法是 
?
1
2
3
LayoutInflater lif = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View headerView = lif.inflate(R.layout.header, null );
mListView.addHeaderView(headerView);

原因: 
lif.inflate(R.layout.header, null)丢失了XML布局中根View的LayoutParam,应该使用的是 
?
1
lif.inflate(R.layout.header, mListView, false );

(2)添加HeaderView之后导致OnItemClickListener的position移位

OnItemClickListener接口的方法: 
?
1
void onItemClick(AdapterView<?> parent, View view, int position, long id)

position通常是从0开始的,但是添加了HeaderView之后,position也会将HeaderView的数目计算进去。 
几个解决办法: 
1,手动计算真实的position位置: 
?
1
2
3
4
5
6
7
8
final headerCount = 1 ;
mListView.setOnItemClickListener( new OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> parent, View view,
             int position, long id) {
         Item item = myAdapter.getItem(position - headerCount);
     }
});

2,其实上面的步骤ListView已经为我们提供了,所以可以改写为: 
?
1
2
3
4
5
6
7
mListView.setOnItemClickListener( new OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> parent, View view,
             int position, long id) {
         Item item = parent.getAdapter().getItem(position);
     }
});
原因在源码中有比较清晰的解释: 
当有headerView被添加时,实际传递给ListView的adapter被包装,parent.getAdapter()返回真实被ListView使用的Adapter(HeaderViewListAdapter),HeaderViewListAdapter的getItem(int)方法处理了position的问题。 

(3)LayoutInflater的infalte()

用来呼应第一个问题。LayoutInflater的作用很简单,就是将XML的布局文件“翻译”成相应的View对象,而且出于性能的考虑,LayoutInflater只能处理编译后的XML文件,而不能处理通常明文编码的XML文件。 
最常用的一个方法: 
?
1
View inflate( int resource, ViewGroup root, boolean attachToRoot)

其中: 
resource 是布局文件ID 
root 是父ViewGroup对象, 
attachToRoot 是是否将“翻译”出来的View添加到上面的root中 

root和attachToRoot是共同作用的: 
1,有root,同时attachToRoot为false,那么inflate()返回的就是“翻译”得到的view 
2,有root,同时attachToRoot为true,那么inflate()就是将“翻译”得到的view添加到root后,然后返回root 
3,无root,同时attachToRoot为false,那么inflate()返回的就是“翻译”得到的view。 
4,无root,同时attachToRoot为true,报错。 

另外,root还有一个重要的作用就是为“翻译”得到的view添加合适的LayoutParam,并且如果并不想将得到的View添加到root的话,传递何种root是并没有要求的,比如: 
?
1
2
3
View view = mLayoutInflater.inflate(R.layout.header, new ListView(mContext), false );
View view = mLayoutInflater.inflate(R.layout.header, new LinearLayout(mContext), false );
View view = mLayoutInflater.inflate(R.layout.header, new RelativeLayout(mContext), false );
上面得到的View,除了view的LayoutParam分别为AbsListView.LayoutParams,LinearLayout.LayoutParams,RelativeLayout.LayoutParams之外,内容都一致。 


转载地址:  http://my.oschina.net/xesam/blog/155542#OSC_h2_1

1
0









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值