下面这个分析是基于安卓 5.0的 ,7.0的目前正在研究。
http://blog.csdn.net/johnWcheung/article/details/50615584
在Android Latin输入法的基础上进行改写,该输入法不支持中文,处理流程如图:
第一次启动输入法服务时,根据键盘布局文件创建软键盘view,分别是全字母软键盘、数字软键盘以及特殊字符软键盘。在创建软键盘时,把每一个软键盘上的所有按键放到一个HashMap中,使每一个按键都分别与一个String型的数字对应。三个软键盘view对应的HashMap分别是qwertyMap、symbolsMap和symbolsShiftMap。
添加坐标类KeyCoordinates,该类的成员属性包括横坐标x和纵坐标y,成员方法包括设置横纵坐标setX()和setY(),以及获取横纵坐标getX()和getY()。
在输入法service类中,添加输入法状态标志位属性isInputMode,默认为false。isInputMode值为true时,软键盘上显示焦点,遥控器上方向键和确认键只用于软键盘上焦点的移动和焦点所在按键字符的输入,不具有系统原有功能,isInputMode为false时,软键盘上不会显示焦点,方向键和确认键具有系统原有功能,不用于对软件盘进行操作。
添加KeyCoordinates属性用来标记当前焦点所在位置,默认横纵坐标均为-1,即(-1,-1)。添加属性num和orinum,默认均为-1,其中num用来标记当前焦点所在按键的位置,orinum用来标记上一次焦点所在按键的位置。添加属性CHANGE_INPUTMODE_KEYCODE,该常量用来标记触发输入法状态切换的KeyEvent事件的keyCode值。
在进行输入时,软键盘打开的时候没有焦点。
按下遥控器上的输入法状态切换键后,触发onKeyDown()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作。否则判断出该KeyEvent事件的keyCode等于CHANGE_INPUTMODE_KEYCODE,执行输入法状态切换操作,如果当前isInputMode为false,则将isInputMode设为true,KeyCoordinates设为(4,1),即软键盘中间按键对应的坐标位置,根据KeyCoordinates中x和y的值换算成对应的值存放到num中(如(0,1)换算成1。
软键盘不同,进行换算结果也不同,具体由软键盘按键个数和布局决定),再到当前软键盘对应的HashMap中取得num对应的按键,将焦点设到该按键上,并且将该按键的pressed状态设为true,高亮显示。
如果当前isInputMode为true,则将isInputMode设为false,将num和orinum的值均设为-1,KeyCoordinates设为(-1,-1),焦点所在按键的pressed状态设为false,取消焦点,软键盘上不再显示焦点。
按下遥控器上的左方向键,触发onKeyDown()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作,而是返回给系统进行调用。否则不返回给系统进行调用,只用于对软键盘进行操作,判断出该KeyEvent事件的keyCode等于KeyEvent.KEYCODE_DPAD_LEFT,然后再判断isInputMode的值,如果为false,不对输入法执行任何操作;如果为true,则对KeyCoordinates中的横坐标x进行操作,如果当前焦点所在的按键已经是最左边的按键,不进行任何操作,否则,x的值减1,然后将num的值赋给orinum。
根据KeyCoordinates中x和y的值换算成对应的值存放到num中,再到当前软键盘对应的HashMap中取得num对应的按键,将焦点移动到该按键上,设置该按键的pressed状态为true,高亮显示,到当前软键盘对应的HashMap中取得orinum对应的按键(即之前焦点所在的按键),将该按键pressed状态设为false,不再高亮显示。按下遥控器上其他方向键处理类似左方向键。
按下遥控器上的确认键,触发onKeyDown()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作,而是返回给系统进行调用。否则不返回给系统进行调用,只用于对软键盘进行操作,判断出该KeyEvent事件的keyCode等于KeyEvent.KEYCODE_ENTER,然后再判断isInputMode的值,如果为false,不对输入法执行任何操作;如果为true,则调用方法onKey(),输入当前焦点所在的按键。
如果该按键为普通的字符键,则输入该按键代表的字符,如果该按键为shift键,则对软键盘执行shift操作,如果该按键为软键盘切换键(比如用于由全字母软键盘切换到数字软键盘),则将软键盘切换到另外一个软键盘,切换后,KeyCoordinates设为(4,1),即切换之后的软键盘中间按键对应的坐标位置,根据KeyCoordinates中x和y的值换算成对应的值存放到num中,再到当前软键盘对应的HashMap中取得num对应的按键,将焦点设到该按键上,并且将该按键的pressed状态设为true,高亮显示。
切换之前的软件盘上不再显示焦点,焦点所在按键的pressed状态设为false,如果该按键为删除键,则删除已输入的字符,如果该按键为确认键,则将执行系统调用的确认操作,按键的pressed状态设为false,软键盘上不再显示焦点,完成本次输入。
按下遥控器上的方向键或确认键,然后在释放的时候,触发onKeyUp()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作,而是返回给系统进行调用。如果为true,则不执行任何操作,也不返回给系统进行调用。当然要想支持中文,也可以修改android自带的google拼音输入法,原理同上。
LatinIME代码定向修改
添加功能
为输入法软键盘键子添加焦点(边框),并支持键盘上的方向键,在按上下左右方向键时,焦点跟着方向键移动;并支持键盘回车按钮,在按下回车键后,将软键盘对应键子内容输入到editor(编辑框)。
代码修改部分
1、在LatinKeyboardView中重写onDraw(Canvas canvas)
//BEGIN: add Keyboard focus
private Keyboard currentKeyboard;
private List<Key> keys = new ArrayList<Key>();
private int lastKeyIndex = 0;
private Key focusedKey;
private Rect rect;
/**
* override onDraw() method to draw rectangle focus round key
* @param canvas
*/
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentKeyboard = this.getKeyboard();
keys = currentKeyboard.mKeys;
Paint p = new Paint();
p.setColor(Color.CYAN);
p.setStyle(Style.STROKE);
p.setStrokeWidth(4.0f);
if(lastKeyIndex > keys.size()){
lastKeyIndex = keys.size() - 1;
}
focusedKey = keys.get(lastKeyIndex);
rect = new Rect(focusedKey.mX,focusedKey.mY+2,focusedKey.mX+focusedKey.mWidth,focusedKey.mY+focusedKey.mHeight);
if(this.isFocusFlag()){
canvas.drawRect(rect, p);
}
}
/** provide lastKeyIndex access */
public int getLastKeyIndex() {
return lastKeyIndex;
}
/** set key index */
public void setLastKeyIndex(int index) {
this.lastKeyIndex = index;
}
/**The keyboard is get the focus*/
private boolean focusFlag = false;
public boolean isFocusFlag() {
return focusFlag;
}
public void setFocusFlag(boolean focusFlag) {
this.focusFlag = focusFlag;
}
2、在LatinIME类中重写onKeyDown(int keyCode, KeyEvent event),在方法中对方向键进行判断,并设计焦点移动过程。
private int nCurKeyboardKeyNums;
private Keyboard nCurrentKeyboard;
private List<Keyboard.Key> nKeys;
private int nLastKeyIndex = 0;
private void setFields() {
if (null == mInputView) return;
nCurrentKeyboard = mInputView.getKeyboard();
nKeys = nCurrentKeyboard.getKeys();
nCurKeyboardKeyNums = nKeys.size();
nLastKeyIndex = mInputView.getLastKeyIndex();
}
/** Deal with remote control key event
*
* @param keyCode
* @param event
* @return
*//*
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
hideFlag = false;
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.getRepeatCount() == 0) {
if (mSuggestionsView !=null && mSuggestionsView.handleBack()) {
return true;
}
LatinKeyboardViewkeyboardView = mKeyboardSwitcher.getKeyboardView();
if (keyboardView != null && keyboardView.handleBack()) {
return true;
}
}
if (inputView != null && inputView.handleBack()) {
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex >= nCurKeyboardKeyNums - 1) {
inputView.setLastKeyIndex(0);
} else {
int[] nearestKeyIndices= nCurrentKeyboard.getNearestKeys(
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
Key lastKey = nKeys.get(nLastKeyIndex);
for (int i = 0; i < nearestKeyIndices.length; i++) {
int index = nearestKeyIndices[i];
if (nLastKeyIndex < index) {
Key nearestKey = nKeys.get(index);
// tell right bound
if ((lastKey.mX+ lastKey.mWidth) > nearestKey.mX
&& (lastKey.mX + lastKey.mWidth) <= (nearestKey.mX + nearestKey.mWidth)) {
inputView.setLastKeyIndex(index);
break;
}
// ensure not bound outof softkeyboard
if (nearestKey.mX + nearestKey.mWidth
+ lastKey.mWidth > nCurrentKeyboard.mOccupiedWidth
&& lastKey.mX >= nearestKey.mX
&& lastKey.mX <(nearestKey.mX + nearestKey.mWidth)) {
Log.d(TAG, "if two " + index);
inputView.setLastKeyIndex(index);
break;
}
if (i == (nearestKeyIndices.length - 1)
&& (lastKey.mY + lastKey.mHeight + nearestKey.mHeight) <= nCurrentKeyboard.mOccupiedHeight) {
Log.d(TAG, "if three " + index);
inputView.setLastKeyIndex(index);
break;
}
}
}// end for loop
}
if (inputView != null && inputView.isShown()) {
inputView.setFocusFlag(true);
inputView.invalidate();
}
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex <= 0) {
inputView.setLastKeyIndex(nCurKeyboardKeyNums -1);
} else {
int[] nearestKeyIndices= nCurrentKeyboard.getNearestKeys(
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
// get current displayed
Key lastKey = nKeys.get(nLastKeyIndex);
for (int i = nearestKeyIndices.length -1; i >= 0; i--) {
int index = nearestKeyIndices[i];
if (nLastKeyIndex > index) {
// get the nextkey
Key nearKey = nKeys.get(index);
// BEGIN: laskKey+1 :Because the last key better
// than his last one button x to small 1
if (lastKey.mX + 1 >= nearKey.mX
&& lastKey.mX <nearKey.mX + nearKey.mWidth) {
inputView.setLastKeyIndex(index);
break;
} else if (nearKey.mX -nearKey.mWidth < 0
&& lastKey.mX <= nearKey.mX) {
inputView.setLastKeyIndex(index);
break;
}
}
}// end for loop
}
if (inputView != null && inputView.isShown()) {
inputView.setFocusFlag(true);
inputView.invalidate();
}
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex <= 0) {
inputView.setLastKeyIndex(nCurKeyboardKeyNums -1);
} else {
nLastKeyIndex--;
inputView.setLastKeyIndex(nLastKeyIndex);
}
inputView.setFocusFlag(true);
inputView.invalidate();
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex >= nCurKeyboardKeyNums - 1) {
inputView.setLastKeyIndex(0);
} else {
nLastKeyIndex++;
inputView.setLastKeyIndex(nLastKeyIndex);
}
inputView.setFocusFlag(true);
inputView.invalidate();
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
Log.d(TAG, "[onKeyDown]::dpad_center========"+ keyCode);
if (inputView != null && inputView.isShown()) {
setFields();
final Key key = nKeys.get(nLastKeyIndex);
if (key.mOutputText != null) {
onTextInput(key.mOutputText);
} else if(mKeyboardSwitcher.isShiftedOrShiftLocked() && mKeyboardSwitcher.isShiftLocked()) {
int curKeyCode = nKeys.get(nLastKeyIndex).mCode;
onCodeInput(curKeyCode, new int[] { curKeyCode },
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
} else if(mKeyboardSwitcher.isShiftedOrShiftLocked() && null != key.mHintLabel) {
onTextInput(key.mHintLabel);
} else {
int curKeyCode = nKeys.get(nLastKeyIndex).mCode;
onCodeInput(curKeyCode, new int[] { curKeyCode },
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
}
return true;
}
break;
case KeyEvent.KEYCODE_DEL:
keyCode = -67;
onCodeInput(keyCode, new int[] {keyCode}, 0, 0);
break;
default:
if ((false == event.isCapsLockOn()) && (false == event.isShiftPressed())
&&((keyCode <= KeyEvent.KEYCODE_Z && keyCode >= KeyEvent.KEYCODE_A))) {
if (event.getKeyCode() <= KeyEvent.KEYCODE_Z) {
//Change to char key
keyCode = keyCode + 68;
onCodeInput(keyCode, new int[] { keyCode },
0,
0);
return true;
}
} else if (keyCode == KeyEvent.KEYCODE_SPACE) {
//Change to space key
keyCode = 32;
onCodeInput(keyCode, new int[] { keyCode },
0,
0);
return true;
}
//END
break;
}
return super.onKeyDown(keyCode,event);
}