首页 > 代码库 > Android ListView OnItemLongClick和OnItemClick事件内部细节分享

Android ListView OnItemLongClick和OnItemClick事件内部细节分享

辞职3,4个月在家休息,本以为楼主要程序员逆袭,结果失败告终继续码农生涯今天开始更新博客。

    正文。
    项目中有个ListView内容比较复杂现在要添加长按删除功能。楼主自然想到利用ListView的onItemLongClick事件来处理。结果可想而知在实际体验中很不好,会出现失灵有些选项能触发onItemLongClick事件有些却没反应。楼主去网上看了都是focusable要设置成false。但依照楼主的经验这只能解决都不能触发onItemLongClick事件问题,像楼主遇到的有些可以有些失灵的估计不是一剂良药。所以楼主具体分析了一下内部细节,希望能给后来的人提供一些帮助。
    首先看一下foucsable造成的事件没反应的问题。
原因:
若你的item中有button或者checkbox等控件,默认情况下焦点focus是最先交给这些子控件,而ListView的Item能被选中的基础是它能获取Focus焦点,所以,我们可以通过将ListView中Item中包含的所有控件的focusable属性设置为false,这样ListView的Item就自动获得了Focus焦点的权限,也就可以被选中了,同时也会响应onItemClickListener中的onItemClick()方法。
解决办法:(以下两种办法任意一种都可)
1.将ListView的Item Layout中的所有子控件focusable属性设置为false
2.将item layout的根控件设置属性(推荐轻量级特别是修改别人的代码)
android:descendantFocusability=”blocksDescendant”
这样Item Layout就屏蔽了所有子控件获取Focus焦点的权限,不需要针对ItemLayout中的每一个控件重新设置focusable属性
分析:
   为什么不设置focusable为false就不会触发onItemLongClick事件呢。通过查看AbsListView的源码你会发现如果focusable不为false跟本就不分发touch事件。代码如下:
Android <wbr>ListView <wbr>OnItemLongClick和OnItemClick事件内部细节分享
附android:descendantFocusability用法简析
   开发中很常见的一个问题,项目中的listview不仅仅是简单的文字,常常需要自己定义listview,自己的Adapter去继承BaseAdapter,在adapter中按照需求进行编写,问题就出现了,可能会发生点击每一个item的时候没有反应,无法获取的焦点。原因多半是由于在你自己定义的Item中存在诸如ImageButton,Button,CheckBox等子控件(也可以说是Button或者Checkable的子类控件),此时这些子控件会将焦点获取到,所以常常当点击item时变化的是子控件,item本身的点击没有响应。
   这时候就可以使用descendantFocusability来解决啦,API描述如下:
 Android <wbr>ListView <wbr>OnItemLongClick和OnItemClick事件内部细节分享

该属性是当一个为view获取焦点时,定义viewGroup和其子控件两者之间的关系。
属性的值有三种:
       beforeDescendants:viewgroup会优先其子类控件而获取到焦点
      afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
       blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
通常我们用到的是第三种,即在Item布局的根布局加上android:descendantFocusability=”blocksDescendants”的属性就好了
 
实际情况根据上面的设置楼主的问题依然没有解决,楼主坚信上面的文章的权威性,再次分析自己的代码。发现在adapter中convertView被设置clickable为true(另一位同事为了屏蔽item的黄色selector设置的待会儿楼主会介绍修改listview点击黄色背景修改)这必然导致touch事件被convertView截获,由于convertView是item的根容器,所以就无法点击到listView使listview获取touch事件。去掉item跟容器的clickable设置。果然能触发onItemLongClick事件。失灵问题解决后发现在内容很少简单的item没问题,但在复杂的item时item内的子view不可避免的要设置click事件,而这个view又占item的大部分地方,总不能每次要长按长按item狭小的空余地方来触发onItemLongClick事件吧。楼主这里要给一个比较轻量级的解决方案。代码片段如下:
//Adapter 内部代码:
//holder.audioPanel这个view占很大的地方但又必须实现click事件,还好它有也有longclick事件
holder.audioPanel.setOnClickListener(this);
holder.audioPanel.setOnLongClickListener(this);
//关键在这里手动触发listview的长按方法自然又回到了正常的逻辑了。
@Override
public boolean onLongClick(View v) {
mListView.performLongClick();
return false;
}
这下就完美解决触发onItemLongClick事件了不管listview界面多么复杂。
对了如何去除listview默认的黄色背景。楼主是这样解决的:
Android <wbr>ListView <wbr>OnItemLongClick和OnItemClick事件内部细节分享
 
