zoukankan      html  css  js  c++  java
  • 写一个Android输入法02——候选窗、转换

    上一篇介绍了完成Android输入法的最小化步骤,它只能将按键对应的字符上屏。一般的东亚语言都有一个转换的过程,比如汉语输入拼音,需要由拼音转成汉字再上屏。本文将在前文基础上加入完成转换过程所必需的候选窗。本文代码可参见https://github.com/palanceli/AndroidXXIME/tree/v2。

    如下图所示,用红框框出来的窗体是候选窗,其内的字符创叫做候选串,点击候选窗使之进入输入控件叫做上屏。没有输入的时候隐藏候选窗,当输入字串还未上屏时显示候选窗:

    引入候选窗需要完成两个步骤:

    一、创建CandidateView,该窗口需要覆盖如下两个方法,已完成自绘:

    • onDraw(Canvas canvas);
    • onMeasure(int widthMeasureSpec, int heightMeasureSpec);

    二、覆盖AndroidXXIME类的如下两个方法:

    • onCreateCandidateView();

      在该方法中创建CandidateView。

    • onKey(int  primaryCode, int [] keyCodes);

      在该方法中响应按键消息,如:当按下字母键,则展现候选窗以及候选字串;当按下空格,则上屏候选字串,等等。    


    创建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();
        }
    
    }

    覆盖onCreateCandidateView()方法

    该方法会在每次输入法被呼出的时候调用,如函数名所示,在这里创建候选窗口。

    public class AndroidXXIME extends InputMethodService
            implements KeyboardView.OnKeyboardActionListener {
    ……
    
        @Override public View onCreateCandidatesView(){
            Log.d(this.getClass().toString(), "onCreateCandidatesView: ");
            candidateView = new CandidateView(this);
            return candidateView;
        }
    ……
    }

    覆盖onKey(int primaryCode, int [] keyCodes)方法

    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的如下方法:

    • onCreate()
    • onInitializeInterface()  可以在该方法中完成与输入法相关的初始化操作,比如加载词库。
    • onStartInput()    每次切换输入焦点的时候,都会调用该方法,在这里可以完成和会话相关的初始化操作,后面还会介绍。

    当一个输入控件获得焦点,呼出输入法,到它失去焦点,这期间成为一次会话。当一个会话开始时,系统会调用输入法InputMethodService的如下方法:

    • onStartInput()    负责会话相关的初始化工作。输入法要负责在会话切换时,清除上次会话的中间数据,以防止前一个会话的中间数据窜入下个会话。这和Windows平台下的输入法有很大区别,在Windows下,输入法一个DLL,它依附在切出输入法的进程中,因此,每个输入法进程保存各自的输入法上下文,如果需要在进程间共享数据(比如词库),则需要采用共享内存机制;而在Android平台下,输入法是一个独立的进程,所有的数据仅在该进程中保存一份,此时则需要考虑如何隔离不同进程间的私有数据,比如前一个进程输入一半但未上屏的数据,切到另一个进程或输入控件后,就应该清除掉。该回调函数用来做这类工作。
    • onCreateInputView()    创建输入法键盘布局。
    • onCreateCandidatesView()   创建候选窗。

    完成以上步骤之后,输入法就多出了候选窗口,下图中浅蓝色窗体既是:

    在处理上还是很简陋,比如退格还不支持删除输入串,还不支持点击上屏,等等。这些属于业务逻辑的细节了,可以慢慢精耕细作。

  • 相关阅读:
    Scrum Meeting Alpha
    Scrum Meeting Alpha
    Scrum Meeting Alpha
    你连自律都做不到,还奢谈什么自由?
    改变这个世界
    这世界没有人能随随便便成功
    “沙堆实验”
    解读那些年我们见过的“富人思维”
    心存希望,面朝大海
    闻香识女人 演讲台词
  • 原文地址:https://www.cnblogs.com/palance/p/5182386.html
Copyright © 2011-2022 走看看