View.requestFocus()
先更要让某个视图获得焦点,一种犯法是用户使用物理方向键将焦点移动到该视图,另一种方法是程序员直接调用View.requestFocus()。
requestFocus()也是不能独自完成的,当一个视图想要获取焦点时,必须请求它的父视图来完成该操作。因为父视图知道当前哪个视图正在拥有焦点,如果要进行焦点的切换,则必须要先告诉原先的视图放弃焦点,而这些操作所需要的信息正是父视图中保存的,所以requestFocus()也必须由父视图来完成。
- requestFocus():空参,它被转换成requestFocus(View.FOCUS_DOWN)。
- requestFocus(int direction):它被转换成requestFocus(direction,null)。
- requestFocus(int direction , Rect preFocusRec):
- direction:往哪个方向上寻找下一个视图
- preFocusRec:当前拥有焦点的视图所占的矩形区域,这个区域是相对该视图的直接父视图来定义的。
因为该函数内部世界级上在DOWN方向上找下一个可以获得焦点的视图,至于是哪一个视图就不一定了,这取决于父视图的执行逻辑,这就是为什么该函数的返回值是一个boolean的原因,其意义是该视图到底能不能获得焦点。
/**
* Call this to try to give focus to a specific view or to one of its descendants
* and give it hints about the direction and a specific rectangle that the focus
* is coming from. The rectangle can help give larger views a finer grained hint
* about where focus is coming from, and therefore, where to show selection, or
* forward focus change internally.
*
* 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.
*
* A View will not take focus if it is not visible.
*
* A View will not take focus if one of its parents has {@link android.view.ViewGroup#getDescendantFocusability()}
* equal to {@link ViewGroup#FOCUS_BLOCK_DESCENDANTS}.
*
* See also {@link #focusSearch}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* You may wish to override this method if your custom {@link View} has an internal
* {@link View} that it wishes to forward the request to.
*
* @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
* @param previouslyFocusedRect The rectangle (in this View's coordinate system)
* to give a finer grained hint about where focus is coming from. May be null
* if there is no hint.
* @return Whether this view or one of its descendants actually took focus.
*/
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
//需要有一个获取焦点的能力
// need to be focusable
//如果当前视图的逻辑标志位的FOCUSABLE位不为 FOCUSABLE(没有获取焦点的能力),或者没有显示。直接return false。
if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
(mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
// 如果是在touch 模式下,就要有在touch 模式下获取焦点的能力。
// need to be focusable in touch mode if in touch mode
//如果当前为touchMode,并且当前视图的逻辑标志位不具备 FOCUSABLE_IN_TOUCH_MODE的属性,那么也直接return false。
if (isInTouchMode() &&
(FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
return false;
}
//需要判断是否父视图阻止当前视图获取焦点,如果阻止,则直接返回false。应用程序可以调用其父视图的 ViewGroup.setDecendantFocusability(int focusability)方法设置该VIewGroup是否要阻止其子视图获取焦点,默认情况下都不阻止。
// need to not have any parents blocking us
if (hasAncestorThatBlocksDescendantFocus()) {
return false;
}
//执行完该函数后,则该视图肯定会获取焦点,所以直接返回true。
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
View.handleFocusGainInternal(int direction , Rect previouslyFocusedRect)
/**
* Give this view focus. This will cause {@link #onFocusChanged} to be called.
*
* Note: this does not check whether this {@link View} should get focus, it just
* gives it focus no matter what. It should only be called internally by framework
* code that knows what it is doing, namely {@link #requestFocus(int, Rect)}.
*
* @param direction values are View.FOCUS_UP, View.FOCUS_DOWN,
* View.FOCUS_LEFT or View.FOCUS_RIGHT. This is the direction which
* focus moved when requestFocus() is called. It may not always
* apply, in which case use the default View.FOCUS_DOWN.
* @param previouslyFocusedRect The rectangle of the view that had focus
* prior in this View's coordinate system.
*/
void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
if (DBG) {
System.out.println(this + " requestFocus()");
}
//如果当前视图没有获得焦点。这就意味着,应用程序连续调用两次requestFocus(),第二次调用的时候就直接返回了。
if ((mPrivateFlags & FOCUSED) == 0) {
//更新当前视图的逻辑标志位。
mPrivateFlags |= FOCUSED;
//如果该视图有父视图
if (mParent != null) {
//调用父视图来使当前视图获取焦点。第一个参数为当前视图,第二个参数为真正获取申请焦点的视图,该视图可以为当前视图的子视图。
mParent.requestChildFocus(this, this);
}
//执行回调方法。应用程序可以重载该函数以便进行其他操作。这时该视图已经获取到焦点了。
onFocusChanged(true, direction, previouslyFocusedRect);
//重绘当前视图状态
refreshDrawableState();
}
}
ViewGroup.requestChildFocus(View child, View focused)
public void requestChildFocus(View child, View focused) {
if (DBG) {
System.out.println(this + " requestChildFocus()");
}
//判断当前ViewGroup的逻辑标志位是否存在 FOCUS_BLOCK_DESCENDANTS 属性,也就是是否阻止其子视图的获取焦点。
if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
return;
}
// Unfocus us, if necessary
//释放当前视图的焦点,如果有必要的话。
super.unFocus();
// We had a previous notion of who had focus. Clear it.
// mFocused 记录的是之前拥有焦点的子视图,如果之前拥有焦点的视图不是现在要请求获取焦点的视图
if (mFocused != child) {
if (mFocused != null) {
//放弃之前视图的焦点
mFocused.unFocus();
}
//更新mFocused变量
mFocused = child;
}
//如果当前ViewGroup有父视图的话,就继续递归
if (mParent != null) {
//注意,这里直接传递的是真正要拥有焦点子视图
mParent.requestChildFocus(this, focused);
}
}
ViewRoot.requestFocus()
public void requestChildFocus(View child, View focused) {
//判断当前操作线程
checkThread();
//如果ViewRoot中记录的之前拥有焦点的视图不等于当前要申请焦点的视图
if (mFocusedView != focused) {
//View树 全局焦点改变 监听回调。
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
//遍历树发起
scheduleTraversals();
}
//更新变量
mFocusedView = mRealFocusedView = focused;
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
+ mFocusedView);
}
View.requestLayout()
该函数的执行过程比较简单,因为当View树进行重新布局时,总是重新给所有的视图进行布局。因为最简单的想法就是只要设置了一个标志位属性就好了。
首先给mPrivateFlags添加 FORCE_LAYOUT标识,然后调用mParent的 ViewGroup.requestLayout()。对于一个具体的VIew对象而言,其父视图要么是一个ViewGroup,要么是一个ViewRoot。而ViewGroup没有对该方法进行重载。也就是说,ViewGroup会按照View.requestLayout()进行处理。
如果有多层视图嵌套,这就会才产生一个递归调用,并最终调用到ViewRoot类的requestLayout()。
ViewRoot.requestLayout
public void requestLayout() {
//判断线程
checkThread();
//给ViewRoot中的变量mLayoutRequested赋值为 true,之后真正进行布局的代码会检查该变量,并决定是否需要重新布局。
mLayoutRequested = true;
//发起一个View树的遍历消息
scheduleTraversals();
}