AndroidTV——解决EditText焦点无法转移问题

2,474 阅读2分钟

经反馈,在AndroidTV9.0 盒子上发现EditText一旦获取了焦点之后,遥控器上的方向键就无效了,即焦点无法再转移到其他焦点控件上,但这个问题在Android7.0及以下盒子上却不会出现,应该是Android8.0及以上源码做了修改,故查看EditText源码,得知确实是官方源码对方向键进行了处理~

一、源码分析

EditText源码中搜索onKeyDown,定位到父类TextView中的onKeyDown()

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    final int which = doKeyDown(keyCode, event, null);
    if (which == KEY_EVENT_NOT_HANDLED) {
        return super.onKeyDown(keyCode, event);
    }
    return true;
}

很明显doKeyDown()是关键方法,查看该方法的代码逻辑,主意看注释的部分。

大意:消费掉键盘的箭头事件以防止焦点离开。

private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) {
	...
	if (mMovement != null && mLayout != null) {
		...
		// Consume arrows from keyboard devices to prevent focus leaving the editor.
		// DPAD/JOY devices (Gamepads, TV remotes) often lack a TAB key so allow those
		// to move focus with arrows.
		if (event.getSource() == InputDevice.SOURCE_KEYBOARD
				&& isDirectionalNavigationKey(keyCode)) {
			return KEY_EVENT_HANDLED;
		}
	}

	return mPreventDefaultMovement && !KeyEvent.isModifierKey(keyCode)
			? KEY_EVENT_HANDLED : KEY_EVENT_NOT_HANDLED;
}

private boolean isDirectionalNavigationKey(int keyCode) {
	switch(keyCode) {
		case KeyEvent.KEYCODE_DPAD_UP:
		case KeyEvent.KEYCODE_DPAD_DOWN:
		case KeyEvent.KEYCODE_DPAD_LEFT:
		case KeyEvent.KEYCODE_DPAD_RIGHT:
			return true;
	}
	return false;
}

二、自定义EditText

通过上面的源码分析,明确原因之后,解决思路也就很明朗了,即:去掉对键盘方向事件的控制即可。但不管是doKeyDown()还是isDirectionalNavigationKey(),这些方法都是私有的,无法通过重写的方式来改变原有逻辑,能重写的只有onKeyDown(),所以,结合FocusFinder通过手动查找焦点控件的方式来处理就好了,完整代码如下:

/**
 * @author LQR
 * @time 2020/3/31
 * @desc 解决Android8.0 EditText获取焦点后,无法转换焦点问题
 */
@SuppressLint("AppCompatCustomView")
public class LQREditText extends EditText {
    public LQREditText(Context context) {
        super(context);
    }

    public LQREditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public LQREditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public LQREditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (isDirectKeyCode(keyCode)) {
            int direction = FOCUS_DOWN;
            switch (keyCode) {
                case KeyEvent.KEYCODE_DPAD_UP:
                    direction = FOCUS_UP;
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    direction = FOCUS_DOWN;
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
                    direction = FOCUS_LEFT;
                    break;
                case KeyEvent.KEYCODE_DPAD_RIGHT:
                    direction = FOCUS_RIGHT;
                    break;
            }
            View nextFocus = FocusFinder.getInstance().findNextFocus((ViewGroup) getParent(), this, direction);
            if (nextFocus != null) {
                nextFocus.requestFocus();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private boolean isDirectKeyCode(int keyCode) {
        return keyCode == KeyEvent.KEYCODE_DPAD_UP
                || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
                || keyCode == KeyEvent.KEYCODE_DPAD_LEFT
                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT;
    }
}

欢迎关注微信公众号:全栈行动