深入焦点处理流程
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):对象创建模式(下篇)