具体情况是这样的: 有一个ViewGroup A, 包含一个View B, 然后 A, B的focusableInTouchMode都是true, 第一次点击B, onClickListener不会触发, 而后续点击都可以.
查看源代码后发现这是因为 View 在 onTouchEvent() 中的 MotionEvent.ACTION_UP 中对focus做了处理: 如果View focusableInTouchMode 是true, 并且当前没有获得焦点, 那么会尝试获取焦点, 并且不会调用 performClick().
相关代码如下:
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
...
}
因此上面的现象可以如下解释:
- 第一次点击时, A 获得焦点, 而 B 没有焦点, 因此第一次点击之后, B 获取了焦点, 但没有调用 performClick().
- 后续点击时, B 已经拥有了焦点, 调用了 performClick(), 所以 OnClickListener能够被触发.
这个问题有下面几种解决方法:
- 将 B 的 focusableInTouchMode 设置为 false
- 在点击 B 之前, 让其获得焦点
- 手动调用它的 requestFocus() 方法
- 让 B 成为视图层级中第一个focusable的View (例如将 A 的focusable设置为false)