本文主要介绍openwnn对Keyboard和KeyboardView的处理。

这一部分主要涉及三个类:InputViewManager.java,DefaultSoftKeyboard.java,DefaultSoftKeyboardJAJP.java。其中InputViewManager是与键盘相关的对外接口,DefaultSoftKeyboard是通用类,DefaultSoftKeyboardJAJP是日文定制类。

1、InputViewManager

第一步我们先来看看InputViewManager。这个接口类代码很简单:

[java] view plain copy
  1. /**
  2. *TheinterfaceofinputviewmanagerusedbyOpenWnn.
  3. *
  4. *@authorCopyright(C)2009OMRONSOFTWARECO.,LTD.AllRightsReserved.
  5. */
  6. publicinterfaceInputViewManager{
  7. /**
  8. *Initializetheinputview.
  9. *
  10. *@paramparentTheOpenWnnobject
  11. *@paramwidthThewidthofthedisplay
  12. *@paramheightTheheightofthedisplay
  13. *
  14. *@returnTheinputviewcreatedintheinitializeprocess;{@codenull}ifcannotcreateainputview.
  15. */
  16. publicViewinitView(OpenWnnparent,intwidth,intheight);
  17. /**
  18. *Gettheinputviewbeingusedcurrently.
  19. *
  20. *@returnTheinputview;{@codenull}ifnoinputviewisusedcurrently.
  21. */
  22. publicViewgetCurrentView();
  23. /**
  24. *Notificationofupdatingparent'sstate.
  25. *
  26. *@paramparentTheOpenWnnobjectusingthismanager
  27. */
  28. publicvoidonUpdateState(OpenWnnparent);
  29. /**
  30. *Reflectthepreferencesintheinputview.
  31. *
  32. *@paramprefThepreferences
  33. *@parameditorTheinformationabouttheeditor
  34. */
  35. publicvoidsetPreferences(SharedPreferencespref,EditorInfoeditor);
  36. /**
  37. *Closetheinputview.
  38. */
  39. publicvoidclosing();
  40. }
从这个接口文件中,我们可以看出在输入法处理中,对于键盘部分需要涉及的操作并不是很多。

2、配置项

这里我们先从简单的部分开始研究。第一个是setPreferences,这只配置项。这一步的工作是读取与键盘部分有关的配置项,在生成键盘(改变键盘)时进行设置。代码中设计到的配置项很减少,只有:震动、声音、是否自动切换大写。在DefaultSoftKeyboard.java中只设置了震动和声音,在DefaultSoftKeyboardJAJP.java添加了是否自动切换为大写。具体大家可以看代码,对于是否自动切换大写,从代码上看,我猜测,有些输入框默认是输入大写的。(这一部分我想不到例子,谁有例子可以share一下)

3、KeyboardView

在InputViewManager中有initView这个函数,实际上使用来生成KeyboardView的。其源码如下:

在DefaultSoftKeyboard.java中:

[java] view plain copy
  1. /**@seejp.co.omronsoft.openwnn.InputViewManager#initView*/
  2. publicViewinitView(OpenWnnparent,intwidth,intheight){
  3. mWnn=parent;
  4. mDisplayMode=(width==320)?PORTRAIT:LANDSCAPE;
  5. /*
  6. *createkeyboards&theview.
  7. *Tore-displaytheinputviewwhenthedisplaymodeischangedportrait<->landscape,
  8. *createkeyboardseverytime.
  9. */
  10. createKeyboards(parent);
  11. SharedPreferencespref=PreferenceManager.getDefaultSharedPreferences(parent);
  12. Stringskin=pref.getString("keyboard_skin",
  13. mWnn.getResources().getString(R.string.keyboard_skin_id_default));
  14. Log.d("OpenWnn","keyboard_skin="+skin);
  15. intid=parent.getResources().getIdentifier(skin,"layout","jp.co.omronsoft.openwnn");
  16. mKeyboardView=(KeyboardView)mWnn.getLayoutInflater().inflate(id,null);
  17. mKeyboardView.setOnKeyboardActionListener(this);
  18. mCurrentKeyboard=null;
  19. mMainView=(ViewGroup)parent.getLayoutInflater().inflate(R.layout.keyboard_default_main,null);
  20. mSubView=(ViewGroup)parent.getLayoutInflater().inflate(R.layout.keyboard_default_sub,null);
  21. if(mDisplayMode==LANDSCAPE&&!mHardKeyboardHidden){
  22. mMainView.addView(mSubView);
  23. }
  24. if(mKeyboardView!=null){
  25. mMainView.addView(mKeyboardView);
  26. }
  27. returnmMainView;
  28. }
