深入焦点处理流程

Posted ihrthk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入焦点处理流程相关的知识,希望对你有一定的参考价值。

深入焦点处理流程

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)

@Override
public 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)

@Override
public 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()

@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) 
   if (getChildCount() == 0) 
       return false;
   
   return super.requestFocus(direction, previouslyFocusedRect);

5.viewGroup.onRequestFocusInDescendants()

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

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

@Override
public 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)

以上是关于深入焦点处理流程的主要内容,如果未能解决你的问题,请参考以下文章

精华推荐 | 深入浅出 RocketMQ原理及实战「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(上篇)

深入理解JavaScript系列(48):对象创建模式(下篇)

JVM技术专题深入分析内存布局及GC原理分析「中卷」

玩转 SpringBoot 2 之整合 JWT 下篇

深入理解JavaScript系列(50):Function模式(下篇)

Boosting算法的前世今生(下篇)