android焦点概念,android焦点分析

在tv端开发中,焦点处理是一个非常重要的技术。该篇主要是想整理相关知识。(本文档依据sdk26进行分析)

第一次寻焦

在 android 的绘制流程中**ViewRootImpl#performTraversals()**起着关键的作用,而焦点状态也会通过影响视图的绘制。

下面来看看android事如何进行第一次寻焦的

private void performTraversals() {

......

if (mFirst && sAlwaysAssignFocus) {

// handle first focus request

if (mView != null) {

if (!mView.hasFocus()) {

mView.restoreDefaultFocus();

}

}

}

......

}

复制代码

mView.restoreDefaultFocus() 将会去查找当前试图第一个可聚焦的View。将会执行

requestFocus(int direction, Rect previouslyFocusedRect) 。因为ViewGroup重写了该方法,增加了是否拦截焦点处理的逻辑,下面我们先来看看 ViewGroup#requestFocus(int direction, Rect previouslyFocusedRect)

@Override

public boolean requestFocus(int direction, Rect previouslyFocusedRect) {

if (DBG) {

System.out.println(this + " ViewGroup.requestFocus direction="

+ direction);

}

int descendantFocusability = getDescendantFocusability();

switch (descendantFocusability) {

//拦截焦点,不管当前View是否被聚焦,子View一定获取不到焦点。

case FOCUS_BLOCK_DESCENDANTS:

return super.requestFocus(direction, previouslyFocusedRect);

//在子View之前判断是否应被聚焦,如果为false则会去判断其子View

case FOCUS_BEFORE_DESCENDANTS: {

final boolean took = super.requestFocus(direction, previouslyFocusedRect);

return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);

}

// 在子View判断焦点之后判断

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);

}

}

复制代码

接着我们来看看 View#requestFocus(int direction, Rect previouslyFocusedRect)

public boolean requestFocus(int direction, Rect previouslyFocusedRect) {

return requestFocusNoSearch(direction, previouslyFocusedRect);

}

private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {

// 判断是否可被聚焦

if ((mViewFlags & FOCUSABLE) != FOCUSABLE

|| (mViewFlags & VISIBILITY_MASK) != VISIBLE) {

return false;

}

// 判断触摸状态下是否可被聚焦

if (isInTouchMode() &&

(FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {

return false;

}

// 判断父View是否需要拦截

if (hasAncestorThatBlocksDescendantFocus()) {

return false;

}

//执行聚焦操作

handleFocusGainInternal(direction, previouslyFocusedRect);

return true;

}

void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {

if (DBG) {

System.out.println(this + " requestFocus()");

}

if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {

//设置聚焦标志位

mPrivateFlags |= PFLAG_FOCUSED;

View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;

if (mParent != null) {

//通知父容器改变焦点View

mParent.requestChildFocus(this, this);

updateFocusedInCluster(oldFocus, direction);

}

if (mAttachInfo != null) {

//全局监听回调

mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);

}

//执行焦点变化与强制绘制

onFocusChanged(true, direction, previouslyFocusedRect);

refreshDrawableState();

}

}

复制代码

遥控器方向键后的寻焦逻辑

首先会根据按键生成一个寻焦方向(可以查看ViewRootImpl#ViewPostImeInputStage#processKeyEvent)

private int processKeyEvent(QueuedInputEvent q) {

......

//上面是判断是否处理当前按键,如dispatchKeyEvent返回true则不会执行下面的焦点逻辑

// 根据当前事件生成一个寻焦方向。

if (event.getAction() == KeyEvent.ACTION_DOWN) {

int direction = 0;

switch (event.getKeyCode()) {

case KeyEvent.KEYCODE_DPAD_LEFT:

if (event.hasNoModifiers()) {

direction = View.FOCUS_LEFT;

}

break;

case KeyEvent.KEYCODE_DPAD_RIGHT:

if (event.hasNoModifiers()) {

direction = View.FOCUS_RIGHT;

}

break;

case KeyEvent.KEYCODE_DPAD_UP:

if (event.hasNoModifiers()) {

direction = View.FOCUS_UP;

}

break;

case KeyEvent.KEYCODE_DPAD_DOWN:

if (event.hasNoModifiers()) {

direction = View.FOCUS_DOWN;

}

break;

case KeyEvent.KEYCODE_TAB:

if (event.hasNoModifiers()) {

direction = View.FOCUS_FORWARD;

} else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {

direction = View.FOCUS_BACKWARD;

}

break;

}

if (direction != 0) {

//查询当前聚集view,根据当前view查询下一个方向的聚焦view

View focused = mView.findFocus();

if (focused != null) {

//由此可见,focusSearch 为寻焦的主要方法,可重新该方法来修改焦点逻辑

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);

}

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;

}

复制代码

下面可查看View#focusSearch与ViewGroup#focusSearch 的相关处理

View.java

public View focusSearch(@FocusRealDirection int direction) {

if (mParent != null) {

return mParent.focusSearch(this, direction);

} else {

return null;

}

}

ViewGroup.java

public View focusSearch(View focused, int direction) {

if (isRootNamespace()) {

// 通过FocusFinder来查找下一个聚焦view

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

} else if (mParent != null) {

return mParent.focusSearch(focused, direction);

}

return null;

}

复制代码

FocusFinder 是一个焦点处理的类,主要用于在一个方向上,通过当前view与聚焦方案来合理判断下一个被聚焦view

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值