View 和 ViewGroup 的 hasFocusable

在 android 中,焦点的获取和事件差不多,有一个分发机制,一般来说View 树上上层节点的 ViewGroup 比底层节点的 View 有更高的优先级获取焦点,这体现在 ViewGroup 有一个属性 descendantFocusability 可以用来控制焦点获取的优先级。 该属性的值有三种:

如何判断 View 是否能够获取焦点

因为上面的原因,判断一个 View 是否有可能获得焦点,就与它所有的父节点有关系了,所以 View 的 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 方法实现如下:

() { 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 是否能够获取焦点就简单多了

View 是否能够获取焦点的实际应用

View 是否能够获取焦点,通常在 AbsListView 的 OnItemClickListener,EditText,Button 的使用和 key 事件分发中会应用到。比如 ListView 的 OnItemClickListener,只有在 ListView 的 ItemView 不能获取到 focus 的情况下,才会调用 OnItemClickListener 的 onItemClick 方法。具体代码可以参考 AbsListView 的 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() {() {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。

当你能飞的时候就不要放弃飞

View 和 ViewGroup 的 hasFocusable

相关文章:

你感兴趣的文章:

标签云: