android 中的一些焦点处理(listView GridView 抢焦点 与focus技巧)

1、概述

在Android 事件传递与焦点处理(tv) 这篇中有描述android 的事件传递与拦截分发的流程
这篇描述一下具体的一些焦点处理
先再提一下最常见的ViewGroup中的三种焦点处理方式,后续详细描述

  • FOCUS_BEFORE_DESCENDANTS 0x20000 默认
    ViewGroup本身先对焦点进行处理,如果没有处理则分发给child View进行处理

  • FOCUS_AFTER_DESCENDANTS 0x40000
    先分发给Child View进行处理,如果所有的Child View都没有处理,则自己再处理

  • FOCUS_BLOCK_DESCENDANTS 0x60000
    ViewGroup本身进行处理,不管是否处理成功,都不会分发给ChildView进行处理

2、listView gridView 抢焦点 问题

2.1、 最常见的 item抢焦点

当item有焦点时,item上的button等子控件获取不到焦点;
当子控件有焦点时,item无焦点无法响应onItemClick事件

解决:(原因后面分析)
把焦点处理方式设成 FOCUS_BLOCK_DESCENDANTS
可以在xml中设置 也可以在代码里设置

2.2、listView GridView 在界面中抢了焦点

比如一个界面中有button gridView 还有其他的一些界面元素,一打开第一个焦点就落在gridView的item0 上
之所以用gridView说事,是实际中自己在写tv 界面时他老抢,listView与gridView类比
后面是用listView 或者 gridView 描述以实际使用中为准

解决:界面一启动的时候先把gridView 的gridView.setFocusable(false); 然后要使用的时候再使能回来

2.3、 在使用gridView 和listView 的技巧
  • a) onItemSelected 不会重复调用
    如果监听了 setOnItemSelectedListener 使用setSelection(int position) 如果gridView
    setFocusable 没有使能, item是不会有focus的效果的,而且两次先后两次
    setSelection 同一个postition onItemSelected监听在第二次重复的时候是不会调用的。
    声明下这里的先后两次包括:第一次selection 然后离开到其他元素比如其他的listView 然后再select回来也算是先后两次调用只要中间不是调用自己的其他item。

  • b) item 的focus技巧
    另外 一个listView在某个item focus了之后。下一步焦点转移到其他元素控件。想再回到该item 比较简单的方式是让该
    listView.requestFocus() 该item自然就又focus出来了。
    比较简单,这也充分的说明了一个问题单焦点离开listview的时候select的状态还是在的只是没有focus的效果显示出来
    这个可以通过读取 listView.getSelectedItemPosition() 看到。

  • c)所以在使用listView gridView 需要item高亮的时候 尽量使用item来高亮不要用内部元素高亮

3、ListView GridView ViewGroup 的焦点分析

简单的描述下为什么
FOCUS_BEFORE_DESCENDANTS
FOCUS_AFTER_DESCENDANTS
FOCUS_BLOCK_DESCENDANTS 三种的处理方式是怎么来的,为什么会被抢焦点

3.1 ViewGroup 分析 三种焦点拦截方式与 onRequestFocusInDescendants 函数(子view处理函数)

ViewGroup 中:
看源码 requestFocus() 函数 可以看出
FOCUS_BLOCK_DESCENDANTS FOCUS_BEFORE_DESCENDANTS FOCUS_AFTER_DESCENDANTS 这三个标签的区别 和对
onRequestFocusInDescendants() 的调用顺序

@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
    if (DBG) {
        System.out.println(this + " ViewGroup.requestFocus direction="
                + direction);
    }
    int descendantFocusability = getDescendantFocusability();

    switch (descendantFocusability) {
        case FOCUS_BLOCK_DESCENDANTS:
//ViewGroup本身进行处理,不管是否处理成功,都不会分发给ChildView进行处理
            return super.requestFocus(direction, previouslyFocusedRect);
        case FOCUS_BEFORE_DESCENDANTS: {
//ViewGroup本身先对焦点进行处理,如果没有处理(返回false)则分发给child View进行处理
            final boolean took = super.requestFocus(direction, previouslyFocusedRect);
            return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
        }
        case FOCUS_AFTER_DESCENDANTS: {
//先分发给Child View进行处理,如果所有的Child View都没有处理(false),则自己再处理
            //final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
            return took ? took : super.requestFocus(direction, previouslyFocusedRect);
        }
        default:
            throw new IllegalStateException("descendant focusability must be "
                    + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
                    + "but is " + descendantFocusability);
    }
}

然后 就是子类可以重写的 onRequestFocusInDescendants 默认的函数如下可以看出,是按顺序的 逐个看看能不能获取焦点

protected boolean onRequestFocusInDescendants(int direction,
        Rect previouslyFocusedRect) {
    int index;
    int increment;
    int end;
    int count = mChildrenCount;
    if ((direction & FOCUS_FORWARD) != 0) {
        index = 0;
        increment = 1;
        end = count;
    } else {
        index = count - 1;
        increment = -1;
        end = -1;
    }
    final View[] children = mChildren;
    for (int i = index; i != end; i += increment) {
        View child = children[i];
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
            if (child.requestFocus(direction, previouslyFocusedRect)) {
                return true;
            }
        }
    }
    return false; }

viewGroup的默认配置
initViewGroup() -> setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);//ViewGroup本身先对焦点进行处理,如果没有处理则分发给child View进行处理。

注:这里的
ViewGroup 本身先对焦点进行处理指的就是:super.requestFocus(direction, previouslyFocusedRect);

分发给child view 处理指的就是:onRequestFocusInDescendants() 函数里的处理过程,是这个函数决定具体分发给哪个childView处理,或者直接拦截不处理。

3.2 listView 的分析

listView 的表现在这个blog 里描述的 挺详细:http://www.it165.net/pro/html/201406/15086.html
ListView 的源码中没有看到对viewGroup 设置 setDescendantFocusability 采用默认的值 则自己没有处理 逐个分发给子view处理,那么

/**
 * Indicates that the views created by the ListAdapter can contain focusable
 * items.
 *
 * @param itemsCanFocus true if items can get focus, false otherwise
 */
public void setItemsCanFocus(boolean itemsCanFocus) {
    mItemsCanFocus = itemsCanFocus;
    if (!itemsCanFocus) {
        setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值