附录一下楼主网上看到的一些小细节:
1.ListView本身可不可以调用setOnClickListner()? 
代码上可以,但是运行马上会丢出异常,所以是不可以拦截Listview本身的click事件。 
2.ListView.setOnItemClickListener设置的listener什么时候会被调用? 
当点击某行内容是会被调用,但是如果这行内容中包含Button,ImgButton等控件时就不会被调用,为什么以及怎么解决见后面。 
3.ListView.setOnItemLongClickListener设置的listener什么时候被调用? 
当长按某一行时会被调用,而且在抬起之前就已经调用了。 
4.收到LongClick的调用后还会调用click吗? 
这个要根据LongClick listener的返回值来决定。
Java代码 
1.lv.setOnItemLongClickListener(new OnItemLongClickListener(){   
2.           publicbooleanonItemLongClick(AdapterView<?>parent, View view, int position, long id) { 
3.              System.out.println("Item LONGclicked. Position:" + position);  
4.              return false;  
5.           }  
6.       });  
如果返回false那么click仍然会被调用。而且是先调用Longclick,然后调用click。 
如果返回true那么click就会被吃掉,click就不会再被调用了。 
5.监听click以及long click影响弹出菜单吗? 
click不影响;longclick如果返回true那么就会吃掉click事件,导致菜单不能弹出。 
 
附录:
Android使用ListView应该注意的地方
在ListView中设置Selector为null会报空指针? 
mListView.setSelector(null);//空指针 
试试下面这种: 
mListView.setSelector(newColorDrawable(Color.TRANSPARENT)); 
 
如何让ListView初始化的时候就选中一项? 
ListView需要在初始化好数据后,其中一项需要呈选中状态。所谓"选中状态"就是该项底色与其它项不同,setSelection(position)只能定位到某个item,但是无法改变底色呈高亮。setSelection(position)只能让某个item显示在可见Item的最上面(如果Item超过一屏的话)!就是所谓的firstVisibleItem啦! 
如果想要实现效果可以在listview所绑定的adapter里的getView函数里去完成一些具体的工作。可以记下你要高亮的那个item的index,在getView函数里判断index(也就是position),如果满足条件则加载不同的背景。 
 
ListView的右边滚动滑块启用方法? 
   很多开发者不知道ListView列表控件的快速滚动滑块是如何启用的,其实辅助滚动滑块只需要一行代码就可以搞定,如果你使用XML布局只需要在ListView节点中加入 android:fastScrollEnabled="true"这个属性即可,而对于Java代码可以通过myListView.setFastScrollEnabled(true);来控制启用,参数false为隐藏。 
   还有一点就是当你的滚动内容较小,不到当前ListView的3个屏幕高度时则不会出现这个快速滚动滑块,该方法是AbsListView的基础方法,可以在ListView或GridView等子类中使用快速滚动辅助。 
 
1.更新ListView中的數據,通過調用BaseAdapter對象的notifyDataSetChanged()方法: 
     mAdapter.notifyDataSetChanged(); 
 
2.每個listview都有無效的位置,如第一行的前一行,最後一行的後一行,這個無效的位置是一個常量. 
    ListView.INVALID_POSITION 
 
3.有時我們需要在程序中通過點擊按鈕來控制ListView行的選中,這就用到了在程序中如何使用代碼來選擇ListView項. 
       mListView.requestFocusFromTouch(); 
        mListView.setSelection(intindex); 
 
    第一條語句並不是必須的,但是若你ListView項中含有Button,RadioButton,CheckBox等比ListView取得焦點優先級高的控件時,那麼第一條語句是你必須加的. 
 
