View 和 ViewGroup 的 hasFocusable
在 android 中。焦点的获取和事件几乎相同,有一个分发机制。一般来说View 树上上层节点的 ViewGroup 比底层节点的 View 有更高的优先级获取焦点。这体如今 ViewGroup 有一个属性 descendantFocusability 可以用来控制焦点获取的优先级。 该属性的值有三种:
- beforeDescendants:ViewGroup 会优先其子类控件而获取到焦点,假设父控件不获取焦点,子控件才可能会获得焦点
- afterDescendants:仅仅有当其子类控件不须要获取焦点时。ViewGroup 才可能获取焦点
- blocksDescendants:viewgroup 会堵塞子类控件获得焦点。不管 ViewGroup 是否获取焦点,子控件都不可以获得焦点
怎样推断 View 是否可以获取焦点
由于上面的原因。推断一个 View 是否有可能获得焦点。就与它全部的父节点有关系了,所以 View 的 hasFocusable 方法实现例如以下:
public boolean hasFocusable() {
if (!isFocusableInTouchMode()) {
for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {
final ViewGroup g = (ViewGroup) p;
if (g.shouldBlockFocusForTouchscreen()) {
return false;
}
}
}
return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();
}
- 在触摸模式下假设不可获取焦点。先遍历 View 的全部父节点,假设有一个父节点设置了堵塞子 View 获取焦点,那么该 View 就不可能获取焦点
- 在触摸模式下假设不可获取焦点,而且没有父节点设置堵塞子 View 获取焦点。和在触摸模式下假设可以获取焦点,那么才推断 View 自身的 visiable 和 focusable 属性,来决定是否可以获取焦点。不可见的 View 当然也不能获取焦点,所以仅仅有 visiable 和 focusable 同一时候为 true,该View 才可能获取焦点
怎样推断 ViewGroup 是否可以获取焦点
推断一个 ViewGroup 是否可能获取到焦点,也与它的子 View 有关系了。假设子 View 可以获取焦点,辣么。就行算作这个 ViewGroup 也可能获取(消耗)焦点,所以 ViewGroup 的 hasFocusable 方法实现例如以下:
public boolean hasFocusable() {
if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
if (isFocusable()) {
return true;
}
final int descendantFocusability = getDescendantFocusability();
if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
if (child.hasFocusable()) {
return true;
}
}
}
return false;
}
依据上面的源代码可知,推断 ViewGroup 是否可以获取焦点就简单多了
- 假设 ViewGroup visiable 和 focusable 都为 true,就算可以获取焦点
- 假设不满足第 1 点,假设子 View 节点可以获取焦点,该 ViewGroup 也算能获取焦点(通过递归实现)
- 否则。就算作不可以获取到焦点
- ViewGroup 自己可以获取焦点,全然没有考虑是否在触摸模式的情况。不晓得是不是 google project师的 bug,快到 google groups 提问 ^-^!
View 是否可以获取焦点的实际应用
View 是否可以获取焦点。通常在 AbsListView 的 OnItemClickListener,EditText,Button 的使用和 key 事件分发中会应用到。
比方 ListView 的 OnItemClickListener,仅仅有在 ListView 的 ItemView 不能获取到 focus 的情况下。才会调用 OnItemClickListener 的 onItemClick 方法。详细代码可以參考 AbsListView 的 onTouchUp 方法:
private void onTouchUp(MotionEvent ev) {
switch (mTouchMode) {
case TOUCH_MODE_DOWN:
case TOUCH_MODE_TAP:
case TOUCH_MODE_DONE_WAITING:
......
该省就省。我是省略代码切割符 ^_^
......
if (inList && !child.hasFocusable()) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
final AbsListView.PerformClick performClick = mPerformClick;
performClick.mClickMotionPosition = motionPosition;
performClick.rememberWindowAttachCount();
......
该省就省,我是省略代码切割符 ^_^
......
mTouchModeReset = new Runnable() {
@Override
public void run() {
mTouchModeReset = null;
mTouchMode = TOUCH_MODE_REST;
child.setPressed(false);
setPressed(false);
if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) {
performClick.run();
}
}
};
postDelayed(mTouchModeReset,
ViewConfiguration.getPressedStateDuration());
} else {
mTouchMode = TOUCH_MODE_REST;
updateSelectorState();
}
return;
} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
performClick.run();
}
}
}
......
该省就省,我是省略代码切割符 ^_^
......
}
从上面代码的第 9 行可知。ListView 的 ItemView 的 hasFocusable 方法必须返回 fasle,才会运行 performClick.run(),才会运行到 OnItemClickListener 的 onItemClick 方法。
这也就是为什么通常我们在使用 ListView 的 OnItemClickListener 方法的时候要把 Item 布局文件的根节点的 descendantFocusability 属性设置为 blocksDescendants,而且该节点的 focusable 属性不能为 true。
posted on 2017-06-15 15:55 cynchanpin 阅读(684) 评论(0) 编辑 收藏 举报