
Android IMF提供了实现第三方输入法的快捷通道,这个框架基能够满足一般输入法的开发需要,显然设计者对于IMF还是投入了不少精力。但是任何的设计都不可能是尽善尽美, 设计者并非是万能的神,所有的方面他都考虑到,总有一些地方做的不够好,留下一些瑕疵。在我的工作当中,就发现了这么一个不尽人意的地方。
此前受客户委托,开发一个输入法,其中有一个要求,即DELETE键和其他按键不一样,背景要区别于其他普通按键。这意味着我们程序应该达到这样一个效果,每个按键独立享有背景设置。
通过IMF的分析可以知道,KeyboardView的XML属性android:keyBackground,可以为按键定义背景。纵观Android PinyinIME,即谷歌拼音输入法,其每个按键的背景都是相同的,似乎它并没有想到单独为按键设置背景。
进而查看它的源码,更加印证我的猜测,在KeyboardView类的onBufferDraw方法中可以看到背景的绘制方法:
for (int i = 0; i < keyCount; i++) {
final Key key = keys[i];
if (drawSingleKey && invalidKey != key) {
continue;
}
int[] drawableState = key.getCurrentDrawableState();
keyBackground.setState(drawableState);
// Switch the character to uppercase if shift is pressed
String label = key.label == null? null : adjustCase(key.label).toString();
final Rect bounds = keyBackground.getBounds();
if (key.width != bounds.right ||
key.height != bounds.bottom) {
keyBackground.setBounds(0, 0, key.width, key.height);
}
canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
keyBackground.draw(canvas);
// 省去剩余部分
}
其中的keyBackground正是我们所要寻找的关键,可惜Android IMF的设计师在这个地方偷懒了,他没有提供给我们单独设置keyBackGround的接口。如果继续看下去,我们会看到按键icon的实现,此时你会觉得我说设计师在偷懒一点都不为过,为什么呢?我们来看按键icon部分是如何绘制的。
else if (key.icon != null) {
final int drawableX = (key.width - padding.left - padding.right
- key.icon.getIntrinsicWidth()) / 2 + padding.left;
final int drawableY = (key.height - padding.top - padding.bottom
- key.icon.getIntrinsicHeight()) / 2 + padding.top;
canvas.translate(drawableX, drawableY);
key.icon.setBounds(0, 0,
key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
key.icon.draw(canvas);
canvas.translate(-drawableX, -drawableY);
}
就这样,按键的icon可以单独设置,并且方便的绘制处理,那么为什么背景就要一刀切,所有按键一个背景呢?我认为设计师没有足够的理由来说服我。它只要在定义icon的时候,同步定义keyBackground,然后把keyBackground如同icon一样作为Keyboard.Key的一个属性,接下来的处理类似icon。这样一来,大家皆大欢喜,岂不是很好?