4. 同樣的,若你ListView項中含有Button,RadioButton,CheckBox等比ListView取得焦點優先級高的控件時,ListView的setOnItemClickListener是不被執行的,這時你需要在你的xml文件中對這些控件添加 android:focusable="false"注意這條語句要放在xml文件中修改,在代碼中使用是無效的. 
 
5. 如何保持ListView的滾動條一直顯示,不隱藏呢:  xml文件中做如下修改  android:fadeScrollbars="false" 
 
6.ListView本身有自己的按鍵事件,即你不需要設置方向鍵的標識,按下方向鍵ListView就會有默認的動作,那如何進行控制,編寫自己的onKey呢,你需要在Activity中重寫dispatchKeyEvent(KeyEventevent);方法,在這裡面定義你自己的動作就可以了 
 
ListView 自定义滚动条样式: 
<ListViewandroid:id="@android:id/list" 
      android:layout_width="match_parent" 
      android:layout_height="0dip" 
      android:layout_weight="1" 
      android:stackFromBottom="true"//从下开始显示条目 
      android:transcriptMode="normal" 
      android:fastScrollEnabled="true" 
       android:focusable="true" 
      android:scrollbarTrackVertical="@drawable/scrollbar_vertical_track" 
      android:scrollbarThumbVertical="@drawable/scrollbar_vertical_thumb" 
/> 
//scrollbar_vertical_track,crollbar_vertical_thumb自定义的xml文件,放在Drawable中,track是指长条,thumb是指短条 
去掉ListView Selector选种时黄色底纹一闪的效果: 
 
Xml代码   
<?xml version="1.0"encoding="utf-8"?>  
<shapexmlns:android="http://schemas.android.com/apk/res/android"> 
    <solidandroid:color="@android:color/transparent"/> 
    <cornersandroid:radius="0dip" />     
</shape> 
//listview.setSelector(R.drawable.thisShape); 
 
或者还有一种办法: 
在Adapter中重写public boolean isEnabled(intposition)方法,将其返回false就可以了,推荐采用此种办法,具体见http://gundumw100.iteye.com/admin/blogs/850654 
 
Java代码   
public boolean isEnabled(int position) { 
    // TODO Auto-generatedmethod stub  
    return false; 
}  
 
 
ListView几个比较特别的属性 
首先是stackFromBottom属性,这只该属性之后你做好的列表就会显示你列表的最下面,值为true和false 
android:stackFromBottom="true"           
 
第二是transciptMode属性,需要用ListView或者其它显示大量Items的控件实时跟踪或者查看信息,并且希望最新的条目可以自动滚动到可视范围内。通过设置的控件transcriptMode属性可以将Android平台的控件(支持ScrollBar)自动滑动到最底部。 
android:transcriptMode="alwaysScroll"   
 