其中我们可以看到,程序先通过layout文件创建KeyboardView,然后通过将其封装在一个mMainView的view变量中。这里很重要的一段代码是,他会去配置项中读取”skin“这一配置项,并通过该配置项去读取对应的layout文件。这一步是动态变换皮肤的关键,也就是说你可以在配置项中选择不同的皮肤,程序会根据你的选择来生成不同的皮肤。

另外,从SoftKeyboard项目中我们知道,KeyboardView实际上是装着一个Keyboard。但在这一段代码中只有生成Keyboard,并未将Keyboard封装到KeyboardView里面,因为当前键盘变量mCurrentKeyboard是空的。这里我们猜测在继承类中会做封装Keyboard的操作。我们看DefaultSoftKeyboardJAJP.java的代码:

[java] view plain copy
  1. /**@seejp.co.omronsoft.openwnn.DefaultSoftKeyboard#initView*/
  2. @OverridepublicViewinitView(OpenWnnparent,intwidth,intheight){
  3. Viewview=super.initView(parent,width,height);
  4. changeKeyboard(mKeyboard[mCurrentLanguage][mDisplayMode][mCurrentKeyboardType][mShiftOn][mCurrentKeyMode][0]);
  5. returnview;
  6. }
这里changeKeyboard是父类DefaultSoftKeyboard中的函数,其代码为:

[java] view plain copy
  1. /**
  2. *Changethekeyboard.
  3. *
  4. *@paramkeyboardThenewkeyboard
  5. *@return{@codetrue}ifthekeyboardischanged;{@codefalse}ifnotchanged.
  6. */
  7. protectedbooleanchangeKeyboard(Keyboardkeyboard){
  8. if(keyboard==null){
  9. returnfalse;
  10. }
  11. if(mCurrentKeyboard!=keyboard){
  12. mKeyboardView.setKeyboard(keyboard);
  13. mKeyboardView.setShifted((mShiftOn==0)?false:true);
  14. mCurrentKeyboard=keyboard;
  15. returntrue;
  16. }else{
  17. mKeyboardView.setShifted((mShiftOn==0)?false:true);
  18. returnfalse;
  19. }
  20. }
这里我们可以看到mKeyboardView.setKeyboard(keyboard)这一句,这就验证了我们对KeyboardView实际上是装着一个Keyboard的猜测。

4、Keyboard

这里的Keyboard异常复杂,以至于需要一个5维数组来维护。

[java] view plain copy
  1. /**
  2. *Keyboardsurfaces
  3. *<br>
  4. *Keyboard[language][portrait/landscape][keyboardtype][shiftoff/on][key-mode]
  5. */
  6. protectedKeyboard[][][][][][]mKeyboard;
在DefaultSoftKeyboard.java中有许多函数是在做键盘切换的。另外,这个数组中的每个元素都是一个Keyboard,他们在DefaultSoftKeyboardJAJP.java类中的createKeyboards函数中创建,其中又调用了createKeyboardsPortrait和createKeyboardsLandscape两个函数来做具体的创建工作。具体而言是每个键盘对应一个配置文件,这些文件存放在xml文件夹下面。

键盘的处理还是比复杂,因为根据不同的输入框,需要显示不同的键盘;同时用户可以选择不同的输入法模式,此时又需要显示不同的键盘。这一点从onUpdateState 函数,以及onKey函数等可以看出。

另外,有些手机不是纯触摸屏的,也就是带有键盘的手机。对于这些手机是不会显示软键盘的(我猜测),对此需要对硬件盘信息进行设置。其中setHardKeyboardHidden函数就是用于做这些事情的。

5、onUpdateState

