android输入法02:openwnn源码解析02—Keyboard和KeyboardView
本文主要介绍openwnn对Keyboard和KeyboardView的处理。
这一部分主要涉及三个类:InputViewManager.java,DefaultSoftKeyboard.java,DefaultSoftKeyboardJAJP.java。其中InputViewManager是与键盘相关的对外接口,DefaultSoftKeyboard是通用类,DefaultSoftKeyboardJAJP是日文定制类。
1、InputViewManager
第一步我们先来看看InputViewManager。这个接口类代码很简单:
[java] view plain copy
- /**
- *TheinterfaceofinputviewmanagerusedbyOpenWnn.
- *
- *@authorCopyright(C)2009OMRONSOFTWARECO.,LTD.AllRightsReserved.
- */
- publicinterfaceInputViewManager{
- /**
- *Initializetheinputview.
- *
- *@paramparentTheOpenWnnobject
- *@paramwidthThewidthofthedisplay
- *@paramheightTheheightofthedisplay
- *
- *@returnTheinputviewcreatedintheinitializeprocess;{@codenull}ifcannotcreateainputview.
- */
- publicViewinitView(OpenWnnparent,intwidth,intheight);
- /**
- *Gettheinputviewbeingusedcurrently.
- *
- *@returnTheinputview;{@codenull}ifnoinputviewisusedcurrently.
- */
- publicViewgetCurrentView();
- /**
- *Notificationofupdatingparent'sstate.
- *
- *@paramparentTheOpenWnnobjectusingthismanager
- */
- publicvoidonUpdateState(OpenWnnparent);
- /**
- *Reflectthepreferencesintheinputview.
- *
- *@paramprefThepreferences
- *@parameditorTheinformationabouttheeditor
- */
- publicvoidsetPreferences(SharedPreferencespref,EditorInfoeditor);
- /**
- *Closetheinputview.
- */
- publicvoidclosing();
- }
2、配置项
这里我们先从简单的部分开始研究。第一个是setPreferences,这只配置项。这一步的工作是读取与键盘部分有关的配置项,在生成键盘(改变键盘)时进行设置。代码中设计到的配置项很减少,只有:震动、声音、是否自动切换大写。在DefaultSoftKeyboard.java中只设置了震动和声音,在DefaultSoftKeyboardJAJP.java添加了是否自动切换为大写。具体大家可以看代码,对于是否自动切换大写,从代码上看,我猜测,有些输入框默认是输入大写的。(这一部分我想不到例子,谁有例子可以share一下)
3、KeyboardView
在InputViewManager中有initView这个函数,实际上使用来生成KeyboardView的。其源码如下:
在DefaultSoftKeyboard.java中:
[java] view plain copy
- /**@seejp.co.omronsoft.openwnn.InputViewManager#initView*/
- publicViewinitView(OpenWnnparent,intwidth,intheight){
- mWnn=parent;
- mDisplayMode=(width==320)?PORTRAIT:LANDSCAPE;
- /*
- *createkeyboards&theview.
- *Tore-displaytheinputviewwhenthedisplaymodeischangedportrait<->landscape,
- *createkeyboardseverytime.
- */
- createKeyboards(parent);
- SharedPreferencespref=PreferenceManager.getDefaultSharedPreferences(parent);
- Stringskin=pref.getString("keyboard_skin",
- mWnn.getResources().getString(R.string.keyboard_skin_id_default));
- Log.d("OpenWnn","keyboard_skin="+skin);
- intid=parent.getResources().getIdentifier(skin,"layout","jp.co.omronsoft.openwnn");
- mKeyboardView=(KeyboardView)mWnn.getLayoutInflater().inflate(id,null);
- mKeyboardView.setOnKeyboardActionListener(this);
- mCurrentKeyboard=null;
- mMainView=(ViewGroup)parent.getLayoutInflater().inflate(R.layout.keyboard_default_main,null);
- mSubView=(ViewGroup)parent.getLayoutInflater().inflate(R.layout.keyboard_default_sub,null);
- if(mDisplayMode==LANDSCAPE&&!mHardKeyboardHidden){
- mMainView.addView(mSubView);
- }
- if(mKeyboardView!=null){
- mMainView.addView(mKeyboardView);
- }
- returnmMainView;
- }
另外,从SoftKeyboard项目中我们知道,KeyboardView实际上是装着一个Keyboard。但在这一段代码中只有生成Keyboard,并未将Keyboard封装到KeyboardView里面,因为当前键盘变量mCurrentKeyboard是空的。这里我们猜测在继承类中会做封装Keyboard的操作。我们看DefaultSoftKeyboardJAJP.java的代码:
[java] view plain copy
- /**@seejp.co.omronsoft.openwnn.DefaultSoftKeyboard#initView*/
- @OverridepublicViewinitView(OpenWnnparent,intwidth,intheight){
- Viewview=super.initView(parent,width,height);
- changeKeyboard(mKeyboard[mCurrentLanguage][mDisplayMode][mCurrentKeyboardType][mShiftOn][mCurrentKeyMode][0]);
- returnview;
- }
[java] view plain copy
- /**
- *Changethekeyboard.
- *
- *@paramkeyboardThenewkeyboard
- *@return{@codetrue}ifthekeyboardischanged;{@codefalse}ifnotchanged.
- */
- protectedbooleanchangeKeyboard(Keyboardkeyboard){
- if(keyboard==null){
- returnfalse;
- }
- if(mCurrentKeyboard!=keyboard){
- mKeyboardView.setKeyboard(keyboard);
- mKeyboardView.setShifted((mShiftOn==0)?false:true);
- mCurrentKeyboard=keyboard;
- returntrue;
- }else{
- mKeyboardView.setShifted((mShiftOn==0)?false:true);
- returnfalse;
- }
- }
4、Keyboard
这里的Keyboard异常复杂,以至于需要一个5维数组来维护。
[java] view plain copy
- /**
- *Keyboardsurfaces
- *<br>
- *Keyboard[language][portrait/landscape][keyboardtype][shiftoff/on][key-mode]
- */
- protectedKeyboard[][][][][][]mKeyboard;
键盘的处理还是比复杂,因为根据不同的输入框,需要显示不同的键盘;同时用户可以选择不同的输入法模式,此时又需要显示不同的键盘。这一点从onUpdateState 函数,以及onKey函数等可以看出。
另外,有些手机不是纯触摸屏的,也就是带有键盘的手机。对于这些手机是不会显示软键盘的(我猜测),对此需要对硬件盘信息进行设置。其中setHardKeyboardHidden函数就是用于做这些事情的。
5、onUpdateState
从函数名我们可以看出这一函数的目的是用于更新状态,主要是用户更新键盘状态(或者是切换键盘)。从DefaultSoftKeyboard类中代码可以看出:
[java] view plain copy
- /**@seejp.co.omronsoft.openwnn.InputViewManager#onUpdateState*/
- publicvoidonUpdateState(OpenWnnparent){
- try{
- if(parent.mComposingText.size(1)==0){
- if(!mNoInput){
- /*whenthemodechangedto"noinput"*/
- mNoInput=true;
- KeyboardnewKeyboard=getKeyboardInputed(false);
- if(mCurrentKeyboard!=newKeyboard){
- changeKeyboard(newKeyboard);
- }
- }
- }else{
- if(mNoInput){
- /*whenthemodechangedto"inputsomecharacters"*/
- mNoInput=false;
- KeyboardnewKeyboard=getKeyboardInputed(true);
- if(mCurrentKeyboard!=newKeyboard){
- changeKeyboard(newKeyboard);
- }
- }
- }
- }catch(Exceptionex){
- }
- }
[java] view plain copy
- /**
- *Statusofthecomposingtext
- *<br>
- *{@codetrue}ifthereisnocomposingtext.
- */
- protectedbooleanmNoInput=true;
这一段代码主要是根据当前的输入状态,来更新键盘的。程序判断当前输入串(这里指输入时,带有下划线的输入串)是否为空。若输入串为空,判断mNoInput是否为有输入串(false),若是,则mNoInput改为true,同时键盘改为没有输入串的状态;若输入串不为空,此时若未没有输入串的状态,则要改为有输入串的状态,并相应修改键盘。
而DefaultSoftKeyboardJAJP中的这段代码则较为简单:
[java] view plain copy
- /**@seejp.co.omronsoft.openwnn.DefaultSoftKeyboard#onUpdateState*/
- @OverridepublicvoidonUpdateState(OpenWnnparent){
- super.onUpdateState(parent);
- setShiftByEditorInfo();
- }
- /**
- *Settheshiftkeystatefrom{@linkEditorInfo}.
- */
- privatevoidsetShiftByEditorInfo(){
- if(mEnableAutoCaps&&(mCurrentKeyMode==KEYMODE_JA_HALF_ALPHABET)){
- intshift=getShiftKeyState(mWnn.getCurrentInputEditorInfo());
- mShiftOn=shift;
- changeKeyboard(getShiftChangeKeyboard(shift));
- }
- }
6、输入方式(直接上屏或者参与变换)
另外,在使用输入法输入时,通常会有两种方式,一种是参与变换,一种是直接上屏。参与变换是指,你输入的内容与你需要选择的内容不是一致的,而是通过一系列复杂的变换得到的,比如你输入”kawai“得到”可愛“,就是通过变换而来的;而你在输入字符或者数字时,通常是直接上屏的。前者需要显示CandidateView的,而后者不要。
这一点可能影响的地方包括:不同的输入框(如密码输入框)和不同的输入模式(或者说是输入内容),比如输入数字和字符时,通常就是直接上屏的。
对于这一部分的技术处理,我们从DefaultSoftKeyboardJAJP类中的changeKeyMode函数可以看出一点端倪。由于该函数代码有点长,就不展现出来了。我们看其中几行代码:
[java] view plain copy
- caseKEYMODE_JA_HALF_ALPHABET:
- if(USE_ENGLISH_PREDICT){
- mInputType=INPUT_TYPE_TOGGLE;
- mode=OpenWnnEvent.Mode.NO_LV1_CONV;
- }else{
- mInputType=INPUT_TYPE_TOGGLE;
- mode=OpenWnnEvent.Mode.DIRECT;
- }
- break;
另外changeKeyMode函数的前两行是
[java] view plain copy
- inttargetMode=keyMode;
- commitText();
另外,需要提一下,在openwnn日文输入法中,有一个功能是切换输入模式,如下图
左下角那个按钮,你按一下他会不断变换,从假名输入、英文输入、数字输入循环切换。
其实现方式如下:
[java] view plain copy
- /**Inputmodetogglecycletable*/
- privatestaticfinalint[]JP_MODE_CYCLE_TABLE={
- KEYMODE_JA_FULL_HIRAGANA,KEYMODE_JA_HALF_ALPHABET,KEYMODE_JA_HALF_NUMBER
- };
- /**
- *Changetothenextinputmode
- */
- privatevoidnextKeyMode(){
- /*Searchthecurrentmodeinthetoggletable*/
- booleanfound=false;
- intindex;
- for(index=0;index<JP_MODE_CYCLE_TABLE.length;index++){
- if(JP_MODE_CYCLE_TABLE[index]==mCurrentKeyMode){
- found=true;
- break;
- }
- }
- if(!found){
- /*Ifthecurrentmodenotexists,setthedefaultmode*/
- setDefaultKeyboard();
- }else{
- /*Ifthecurrentmodeexists,setthenextinputmode*/
- index++;
- if(JP_MODE_CYCLE_TABLE.length<=index){
- index=0;
- }
- changeKeyMode(JP_MODE_CYCLE_TABLE[index]);
- }
- }
[java] view plain copy
- /**@seejp.co.omronsoft.openwnn.DefaultSoftKeyboard#onKey*/
- @OverridepublicvoidonKey(intprimaryCode,int[]keyCodes){
- switch(primaryCode){
- caseKEYCODE_JP12_TOGGLE_MODE:
- caseKEYCODE_QWERTY_TOGGLE_MODE:
- nextKeyMode();
- break;
7、输入变换
7.1 12key键盘
在12key的键盘中,是无法显示所有输入内容的。于是你按一个键,可能包含多个信息。比如诺基亚的12键键盘:
在这种键盘中,你要输入c,则要按三下”2“键才可以。
同样的,在日文输入法中,如下键盘(软键盘),你是无法显示所有输入内容的,因此,你可能也像用诺基亚键盘一样,需要按多次才可以输入一个内容。
比如,在如上键盘中,你按”か“键,则按1下、2下、3下、4下,5下,6下……显示的内容分别是:"か","き", "く", "け", "こ","か",……。(注意需要在假名输入模式下)
这里的程序实现是比较巧妙,其中涉及的代码如下:
[java] view plain copy
- /**Togglecycletableforfull-widthHIRAGANA*/
- privatestaticfinalString[][]JP_FULL_HIRAGANA_CYCLE_TABLE={
- {"\u3042","\u3044","\u3046","\u3048","\u304a","\u3041","\u3043","\u3045","\u3047","\u3049"},
- {"\u304b","\u304d","\u304f","\u3051","\u3053"},
- {"\u3055","\u3057","\u3059","\u305b","\u305d"},
- {"\u305f","\u3061","\u3064","\u3066","\u3068","\u3063"},
- {"\u306a","\u306b","\u306c","\u306d","\u306e"},
- {"\u306f","\u3072","\u3075","\u3078","\u307b"},
- {"\u307e","\u307f","\u3080","\u3081","\u3082"},
- {"\u3084","\u3086","\u3088","\u3083","\u3085","\u3087"},
- {"\u3089","\u308a","\u308b","\u308c","\u308d"},
- {"\u308f","\u3092","\u3093","\u308e","\u30fc"},
- {"\u3001","\u3002","\uff1f","\uff01","\u30fb","\u3000"},
- };
[java] view plain copy
- {"あ","い","う","え","お","ぁ","ぃ","ぅ","ぇ","ぉ"},
- {"か","き","く","け","こ"},
- {"さ","し","す","せ","そ"},
- {"た","ち","つ","て","と","っ"},
- {"な","に","ぬ","ね","の"},
- {"は","ひ","ふ","へ","ほ"},
- {"ま","み","む","め","も"},
- {"や","ゆ","よ","ゃ","ゅ","ょ"},
- {"ら","り","る","れ","ろ"},
- {"わ","を","ん","ゎ","ー"},
- {"、","。","?","!","・"," "},
在@Override public void onKey(int primaryCode, int[] keyCodes)函数中:
[java] view plain copy
- caseKEYCODE_JP12_SHARP:
- /*Processingtoinputbytenkey*/
- if(mInputType==INPUT_TYPE_INSTANT){
- /*Sendainputcharacterdirectlyifinstantinputtypeisselected*/
- commitText();
- mWnn.onEvent(newOpenWnnEvent(OpenWnnEvent.INPUT_CHAR,
- mCurrentInstantTable[getTableIndex(primaryCode)]));
- }else{
- if((mPrevInputKeyCode!=primaryCode)){
- if((mCurrentKeyMode==KEYMODE_JA_HALF_ALPHABET)
- &&(primaryCode==KEYCODE_JP12_SHARP)){
- /*Committextbysymbolcharacter(',''.')whenalphabetinputmodeisselected*/
- commitText();
- }
- }
- /*Convertthekeycodetothetableindexandsendthetoggleeventwiththetableindex*/
- String[][]cycleTable=getCycleTable();
- if(cycleTable==null){
- Log.e("OpenWnn","notfoundscycletable");
- }else{
- intindex=getTableIndex(primaryCode);
- mWnn.onEvent(newOpenWnnEvent(OpenWnnEvent.TOGGLE_CHAR,cycleTable[index]));
- mCurrentCycleTable=cycleTable[index];
- }
- mPrevInputKeyCode=primaryCode;
- }
- break;
[java] view plain copy
- /**
- *Getthetoggletableforinputthatisappropriateincurrentmode.
- *
- *@returnThetoggletableforinput
- */
- privateString[][]getCycleTable(){
- String[][]cycleTable=null;
- switch(mCurrentKeyMode){
- caseKEYMODE_JA_FULL_HIRAGANA:
- cycleTable=JP_FULL_HIRAGANA_CYCLE_TABLE;
- break;
- caseKEYMODE_JA_FULL_KATAKANA:
- cycleTable=JP_FULL_KATAKANA_CYCLE_TABLE;
- break;
- caseKEYMODE_JA_FULL_ALPHABET:
- cycleTable=JP_FULL_ALPHABET_CYCLE_TABLE;
- break;
- caseKEYMODE_JA_FULL_NUMBER:
- caseKEYMODE_JA_HALF_NUMBER:
- /*Becausethesemodesbelongtodirectinputgroup,Notoggletableexists*/
- break;
- caseKEYMODE_JA_HALF_ALPHABET:
- cycleTable=JP_HALF_ALPHABET_CYCLE_TABLE;
- break;
- caseKEYMODE_JA_HALF_KATAKANA:
- cycleTable=JP_HALF_KATAKANA_CYCLE_TABLE;
- break;
- default:
- break;
- }
- returncycleTable;
- }
剩下的就不用解释了吧,看代码就懂了。
7.2 输入变换
大家看了上面的解释,估计会对源码中的后缀为REPLACE_TABLE的变量有了一定的猜测。这些变量是为了输入时变换使用的,大家有没有注意到假名输入和英文输入时,键盘上会有一个“大—小”的转换键。这个键的功能就是基于后缀为REPLACE_TABLE的变量实现的。
在使用是日文输入时,你输入“あ”,按“大—小”变换键后会变为“ぁ”。于是我们来看一下,平假名的REPLACE_TABLE,该变量名为JP_FULL_HIRAGANA_REPLACE_TABLE,该变量的内容为utf-8码,转为文字显示如下:
[java] view plain copy
- put("あ","ぁ")put("い","ぃ")put("う","ぅ")put("え","ぇ")put("お","ぉ")
- put("ぁ","あ")put("ぃ","い")put("ぅ","ヴ")put("ぇ","え")put("ぉ","お")
- put("か","が")put("き","ぎ")put("く","ぐ")put("け","げ")put("こ","ご")
- put("が","か")put("ぎ","き")put("ぐ","く")put("げ","け")put("ご","こ")
- put("さ","ざ")put("し","じ")put("す","ず")put("せ","ぜ")put("そ","ぞ")
- put("ざ","さ")put("じ","し")put("ず","す")put("ぜ","せ")put("ぞ","そ")
- put("た","だ")put("ち","ぢ")put("つ","っ")put("て","で")put("と","ど")
- put("だ","た")put("ぢ","ち")put("っ","づ")put("で","て")put("ど","と")
- put("づ","つ")put("ヴ","う")
- put("は","ば")put("ひ","び")put("ふ","ぶ")put("へ","べ")put("ほ","ぼ")
- put("ば","ぱ")put("び","ぴ")put("ぶ","ぷ")put("べ","ぺ")put("ぼ","ぽ")
- put("ぱ","は")put("ぴ","ひ")put("ぷ","ふ")put("ぺ","へ")put("ぽ","ほ")
- put("や","ゃ")put("ゆ","ゅ")put("よ","ょ")
- put("ゃ","や")put("ゅ","ゆ")put("ょ","よ")
- put("わ","ゎ")
- put("ゎ","わ")
- put("゛","゜")
- put("゜","゛")
8、其他
到这里,我们就将Keyboard和KeyboardView这一部分介绍完成了。相信大家已经对openwnn中这一部分有了必要的了解。
1)具体的键盘定义,也就是如何从一个xml文件生存一个键盘 但是在解析源码中有两部分内容我们并没有说明
2)按键处理
3)DefaultSoftKeyboard.java,DefaultSoftKeyboardJAJP.java文件中关于事件部分代码并没有解释
对于1)后续会不上,对于2)和3)我感觉放在其他地方介绍会好一点,因此会在后续的文章中介绍2)和3)。至于1)后续有时间会补上。
更多相关文章
- android 屏幕切换
- android开发EditText输入时弹出数字输入键盘
- Android拨号键盘增加魔力爱心数字
- android_1 环境搭建
- android linphone中opengl显示的实现
- Android(安卓)EditText的setOnEditorActionListener方法
- Android屏幕、键盘背光Framework和Linux led_classdev
- android 实现自动输入文本效果
- 新建一个 Android(安卓)项目