第三cacheColorHint属性,很多人希望能够改变一下它的背景,使他能够符合整体的UI设计,改变背景背很简单只需要准备一张图片然后指定属性android:background="@drawable/bg",不过不要高兴地太早,当你这么做以后,发现背景是变了,但是当你拖动,或者点击list空白位置的时候发现ListItem都变成黑色的了,破坏了整体效果。 
如果你只是换背景的颜色的话,可以直接指定android:cacheColorHint为你所要的颜色,如果你是用图片做背景的话,那也只要将android:cacheColorHint指定为透明(#00000000)就可以了 
 
第四divider属性,该属性作用是每一项之间需要设置一个图片做为间隔,或是去掉item之间的分割线android:divider="@drawable/list_driver" 其中  @drawable/list_driver是一个图片资源,如果不想显示分割线则只要设置为android:divider="@drawable/@null"就可以了 
 
第五fadingEdge属性,上边和下边有黑色的阴影android:fadingEdge="none"设置后没有阴影了~ 
 
第五scrollbars属性,作用是隐藏listView的滚动条,android:scrollbars="none"与setVerticalScrollBarEnabled(true);的效果是一样的,不活动的时候隐藏,活动的时候也隐藏 
 
第六fadeScrollbars属性,android:fadeScrollbars="true" 配置ListView布局的时候,设置这个属性为true就可以实现滚动条的自动隐藏和显示。 
 
如何在使用gallery在flinging拖动时候不出现选择的情况? 
这时候需要注意使用 
gallery.setCallbackDuringFling(false) 
 
如何让ListView自动滚动? 
注意stackFromBottom以及transcriptMode这两个属性。类似Market客户端的低端不断滚动。 
<ListView android:id="listCWJ" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:stackFromBottom="true"   
    android:transcriptMode="alwaysScroll" 
/> 
如何遍历listView 的的单选框? 
 
Java代码   
ListView listView =(ListView)findViewById(R.id.配置文件中ListView的ID); 
//全选遍历ListView的选项,每个选项就相当于布局配置文件中的RelativeLayout 
for(int i = 0; i < listView.getChildCount();i++){  
      Viewview = listView.getChildAt(i);  
     CheckBox cb = (CheckBox)view.findViewById(R.id.CheckBoxID); 
     cb.setChecked(true);  
}  
 
如何让ListView中TextView的字体颜色跟随焦点的变化? 
我们通常需要ListView中某一项选中时,他的字体颜色和原来的不一样。 如何设置字体的颜色呢?在布局文件中TextColor一项来设置颜色,但是不是只设置一种颜色,而是在不同的条件下设置不同的颜色:下面是个例子: 
 
Xml代码   
<?xml version="1.0" encoding="utf-8"?>  
<selectorxmlns:android="http://schemas.android.com/apk/res/android"> 
<item android:state_enabled="false"android:color="@color/orange"></item> 
<item android:state_window_focused="false"android:color="@color/orange"></item> 
<item android:state_pressed="true"android:color="@color/white"></item> 
<item android:state_selected="true"android:color="@color/white"></item> 
<itemandroid:color="@color/orange"></item> 
</selector>  
在获取焦点或者选中的情况下设置为白色,其他情况设置为橘黄色。  
 
如何自定义ListView行间的分割线? 
所有基于ListView或者说AbsListView实现的widget控件均可以通过下面的方法设置行间距的分割线,分割线可以自定义颜色、或图片。 
在ListView中我们使用属性android:divider="#FF0000"定义分隔符为红色,当然这里值可以指向一个drawable图片对象,如果使用了图片可能高度大于系统默认的像素,可以自己设置高度比如6个像素android:dividerHeight="6px",当然在Java中ListView也有相关方法可以设置。 
 
ListView不通过notifyDataSetChanged()更新指定的Item 
Listview一般大都是通过notifyDataSetChanged()來更新listview,但通过notifyDataSetChanged()会把界面上现实的的item都重绘一次,这样会影响ui性能。 
可以通过更新指定的Item提高效率,伪代码如下: 
 
Java代码   
private void updateView(int itemIndex){   
  int visiblePosition =yourListView.getFirstVisiblePosition();   
  View v = yourListView.getChildAt(itemIndex- visiblePosition);//Do something fancy with your listitem view   
  TextView tv =(TextView)v.findViewById(R.id.sometextview); 
  tv.setText("Hi! I updated you manually"); 
}  
 
 

当在listview的item中嵌入了checkbox, webview等事件优先级高的控件时,listview的onItemClick事件会被屏蔽无响应。。。

这时我们可以通过将item中控件的focuse设为false来解决问题。

使用静态方法:在布局文件中添加 android:focusable="false" 

使用动态方法:在代码中调用 XXXView.setFocusable(false)



原因: 在Android控件中,交互事件都是沿着控件树自顶向下传播的。当位于控件上层的父控件收到交互事件后,会先行判断该事件的目标控件对象,如果该事件正是自己所需要的,则会截获事件进行处理,否则就尝试将该事件向下分发给对应的子控件并递推的逐级向下传播事件,直至该事件被处理或或略

 

转自:http://blog.sina.com.cn/s/blog_783ede030101bnm4.html

http://my.oschina.net/u/249000/blog/109616

Android ListView OnItemLongClick和OnItemClick事件内部细节分享