从函数名我们可以看出这一函数的目的是用于更新状态,主要是用户更新键盘状态(或者是切换键盘)。从DefaultSoftKeyboard类中代码可以看出:

[java] view plain copy
  1. /**@seejp.co.omronsoft.openwnn.InputViewManager#onUpdateState*/
  2. publicvoidonUpdateState(OpenWnnparent){
  3. try{
  4. if(parent.mComposingText.size(1)==0){
  5. if(!mNoInput){
  6. /*whenthemodechangedto"noinput"*/
  7. mNoInput=true;
  8. KeyboardnewKeyboard=getKeyboardInputed(false);
  9. if(mCurrentKeyboard!=newKeyboard){
  10. changeKeyboard(newKeyboard);
  11. }
  12. }
  13. }else{
  14. if(mNoInput){
  15. /*whenthemodechangedto"inputsomecharacters"*/
  16. mNoInput=false;
  17. KeyboardnewKeyboard=getKeyboardInputed(true);
  18. if(mCurrentKeyboard!=newKeyboard){
  19. changeKeyboard(newKeyboard);
  20. }
  21. }
  22. }
  23. }catch(Exceptionex){
  24. }
  25. }
其中mNoInput定义为:

[java] view plain copy
  1. /**
  2. *Statusofthecomposingtext
  3. *<br>
  4. *{@codetrue}ifthereisnocomposingtext.
  5. */
  6. protectedbooleanmNoInput=true;

这一段代码主要是根据当前的输入状态,来更新键盘的。程序判断当前输入串(这里指输入时,带有下划线的输入串)是否为空。若输入串为空,判断mNoInput是否为有输入串(false),若是,则mNoInput改为true,同时键盘改为没有输入串的状态;若输入串不为空,此时若未没有输入串的状态,则要改为有输入串的状态,并相应修改键盘。

而DefaultSoftKeyboardJAJP中的这段代码则较为简单:

[java] view plain copy
  1. /**@seejp.co.omronsoft.openwnn.DefaultSoftKeyboard#onUpdateState*/
  2. @OverridepublicvoidonUpdateState(OpenWnnparent){
  3. super.onUpdateState(parent);
  4. setShiftByEditorInfo();
  5. }
[java] view plain copy
  1. /**
  2. *Settheshiftkeystatefrom{@linkEditorInfo}.
  3. */
  4. privatevoidsetShiftByEditorInfo(){
  5. if(mEnableAutoCaps&&(mCurrentKeyMode==KEYMODE_JA_HALF_ALPHABET)){
  6. intshift=getShiftKeyState(mWnn.getCurrentInputEditorInfo());
  7. mShiftOn=shift;
  8. changeKeyboard(getShiftChangeKeyboard(shift));
  9. }
  10. }
这一部分代码主要是根据当前输入框的特点相应设置对应的键盘。
6、输入方式(直接上屏或者参与变换)

另外,在使用输入法输入时,通常会有两种方式,一种是参与变换,一种是直接上屏。参与变换是指,你输入的内容与你需要选择的内容不是一致的,而是通过一系列复杂的变换得到的,比如你输入”kawai“得到”可愛“,就是通过变换而来的;而你在输入字符或者数字时,通常是直接上屏的。前者需要显示CandidateView的,而后者不要。

这一点可能影响的地方包括:不同的输入框(如密码输入框)和不同的输入模式(或者说是输入内容),比如输入数字和字符时,通常就是直接上屏的。

对于这一部分的技术处理,我们从DefaultSoftKeyboardJAJP类中的changeKeyMode函数可以看出一点端倪。由于该函数代码有点长,就不展现出来了。我们看其中几行代码:

[java] view plain copy
  1. caseKEYMODE_JA_HALF_ALPHABET:
  2. if(USE_ENGLISH_PREDICT){
  3. mInputType=INPUT_TYPE_TOGGLE;
  4. mode=OpenWnnEvent.Mode.NO_LV1_CONV;
  5. }else{
  6. mInputType=INPUT_TYPE_TOGGLE;
  7. mode=OpenWnnEvent.Mode.DIRECT;
  8. }
  9. break;
如果当前的模式是半角字母输入,则若使用英文预测,则使用的是参与变换的输入方式,若不是用英文预测,则是直接上屏的输入方式。

