标签:
上一篇介绍了完成Android输入法的最小化步骤,它只能将按键对应的字符上屏。一般的东亚语言都有一个转换的过程,比如汉语输入拼音,需要由拼音转成汉字再上屏。本文将在前文基础上加入完成转换过程所必需的候选窗。本文代码可参见https://github.com/palanceli/AndroidXXIME/tree/v2。
如下图所示,用红框框出来的窗体是候选窗,其内的字符创叫做候选串,点击候选窗使之进入输入控件叫做上屏。没有输入的时候隐藏候选窗,当输入字串还未上屏时显示候选窗:
引入候选窗需要完成两个步骤:
一、创建CandidateView,该窗口需要覆盖如下两个方法,已完成自绘:
二、覆盖AndroidXXIME类的如下两个方法:
在该方法中创建CandidateView。
在该方法中响应按键消息,如:当按下字母键,则展现候选窗以及候选字串;当按下空格,则上屏候选字串,等等。
public CandidateView(Context context) { super(context); Log.d(this.getClass().toString(), "CandidateView: "); // 设置前景、背景色、字体、字号 Resources r = context.getResources(); setBackgroundColor(getResources().getColor(R.color.candidate_background, null)); mColorNormal = r.getColor(R.color.candidate_normal, null); mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding); mPaint = new Paint(); mPaint.setColor(mColorNormal); mPaint.setAntiAlias(true); mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height)); mPaint.setStrokeWidth(0); setWillNotDraw(false); // 覆盖了onDraw函数应清除该标记 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Log.d(this.getClass().toString(), "onMeasure: "); int wMode = MeasureSpec.getMode(widthMeasureSpec); int wSize = MeasureSpec.getSize(widthMeasureSpec); int measuredWidth = resolveSize(50, widthMeasureSpec); final int desiredHeight = ((int)mPaint.getTextSize()) + mVerticalPadding; // 系统会根据返回值确定窗体的大小 setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec)); } @Override protected void onDraw(Canvas canvas) { Log.d(this.getClass().toString(), "onDraw: "); super.onDraw(canvas); if (mSuggestions == null) return; // 依次绘制每组候选字串 int x = 0; final int count = mSuggestions.size(); final int height = getHeight(); final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent()); for (int i = 0; i < count; i++) { String suggestion = mSuggestions.get(i); float textWidth = mPaint.measureText(suggestion); final int wordWidth = (int) textWidth + X_GAP * 2; canvas.drawText(suggestion, x + X_GAP, y, mPaint); x += wordWidth; } } public void setSuggestions(List<String> suggestions) { // 设置候选字串列表 if (suggestions != null) { mSuggestions = new ArrayList<String>(suggestions); } invalidate(); requestLayout(); } }
该方法会在每次输入法被呼出的时候调用,如函数名所示,在这里创建候选窗口。
public class AndroidXXIME extends InputMethodService implements KeyboardView.OnKeyboardActionListener { …… @Override public View onCreateCandidatesView(){ Log.d(this.getClass().toString(), "onCreateCandidatesView: "); candidateView = new CandidateView(this); return candidateView; } …… }
public class AndroidXXIME extends InputMethodService implements KeyboardView.OnKeyboardActionListener { …… @Override public void onKey(int primaryCode, int[] keyCodes) { InputConnection ic = getCurrentInputConnection(); playClick(primaryCode); switch(primaryCode){ case Keyboard.KEYCODE_DELETE : // 如果收到的是DELETE键,则删除光标前的一个字符 ic.deleteSurroundingText(1, 0); break; case Keyboard.KEYCODE_DONE: // 如果收到的是DONE键,则执行回车 ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); break; default: char code = (char)primaryCode; if(code == ‘ ‘){ // 如果收到的是空格 if(m_composeString.length() > 0) { // 如果有写作串,则将首个候选提交上屏 ic.commitText(m_composeString, m_composeString.length()); m_composeString.setLength(0); }else{ // 如果没有写作串,则直接将空格上屏 ic.commitText(" ", 1); } }else { // 否则,将字符计入写作串 m_composeString.append(code); ic.setComposingText(m_composeString, 1); } updateCandidates(); } } }
在updateCandidates()函数中向CandidateView塞入候选字串列表,并触发该窗口更新。
当在系统“语言和输入法”-“更改键盘”中选择输入法时,
系统会调用该输入法InputMethodService的如下方法:
当一个输入控件获得焦点,呼出输入法,到它失去焦点,这期间成为一次会话。当一个会话开始时,系统会调用输入法InputMethodService的如下方法:
完成以上步骤之后,输入法就多出了候选窗口,下图中浅蓝色窗体既是:
在处理上还是很简陋,比如退格还不支持删除输入串,还不支持点击上屏,等等。这些属于业务逻辑的细节了,可以慢慢精耕细作。
标签:
原文地址:http://www.cnblogs.com/palance/p/5182386.html