Android 中自定义 ListView 无法响应 OnItemClickListener 中的 onItemClick 方法问题解决方案

在 Android 软件设计与实现中我们通常都会使用到 ListView 这个控件,系统有一些预置的 Adapter 可以使用,例如 SimpleAdapter 和 ArrayAdapter,但是总是会有一些情况我们需要通过自定义 ListView 来实现一些效果,那么在这个时候,我们通常会碰到自定义 ListView 无法选中整个 ListViewItem 的情况,也就是无法响应 ListView 的 onItemClickListener 中的 onItemClick() 方法,究竟是为什么呢?我之前也在网上查过不少的资料,但是没有发现什么有价值的文章,有一些是建议在 Adapter 的 getView 方法中对自己需要响应单击事件的控件进行设置。但是最终的效果并不是特别理想,而且我认为这是一种取巧的方式,并不推荐。之后自己查看了一下 ViewGroup 的源码,发现了以下的一段常量声明:

/**
* This view will get focus before any of its descendants.
*/
public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
/**
* This view will get focus only if none of its descendants want it.
*/
public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
/**
* This view will block any of its descendants from getting focus, even
* if they are focusable.
*/
public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;

/**     * This view will get focus before any of its descendants.     */

public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
/**     * This view will get focus only if none of its descendants want it.     */

public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
/**     * This view will block any of its descendants from getting focus, even     * if they are focusable.     */

public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;

我们看到了一行代码定义的变量的意思是“当前 View 将屏蔽他所有子控件的 Focus 状态,即便这些子控件是可以 Focus 的”,其实这段话的意思就是这个变量代表着当前的 View 将不顾其子控件是否可以 Focus 自身接管了所有的 Focus,通常默认能获得 focus 的控件有 Button,Checkable 继承来的所有控件,这就意味着如果你的自定义 ListViewItem 中有 Button 或者 Checkable 的子类控件的话,那么默认 focus 是交给了子控件,而 ListView 的 Item 能被选中的基础是它能获取 Focus,也就是说我们可以通过将 ListView 中 Item 中包含的所有控件的 focusable 属性设置为 false,这样的话 ListView 的 Item 自动获得了 Focus 的权限,也就可以被选中了,也就会响应 onItemClickListener 中的 onItemClick() 方法,然而将 ListView 的 Item Layout 的子控件 focusable 属性设置为 false 有点繁琐,我们可以通过对 Item Layout 的根控件设置其 android:descendantFocusability=”blocksDescendant” 即可,这样 Item Layout 就屏蔽了所有子控件获取 Focus 的权限,不需要针对 Item Layout 中的每一个控件重新设置 focusable 属性了,如此就可以顺利的响应 onItemClickListener 中的 onItenClick() 方法了。

  • 兄弟谢谢你让我明白了这个问题!

    • 客气啦,呵呵,大家一起交流才是最重要的呢!

  • 哦,原来是这样,看来我要换中方式来实现了。谢谢了哦~~

    • 客气客气,能搞定问题就好

  • lihai

    我爱你, 你太厉害了

    • 客气客气,能解决问题就好了

  • ludingping

    加了之后。好像也不能点击 Button

    • 设置 Block 只是会让 OnItemClickListener 成功监听到 OnItemClick 事件而已,应该不会影响到 Item 中 Button 的点击的,不知道你碰到的是什么问题?如果方便的话,可以将问题的详细情况描述一下,这样好分析一些。

  • yimei

    试了,对 Button 有用,但是对自定义的 mageButton 没有用。何解呢?

  • yimei

    不好意思是我搞错了,好用的。

    • 客气客气,能解决问题就是王道啊

  • wzz24

    牛人,谢谢了,解决俺的问题了,O(∩_∩)O 哈哈~

  • mahiayun

    牛,这个问题困扰我三天了

  • lishali

    能解决你的问题我也很高兴,:-)

  • lishali

    能解决你的问题,我也很高兴,:-)

  • wanghh

    高手啊,能 qq 联系吗,一起交流,我的 QQ2083659

  • wjd

    这文章是你自己写的还是转的,如果是自己写的,你觉得里的逻辑对吗?“当前 View 将屏蔽他所有子控件的 Focus 状态,即便这些子控件是可以 Focus 的”,其实这段话的意思就是这个变量代表着当前的 View 将不顾其子控件是否可以 Focus 自身接管了所有的 Focus“,你说这句话的意思不就是 layout 先于 laoyout 的子控件拿到 focus, 怎么后面又搞句 “ 这就意味着如果你的自定义 ListViewItem 中有 Button 或者 Checkable 的子类控件的话,那么默认 focus 是交给了子控件”, 完全对不上号,下面的人还一个劲的评论看懂了… 是把属性设成 false 后, 自已的程序正常了,就觉得看懂了吧. 把 button 的 focusable 设成 false 后,item 是能获得 click 事件, 同时单击 button,button 也能得到响应, 这个又是什么原因呢,button 连焦点都被它的父控件给截获了,怎么还能响应 click 事件呢?你能把这个解释清楚吗?

    • 这位兄台,不用着急,首先这篇文章一定是我写的,这个不容置疑。
      其次我的逻辑是对的,在 Layout 中存在 Button 和 Checkable 的子控件时确实会将 OnItemClickListener 给截获,导致无法响应 onItemClickListener 方法,将 Layout 中所有控件的 focusable 设置为 false 也能让 item 响应 OnItemClickListener,与 button 自己能响应 onClick 本身就不冲突,你任何一个点击行为都会被 dispatch 的,首先就会判断 item 中子控件是否是可以点击的,如果可以的话而且设置了 onClickListener 的话,那么一定会响应 onClick 方法,至于将 android:descendantFocusability=”blocksDescendant”设置给 item 的 layout 会保证 OnItemClickListener 正常使用是绝对没有问题的,请分清楚我在讲的问题是 focusable 而不是 clickable,在 Android 中焦点本身就和可点击没有一个必然的联系,请确认这个问题。

  • zyx

    呵呵 楼主你好··看到文章了··但是我也按照你的方式加上去了··但是还是无妨响应 OnItemClickListener···里面的 ImageButton 是可以的···求指点:Q308711822 谢谢楼主哦 呵呵·

  • Sam

    楼主你好, 我按照你的方式加上去了, 可是还是没反应. 我的情况是, 点击 Menu, 会弹出一个 PoPWindow, 里面有三个 Tab, 每个 Tab 里面装有 Gridview, GridView 里面的 item 点击事件没反应. GridView 里面没有放其他的 Button 或者 Checkable 子控件. 我觉得应该也是焦点问题. 但是不知道为什么改了 focusable 属性后, 还是不行. 求指点

    • 先确定一下是否是 PopWindow 的问题,例如先在 PopWindow 的三个 Tab 中不放 GridView, 放个 Button 试试能否获取点击事件,然后一步一步地试,然后将 GridView 放到 Tab 中去看看,确定问题了之后应该就能找到解决方法了

  • 过客匆匆

    谢了哥们 写的真好

  • SweetSunshine

    按照上面的办法, Button/ImageButton 都没有试验成功, 无奈了, 求解啊

    • 请问你具体的问题是什么?

  • 袁杭

    我也按照你上面的步骤写了,还是没有响应。求指点……
    +QQ 439606935

  • ha

    我也按照您的方法寫了,但還是沒有反應
    我用了繼承 CursorAdapter 的自寫 Adapter
    我還有使用到一個 spinner,這會影響嗎?????

  • Pingback: android,listview,自定义列表,adapter()

  • Pingback: listview问题总结 - 移动开发教程 - 开发者()