另外changeKeyMode函数的前两行是

[java] view plain copy
  1. inttargetMode=keyMode;
  2. commitText();
这是调用了commitText函数,该函数的主要功能是上屏,也就是将输入的内容输出到输入框(上屏后的内容没有下划线),在切换输入模式时,通常需要先将当前输入的内容上屏。

另外,需要提一下,在openwnn日文输入法中,有一个功能是切换输入模式,如下图


左下角那个按钮,你按一下他会不断变换,从假名输入、英文输入、数字输入循环切换。

其实现方式如下:

[java] view plain copy
  1. /**Inputmodetogglecycletable*/
  2. privatestaticfinalint[]JP_MODE_CYCLE_TABLE={
  3. KEYMODE_JA_FULL_HIRAGANA,KEYMODE_JA_HALF_ALPHABET,KEYMODE_JA_HALF_NUMBER
  4. };
[java] view plain copy
  1. /**
  2. *Changetothenextinputmode
  3. */
  4. privatevoidnextKeyMode(){
  5. /*Searchthecurrentmodeinthetoggletable*/
  6. booleanfound=false;
  7. intindex;
  8. for(index=0;index<JP_MODE_CYCLE_TABLE.length;index++){
  9. if(JP_MODE_CYCLE_TABLE[index]==mCurrentKeyMode){
  10. found=true;
  11. break;
  12. }
  13. }
  14. if(!found){
  15. /*Ifthecurrentmodenotexists,setthedefaultmode*/
  16. setDefaultKeyboard();
  17. }else{
  18. /*Ifthecurrentmodeexists,setthenextinputmode*/
  19. index++;
  20. if(JP_MODE_CYCLE_TABLE.length<=index){
  21. index=0;
  22. }
  23. changeKeyMode(JP_MODE_CYCLE_TABLE[index]);
  24. }
  25. }
这里从程序中就很容易看出是一个循环切换的过程。同时这个函数,在类似如下场合调用:

