深入焦点处理流程

来源:互联网 发布:m2m 数据采集 编辑:程序博客网 时间:2024/06/04 01:05

深入焦点处理流程

0.开始响应按键

public final class ViewRootImpl implements ViewParent,        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {    /**     * Delivers post-ime input events to the view hierarchy.     */    final class ViewPostImeInputStage extends InputStage {        public ViewPostImeInputStage(InputStage next) {            super(next);        }        //入口方法        private int processKeyEvent(QueuedInputEvent q) {            final KeyEvent event = (KeyEvent)q.mEvent;            // 1.先去执行mView的dispatchKeyEvent            // Deliver the key to the view hierarchy.            if (mView.dispatchKeyEvent(event)) {                return FINISH_HANDLED;            }            //...            // Handle automatic focus changes.            if (event.getAction() == KeyEvent.ACTION_DOWN) {                if (direction != 0) {                    //2.找到当前获取焦点的view                    View focused = mView.findFocus();                    if (focused != null) {                        //3.之后通过‘当前焦点的view’和‘方向’,寻找下一个焦点view                        View v = focused.focusSearch(direction);                        if (v != null && v != focused) {                            // do the math the get the interesting rect                            // of previous focused into the coord system of                            // newly focused view                            focused.getFocusedRect(mTempRect);                            if (mView instanceof ViewGroup) {                                ((ViewGroup) mView).offsetDescendantRectToMyCoords(                                        focused, mTempRect);                                ((ViewGroup) mView).offsetRectIntoDescendantCoords(                                        v, mTempRect);                            }                            //4.尝试让下一个焦点view,获取焦点                            if (v.requestFocus(direction, mTempRect)) {                                playSoundEffect(SoundEffectConstants                                        .getContantForFocusDirection(direction));                                return FINISH_HANDLED;                            }                        }                        // Give the focused view a last chance to handle the dpad key.                        if (mView.dispatchUnhandledMove(focused, direction)) {                            return FINISH_HANDLED;                        }                    } else {                        // find the best view to give focus to in this non-touch-mode with no-focus                        View v = focusSearch(null, direction);                        if (v != null && v.requestFocus(direction)) {                            return FINISH_HANDLED;                        }                    }                }            }            return FORWARD;        }    }}

1.先去执行mView的dispatchKeyEvent

// Deliver the key to the view hierarchy.if (mView.dispatchKeyEvent(event)) {    return FINISH_HANDLED;}

2.找到当前获取焦点的view

/*** Find the view in the hierarchy rooted at this view that currently has* focus.** @return The view that currently has focus, or null if no focused view can*         be found.*/View focused = mView.findFocus();

3.之后通过‘当前焦点的view’和‘方向’,寻找下一个焦点view

/*** Find the nearest view in the specified direction that can take focus.* This does not actually give focus to that view.** @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT** @return The nearest focusable in the specified direction, or null if none*         can be found.*/View v = focused.focusSearch(direction)

4.尝试让下一个焦点view,获取焦点

/*** Call this to try to give focus to a specific view or to one of its* descendants.** A view will not actually take focus if it is not focusable ({@link #isFocusable} returns* false), or if it is focusable and it is not focusable in touch mode* ({@link #isFocusableInTouchMode}) while the device is in touch mode.** See also {@link #focusSearch(int)}, which is what you call to say that you* have focus, and you want your parent to look for the next one.** This is equivalent to calling {@link #requestFocus(int, Rect)} with arguments* {@link #FOCUS_DOWN} and <code>null</code>.** @return Whether this view or one of its descendants actually took focus.*/v.requestFocus(direction, mTempRect)

1.dispatchKeyEvent()

1.mView.dispatchKeyEvent(event)

2.activity.dispatchKeyEvent

3.win.superDispatchKeyEvent

4.mDecor.superDispatchKeyEvent

5.framelayout.dispatchKeyEvent

6.view.dispatchKeyEvent

7.event.dispatch

8.view.onKeyUp

9.view.performClick

2.findFocus()

1.viewGroup.findFocus

2.view.findFocus

3.focusSearch()

1.view.focusSearch(direction)

2.mParent.focusSearch(this, direction);

3.FocusFinder.getInstance().findNextFocus(this, focused, direction);

4.requestFocus(direction, mTempRect)

1.viewGroup.requestFocus

//FOCUS_BEFORE_DESCENDANTS默认值public abstract class ViewGroup extends View implements ViewParent, ViewManager    public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        initViewGroup();        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);    }    private void initViewGroup() {        // ViewGroup doesn't draw by default        if (!debugDraw()) {            setFlags(WILL_NOT_DRAW, DRAW_MASK);        }        mGroupFlags |= FLAG_CLIP_CHILDREN;        mGroupFlags |= FLAG_CLIP_TO_PADDING;        mGroupFlags |= FLAG_ANIMATION_DONE;        mGroupFlags |= FLAG_ANIMATION_CACHE;        mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;        if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {            mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;        }        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);        mChildren = new View[ARRAY_INITIAL_CAPACITY];        mChildrenCount = 0;        mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;    }    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:                return super.requestFocus(direction, previouslyFocusedRect);            case FOCUS_BEFORE_DESCENDANTS: {                final boolean took = super.requestFocus(direction, previouslyFocusedRect);                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);            }            case FOCUS_AFTER_DESCENDANTS: {                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);        }    }}

2.viewGroup.onRequestFocusInDescendants()

public abstract class ViewGroup extends View implements ViewParent, ViewManager {    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;    }}
/*** Use with {@link #focusSearch(int)}. Move focus to the previous selectable* item.*/public static final int FOCUS_BACKWARD = 0x00000001;/*** Use with {@link #focusSearch(int)}. Move focus to the next selectable* item.*/public static final int FOCUS_FORWARD = 0x00000002;/*** Use with {@link #focusSearch(int)}. Move focus to the left.*/public static final int FOCUS_LEFT = 0x00000011;/*** Use with {@link #focusSearch(int)}. Move focus up.*/public static final int FOCUS_UP = 0x00000021;/*** Use with {@link #focusSearch(int)}. Move focus to the right.*/public static final int FOCUS_RIGHT = 0x00000042;/*** Use with {@link #focusSearch(int)}. Move focus down.*/public static final int FOCUS_DOWN = 0x00000082;

3.view.requestFocus

public class View implements Drawable.Callback, KeyEvent.Callback,        AccessibilityEventSource {    public final boolean requestFocus(int direction) {        return requestFocus(direction, null);    }        public boolean requestFocus(int direction, Rect previouslyFocusedRect) {        return requestFocusNoSearch(direction, previouslyFocusedRect);    }    private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {        // need to be focusable        if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||                (mViewFlags & VISIBILITY_MASK) != VISIBLE) {            return false;        }        // need to be focusable in touch mode if in touch mode        if (isInTouchMode() &&            (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {               return false;        }        // need to not have any parents blocking us        if (hasAncestorThatBlocksDescendantFocus()) {            return false;        }        handleFocusGainInternal(direction, previouslyFocusedRect);        return true;    }}

突破口

1.focusFinder.findNextUserSpecifiedFocus()

public void setNextFocusUpId(int nextFocusUpId) {   mNextFocusUpId = nextFocusUpId;}public void setNextFocusDownId(int nextFocusDownId) {   mNextFocusDownId = nextFocusDownId;}public void setNextFocusLeftId(int nextFocusLeftId) {   mNextFocusLeftId = nextFocusLeftId;}public void setNextFocusRightId(int nextFocusRightId) {   mNextFocusRightId = nextFocusRightId;}

2.viewgroup.focusSearch(int direction)

@Overridepublic View focusSearch(int direction) {   if (direction == View.FOCUS_LEFT) {       View view = getRootView();       VerticalViewPager viewPager = (VerticalViewPager) view.findViewById(R.id.view_pager);       int currentItem = viewPager.getCurrentItem();       ViewGroup headerView = (ViewGroup) view.findViewById(R.id.header_view);       return headerView.getChildAt(currentItem);   } else {       return super.focusSearch(direction);   }}

3.viewgroup.focusSearch(View focused, int direction)

@Overridepublic View focusSearch(View focused, int direction) {   final FocusFinder ff = FocusFinder.getInstance();   View result = ff.findNextFocus(this, focused, direction);   if (result == null) {       if (direction == View.FOCUS_LEFT) {           View view = getRootView();           VerticalViewPager viewPager = (VerticalViewPager) view.findViewById(R.id.view_pager);           int currentItem = viewPager.getCurrentItem();           ViewGroup headerView = (ViewGroup) view.findViewById(R.id.header_view);           return headerView.getChildAt(currentItem);       } else {           return focused;       }   } else {       return result;   }}

4.viewGroup.requestFocus()

@Overridepublic boolean requestFocus(int direction, Rect previouslyFocusedRect) {   if (getChildCount() == 0) {       return false;   }   return super.requestFocus(direction, previouslyFocusedRect);}

5.viewGroup.onRequestFocusInDescendants()

@Overrideprotected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {   View preFocusedView = getLastChildFocusedView();   View view = preFocusedView != null ? preFocusedView : getChildAt(0);   return view.requestFocus();}

6.viewGroup.dispatchKeyEvent() 返回true,就停止焦点处理

@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {   return super.dispatchKeyEvent(event) || executeKeyEvent(event);}private boolean executeKeyEvent(KeyEvent event) {   boolean handled = false;   if (event.getAction() == KeyEvent.ACTION_DOWN) {       switch (event.getKeyCode()) {           case KeyEvent.KEYCODE_DPAD_LEFT:               break;       }   }}

7.viewGroup.addOnLayoutChangeListener()

//第一种写法//当使用这种写法的流程//1.第一个pager获取焦点//2.由于第一个pager没有内容,焦点跑到第一个radiobutton上(导航tab)viewPager.requestFocus();//2.第二中写法ViewUtil.addGlobalLayoutListenerOnce(viewPager, new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                viewPager.requestFocus();            }        });

8.发现目标的view没有正确获取焦点

1.第一步检查是否成功 view.requestFocus()
2.延时获取activity.getCurrentFocus()
3.debug上一步的view,调用栈。

参考

Android View框架总结(二)View焦点(http://blog.csdn.net/hejjunlin/article/details/52263256)

从源码出发浅析Android TV的焦点移动原理-上篇(http://mp.weixin.qq.com/s/hBWa9q3SP8LCWE_ER3FnaQ)

从源码出发浅析Android TV的焦点移动原理-下篇(http://mp.weixin.qq.com/s/ghT_2uKn5v6MgaRq1lfmrA)

原创粉丝点击