码迷,mamicode.com
首页 > 其他好文 > 详细

View 和 ViewGroup 的 hasFocusable

时间:2015-07-26 11:08:46      阅读:168      评论:0      收藏:0      [点我收藏+]

标签:view   android   焦点分发机制   

在 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();
}
  1. 在触摸模式下如果不可获取焦点的情况,先遍历 View 的所有父节点,如果有一个父节点设置了阻塞子 View 获取焦点,那么该 View 就不可能获取焦点
  2. 在触摸模式下如果不可获取焦点的情况,并且没有父节点设置阻塞子 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 是否能够获取焦点就简单多了

  1. 如果 ViewGroup visiable 和 focusable 都为 true,就算能够获取焦点
  2. 如果不满足第 1 点,就算子 View 节点能够获取焦点,该 ViewGroup 也算能获取焦点
  3. 否则,就算作不能够获取到焦点
  4. ViewGroup 自己能够获取焦点,完全没有考虑是否在触摸模式的情况,不晓得是不是 google 工程师的 bug

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。

版权声明:本文为博主原创文章,未经博主允许不得转载。

View 和 ViewGroup 的 hasFocusable

标签:view   android   焦点分发机制   

原文地址:http://blog.csdn.net/zhaoyw2008/article/details/47065945

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!