[java] view plain copy
  1. /**@seejp.co.omronsoft.openwnn.DefaultSoftKeyboard#onKey*/
  2. @OverridepublicvoidonKey(intprimaryCode,int[]keyCodes){
  3. switch(primaryCode){
  4. caseKEYCODE_JP12_TOGGLE_MODE:
  5. caseKEYCODE_QWERTY_TOGGLE_MODE:
  6. nextKeyMode();
  7. break;

7、输入变换

7.1 12key键盘

在12key的键盘中,是无法显示所有输入内容的。于是你按一个键,可能包含多个信息。比如诺基亚的12键键盘:


在这种键盘中,你要输入c,则要按三下”2“键才可以。

同样的,在日文输入法中,如下键盘(软键盘),你是无法显示所有输入内容的,因此,你可能也像用诺基亚键盘一样,需要按多次才可以输入一个内容。


比如,在如上键盘中,你按”か“键,则按1下、2下、3下、4下,5下,6下……显示的内容分别是:"か","き", "く", "け", "こ","か",……。(注意需要在假名输入模式下)

这里的程序实现是比较巧妙,其中涉及的代码如下:

[java] view plain copy
  1. /**Togglecycletableforfull-widthHIRAGANA*/
  2. privatestaticfinalString[][]JP_FULL_HIRAGANA_CYCLE_TABLE={
  3. {"\u3042","\u3044","\u3046","\u3048","\u304a","\u3041","\u3043","\u3045","\u3047","\u3049"},
  4. {"\u304b","\u304d","\u304f","\u3051","\u3053"},
  5. {"\u3055","\u3057","\u3059","\u305b","\u305d"},
  6. {"\u305f","\u3061","\u3064","\u3066","\u3068","\u3063"},
  7. {"\u306a","\u306b","\u306c","\u306d","\u306e"},
  8. {"\u306f","\u3072","\u3075","\u3078","\u307b"},
  9. {"\u307e","\u307f","\u3080","\u3081","\u3082"},
  10. {"\u3084","\u3086","\u3088","\u3083","\u3085","\u3087"},
  11. {"\u3089","\u308a","\u308b","\u308c","\u308d"},
  12. {"\u308f","\u3092","\u3093","\u308e","\u30fc"},
  13. {"\u3001","\u3002","\uff1f","\uff01","\u30fb","\u3000"},
  14. };
这是一个循环变换table,其内容用utf-8码显示,对此你可能有点迷糊,但是我把他转为日文,你就懂了:

[java] view plain copy
  1. {"あ","い","う","え","お","ぁ","ぃ","ぅ","ぇ","ぉ"},
  2. {"か","き","く","け","こ"},
  3. {"さ","し","す","せ","そ"},
  4. {"た","ち","つ","て","と","っ"},
  5. {"な","に","ぬ","ね","の"},
  6. {"は","ひ","ふ","へ","ほ"},
  7. {"ま","み","む","め","も"},
  8. {"や","ゆ","よ","ゃ","ゅ","ょ"},
  9. {"ら","り","る","れ","ろ"},
  10. {"わ","を","ん","ゎ","ー"},
  11. {"、","。","?","!","・"," "},
看到了吧,第二行就是我们刚才按 “键那个例子中显示的内容。因此其实现我们估计也是很简单的,就是不断的读取这一行的内容。其程序如下:

在@Override public void onKey(int primaryCode, int[] keyCodes)函数中:

[java] view plain copy
  1. caseKEYCODE_JP12_SHARP:
  2. /*Processingtoinputbytenkey*/
  3. if(mInputType==INPUT_TYPE_INSTANT){
  4. /*Sendainputcharacterdirectlyifinstantinputtypeisselected*/
  5. commitText();
  6. mWnn.onEvent(newOpenWnnEvent(OpenWnnEvent.INPUT_CHAR,
  7. mCurrentInstantTable[getTableIndex(primaryCode)]));
  8. }else{
  9. if((mPrevInputKeyCode!=primaryCode)){
  10. if((mCurrentKeyMode==KEYMODE_JA_HALF_ALPHABET)
  11. &&(primaryCode==KEYCODE_JP12_SHARP)){
  12. /*Committextbysymbolcharacter(',''.')whenalphabetinputmodeisselected*/
  13. commitText();
  14. }
  15. }
  16. /*Convertthekeycodetothetableindexandsendthetoggleeventwiththetableindex*/
  17. String[][]cycleTable=getCycleTable();
  18. if(cycleTable==null){
  19. Log.e("OpenWnn","notfoundscycletable");
  20. }else{
  21. intindex=getTableIndex(primaryCode);
  22. mWnn.onEvent(newOpenWnnEvent(OpenWnnEvent.TOGGLE_CHAR,cycleTable[index]));
  23. mCurrentCycleTable=cycleTable[index];
  24. }
  25. mPrevInputKeyCode=primaryCode;
  26. }
  27. break;
其中getCycleTable()定义如下:

[java] view plain copy
  1. /**
  2. *Getthetoggletableforinputthatisappropriateincurrentmode.
  3. *
  4. *@returnThetoggletableforinput
  5. */
  6. privateString[][]getCycleTable(){
  7. String[][]cycleTable=null;
  8. switch(mCurrentKeyMode){
  9. caseKEYMODE_JA_FULL_HIRAGANA:
  10. cycleTable=JP_FULL_HIRAGANA_CYCLE_TABLE;
  11. break;
  12. caseKEYMODE_JA_FULL_KATAKANA:
  13. cycleTable=JP_FULL_KATAKANA_CYCLE_TABLE;
  14. break;
  15. caseKEYMODE_JA_FULL_ALPHABET:
  16. cycleTable=JP_FULL_ALPHABET_CYCLE_TABLE;
  17. break;
  18. caseKEYMODE_JA_FULL_NUMBER:
  19. caseKEYMODE_JA_HALF_NUMBER:
  20. /*Becausethesemodesbelongtodirectinputgroup,Notoggletableexists*/
  21. break;
  22. caseKEYMODE_JA_HALF_ALPHABET:
  23. cycleTable=JP_HALF_ALPHABET_CYCLE_TABLE;
  24. break;
  25. caseKEYMODE_JA_HALF_KATAKANA:
  26. cycleTable=JP_HALF_KATAKANA_CYCLE_TABLE;
  27. break;
  28. default:
  29. break;
  30. }
  31. returncycleTable;
  32. }

剩下的就不用解释了吧,看代码就懂了。

7.2 输入变换

大家看了上面的解释,估计会对源码中的后缀为REPLACE_TABLE的变量有了一定的猜测。这些变量是为了输入时变换使用的,大家有没有注意到假名输入和英文输入时,键盘上会有一个“大—小”的转换键。这个键的功能就是基于后缀为REPLACE_TABLE的变量实现的。

在使用是日文输入时,你输入“あ”,按“大—小”变换键后会变为“ぁ”。于是我们来看一下,平假名的REPLACE_TABLE,该变量名为JP_FULL_HIRAGANA_REPLACE_TABLE,该变量的内容为utf-8码,转为文字显示如下:

[java] view plain copy
  1. put("あ","ぁ")put("い","ぃ")put("う","ぅ")put("え","ぇ")put("お","ぉ")
  2. put("ぁ","あ")put("ぃ","い")put("ぅ","ヴ")put("ぇ","え")put("ぉ","お")
  3. put("か","が")put("き","ぎ")put("く","ぐ")put("け","げ")put("こ","ご")
  4. put("が","か")put("ぎ","き")put("ぐ","く")put("げ","け")put("ご","こ")
  5. put("さ","ざ")put("し","じ")put("す","ず")put("せ","ぜ")put("そ","ぞ")
  6. put("ざ","さ")put("じ","し")put("ず","す")put("ぜ","せ")put("ぞ","そ")
  7. put("た","だ")put("ち","ぢ")put("つ","っ")put("て","で")put("と","ど")
  8. put("だ","た")put("ぢ","ち")put("っ","づ")put("で","て")put("ど","と")
  9. put("づ","つ")put("ヴ","う")
  10. put("は","ば")put("ひ","び")put("ふ","ぶ")put("へ","べ")put("ほ","ぼ")
  11. put("ば","ぱ")put("び","ぴ")put("ぶ","ぷ")put("べ","ぺ")put("ぼ","ぽ")
  12. put("ぱ","は")put("ぴ","ひ")put("ぷ","ふ")put("ぺ","へ")put("ぽ","ほ")
  13. put("や","ゃ")put("ゆ","ゅ")put("よ","ょ")
  14. put("ゃ","や")put("ゅ","ゆ")put("ょ","よ")
  15. put("わ","ゎ")
  16. put("ゎ","わ")
  17. put("゛","゜")
  18. put("゜","゛")
看到这个表,大家现在知道是为什么了吧。

8、其他

到这里,我们就将Keyboard和KeyboardView这一部分介绍完成了。相信大家已经对openwnn中这一部分有了必要的了解。

1)具体的键盘定义,也就是如何从一个xml文件生存一个键盘 但是在解析源码中有两部分内容我们并没有说明

2)按键处理

3)DefaultSoftKeyboard.java,DefaultSoftKeyboardJAJP.java文件中关于事件部分代码并没有解释

对于1)后续会不上,对于2)和3)我感觉放在其他地方介绍会好一点,因此会在后续的文章中介绍2)和3)。至于1)后续有时间会补上。


更多相关文章

  1. android 屏幕切换
  2. android开发EditText输入时弹出数字输入键盘
  3. Android拨号键盘增加魔力爱心数字
  4. android_1 环境搭建
  5. android linphone中opengl显示的实现
  6. Android(安卓)EditText的setOnEditorActionListener方法
  7. Android屏幕、键盘背光Framework和Linux led_classdev
  8. android 实现自动输入文本效果
  9. 新建一个 Android(安卓)项目

随机推荐

  1. 2019年终总结
  2. Python数据结构:字典那些事儿
  3. 你真的了解参数估计和假设检验吗?
  4. Python数据结构:神奇的序列
  5. 遇到喷子,请先别急于反驳!
  6. 一份电商数据分析案例
  7. 怎样绘制漂亮的统计图表|不一样的折线图
  8. C#基础入门第十三天(多态接口)
  9. 【51CTO博客最全】2021阿里Java后端面试
  10. 一款优秀的 SDK 接口设计十大原则