Android4.3 Google Pinyin输入法UI定制

先来看原版输入法的效果如下:

定制后的效果如下:

SystemServer会启动InputMethodManagerService,在InputMethodManagerService中会启动action为"android.view.InputMethod"的Service,同时会设置组件名称来决定启动哪个输入法。

/frameworks/base/services/java/com/android/server/InputMethodManagerService.java
1194    InputBindResult startInputInnerLocked() {1195        if (mCurMethodId == null) {1196            return mNoBinding;
1197        }
1198
1199        if (!mSystemReady) {1200            // If the system is not yet ready, we shouldn't be running third
1201            // party code.
1202            return new InputBindResult(null, null, mCurMethodId, mCurSeq);
1203        }
1204
1205        InputMethodInfo info = mMethodMap.get(mCurMethodId);
1206        if (info == null) {1207            throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
1208        }
1209
1210        unbindCurrentMethodLocked(false, true);
1211        //InputMethod.SERVICE_INTERFACE ="android.view.InputMethod"
1212        mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
1213        mCurIntent.setComponent(info.getComponent());
1214        mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1215                com.android.internal.R.string.input_method_binding_label);
1216        mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
1217                mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
1218        if (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE
1219                | Context.BIND_NOT_VISIBLE | Context.BIND_SHOWING_UI)) {1220            mLastBindTime = SystemClock.uptimeMillis();
1221            mHaveConnection = true;
1222            mCurId = info.getId();
1223            mCurToken = new Binder();
1224            try {1225                if (true || DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);
1226                mIWindowManager.addWindowToken(mCurToken,
1227                        WindowManager.LayoutParams.TYPE_INPUT_METHOD);
1228            } catch (RemoteException e) {1229            }
1230            return new InputBindResult(null, null, mCurId, mCurSeq);
1231        } else {1232            mCurIntent = null;
1233            Slog.w(TAG, "Failure connecting to input method service: "
1234                    + mCurIntent);
1235        }
1236        return null;
1237    }

我们需要关注的是PinyinIME,在启动Service时如果进程没有创建,那么会首先创建进程,这里就是创建PinyinIME这个apk的进程,同时启动com.android.inputmethod.pinyin.PinyinIME这个service。在这个service的onCreate中调用了startPinyinDecoderService启动了AndroidManifest.xml中声明的另一个Sevcie采用显示启动的方式。

177    public void onCreate() {178        mEnvironment = Environment.getInstance();
179        if (mEnvironment.needDebug()) {180            Log.d(TAG, "onCreate.");
181        }
182        super.onCreate();
183
184        startPinyinDecoderService();
185        mImEn = new EnglishInputProcessor();
186        Settings.getInstance(PreferenceManager
187                .getDefaultSharedPreferences(getApplicationContext()));
188
189        mInputModeSwitcher = new InputModeSwitcher(this);
190        mChoiceNotifier = new ChoiceNotifier(this);
191        mGestureListenerSkb = new OnGestureListener(false);
192        mGestureListenerCandidates = new OnGestureListener(true);
193        mGestureDetectorSkb = new GestureDetector(this, mGestureListenerSkb);
194        mGestureDetectorCandidates = new GestureDetector(this,
195                mGestureListenerCandidates);
196
197        mEnvironment.onConfigurationChanged(getResources().getConfiguration(),
198                this);
199    }

在PinyinIME中这个包的layout中有三个layout分别包含以下区域:

floating_container中文区域上面显示你所按下按键的字符的区域(红框部分)

candidates_container- > 显示中文的区域(黄框部分)

skb_container-> 软键盘UI(蓝框部分)

开启apk的log后有以下发现:

1.点击editText的组件会调用到 onStartInputView去开启软键盘Android 4.3软键盘如下图所示:

Candidates window是上面中文字符(红框部分)

键盘定制:

1.各种键的大小,间隔,颜色,背景

键的大小和间隔这次是通过键的背景图通过预留一些透明边框来实现

键盘的背景是在/packages/inputmethods/PinyinIME/res/layout/skb_container.xml中的SkbContainer的背景定义的要切换键盘背景直接替换掉相应的图片即可

键盘的UI的xml对应的是在/packages/inputmethods/PinyinIME/res/xml/中定义的,如qwerty键盘对应的是:
/packages/inputmethods/PinyinIME/res/xml/skb_qwerty.xml

16<keyboard//skb_template1是一个模板其中定义了普通按键,功能按键,enter按键等等的样式可以直接引用
17  skb_template="@xml/skb_template1"
18  skb_cache_flag="true"
19  qwerty="true"
20  qwerty_uppercase="true"
21  width="10%p"
22  height="25%p"
23  key_type="0"
24  repeat="false"
25  balloon="true">
26  <!--第一行 qwerty 由于没有指定键的宽高则是使用keyboard的宽(10%)高(25%) 10个键*10% =100% 键盘的高度是有普通按键的高度*4决定的所以这边是25%也是由于qwerty的键盘只有4行-->
27  <row>
28    <keys splitter="|" labels="Q|W|E|R|T|Y|U|I|O|P"
29      codes="45|51|33|46|48|53|49|37|43|44"/>
30  </row>
31  <!--第二行指定键的宽度是10.205%p 从4.078%p开始,所以前面预留了一块空间并且由于4.078%p+10.205*9 小于100% 所以后面也空下来了一部分空间-->
32  <row start_pos_x="4.078%p" width="10.205%p">
33    <keys splitter="|" labels="A|S|D|F|G|H|J|K|L"
34      codes="29|47|32|34|35|36|38|39|40"/>
35  </row>
36 <!--第三行包含 ,Z X C V B N M 同上-->:
37  <row width="10.205%p"><!--下面这一行指定了 ,这个键的宽度是14.286%p 需要使用图片来作为字符 icon_popup 则是指定在按下相应的键盘后应该显示什么字符或者图片-->
38    <key label="," width="14.286%p" icon="@drawable/comma_full_icon"
39      icon_popup="@drawable/comma_full_popup_icon"><!--toggle_state 则是在不同输入模式下的不同效果 ,比如中文模式和英文模式的逗号的样式是不同的 keyType则是在模板中定义的键的类型:如keyType = 2 则对应skb_template1.xml中如下:意思是带小点的key
39  <key_type
40    id="2"
41    bg="@drawable/light_key_bg"
42    hlbg="@drawable/light_key_hl_bg"/>接着在下面 <key id="3"/>也是模板中定义的一种按键:下面这种定义的是删除键
97  <key id="3" start_pos_x="85.715%p" start_pos_y="50%p"
98      width="14.286%p" height="25%p" code="67" key_type="1"
99      repeat="true"/>-->
40      <toggle_state state_id="@string/toggle_smiley" code="-6"
41        icon="@drawable/smiley_icon" icon_popup="@drawable/smiley_popup_icon"
42        key_type="2"/>
43      <toggle_state state_id="@string/toggle_en_lower" code="-1"
44        icon="@drawable/shift_off_icon"
45        icon_popup="@drawable/shift_off_popup_icon" key_type="2"/>
46      <toggle_state state_id="@string/toggle_en_upper" code="-1"
47        icon="@drawable/shift_on_icon"
48        icon_popup="@drawable/shift_on_popup_icon" key_type="3"/>
49      <toggle_state state_id="@string/toggle_cn_cand" label="'"
50        key_type="1"/>
51    </key>
52    <keys splitter="|" labels="Z|X|C|V|B|N|M"
53      codes="54|52|31|50|30|42|41"/>
54    <key id="3"/>
55  </row>
56
57  <row width="20%p" row_id="@string/toggle_row_cn">
58    <key id="6"/>
59    <key id="4"/>
60    <key code="62" key_type="5" width="30.608%p"/>
61    <key label="。" width="14.696%p" icon="@drawable/period_full_icon"
62      icon_popup="@drawable/period_full_popup_icon"/>
63    <key id="1"/>
64  </row>
65
66  <row width="20%p" row_id="@string/toggle_row_en" start_pos_y="75%p">
67    <key id="6"/>
68    <key id="4"/>
69    <key code="62" key_type="5" width="30.608%p"/>
70    <key id="7"/>
71    <key id="1"/>
72  </row>
73
74  <row width="20%p" row_id="@string/toggle_row_uri" start_pos_y="75%p">
75    <key id="6"/>
76    <key id="4"/>
77    <key label="/" width="15.304%p"/>
78    <key code="62" key_type="5" width="15.304%p"/>
79    <key id="7"/>
80    <key id="1"/>
81  </row>
82
83  <row width="20%p" row_id="@string/toggle_row_emailaddress" start_pos_y="75%p">
84    <key id="6"/>
85    <key id="4"/>
86    <key label="\@" width="15.304%p"/>
87    <key code="62" key_type="5" width="15.304%p"/>
88    <key id="7"/>
89    <key id="1"/>
90  </row>
91</keyboard>

app通过XmlKeyboardLoader来解析xml并创建成softKey对象加入到softKeyboard中

/packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/XmlKeyboardLoader.java

 public SoftKeyboard loadKeyboard(int resourceId, int skbWidth, int skbHeight) {...508                    } else if (XMLTAG_ROW.compareTo(attr) == 0) {509                        if (!attrRow.getAttributes(attrSkb)) {510                            return null;
511                        }
512                        // Get the starting positions for the row.//解析row的tag 设置第一个键的坐标
513                        mKeyXPos = getFloat(xrp, XMLATTR_START_POS_X, 0);
514                        mKeyYPos = getFloat(xrp, XMLATTR_START_POS_Y, mKeyYPos);
515                        int rowId = getInteger(xrp, XMLATTR_ROW_ID,
516                                KeyRow.ALWAYS_SHOW_ROW_ID);
517                        softKeyboard.beginNewRow(rowId, mKeyYPos);
518                    } else if (XMLTAG_KEYS.compareTo(attr) == 0) {519                        if (null == softKeyboard) return null;
520                        if (!attrKeys.getAttributes(attrRow)) {521                            return null;
522                        }
523
524                        String splitter = xrp.getAttributeValue(null,
525                                XMLATTR_KEY_SPLITTER);
526                        splitter = Pattern.quote(splitter);
527                        String labels = xrp.getAttributeValue(null,
528                                XMLATTR_KEY_LABELS);
529                        String codes = xrp.getAttributeValue(null,
530                                XMLATTR_KEY_CODES);
531                        if (null == splitter || null == labels) {532                            return null;
533                        }
534                        String labelArr[] = labels.split(splitter);
535                        String codeArr[] = null;
536                        if (null != codes) {537                            codeArr = codes.split(splitter);
538                            if (labelArr.length != codeArr.length) {539                                return null;
540                            }
541                        }
542
543                        for (int i = 0; i < labelArr.length; i++) {544                            softKey = new SoftKey();
545                            int keyCode = 0;
546                            if (null != codeArr) {547                                keyCode = Integer.valueOf(codeArr[i]);
548                            }
549                            softKey.setKeyAttribute(keyCode, labelArr[i],
550                                    attrKeys.repeat, attrKeys.balloon);
551
552                            softKey.setKeyType(mSkbTemplate
553                                    .getKeyType(attrKeys.keyType), null, null);
554                            //定义key的上下左右  可以在这里设置普通键的大小和间隔 以比例的方式设置
555                            float left, right, top, bottom;
556                            left = mKeyXPos;
557
558                            right = left + attrKeys.keyWidth;
559                            top = mKeyYPos;
560                            bottom = top + attrKeys.keyHeight;
561
562                            if (right - left < 2 * mKeyXMargin) return null;
563                            if (bottom - top < 2 * mKeyYMargin) return null;
564
565                            softKey.setKeyDimensions(left, top, right, bottom);
566                            softKeyboard.addSoftKey(softKey);
567                            mKeyXPos = right;
568                            if ((int) mKeyXPos * mSkbWidth > mSkbWidth) {569                                return null;
570                            }
571                        }
572                    } else if (XMLTAG_KEY.compareTo(attr) == 0) {573                        if (null == softKeyboard) {574                            return null;
575                        }
576                        if (!attrKey.getAttributes(attrRow)) {577                            return null;
578                        }
579
580                        int keyId = this.getInteger(xrp, XMLATTR_ID, -1);//获取功能键
581                        if (keyId >= 0) {582                            softKey = mSkbTemplate.getDefaultKey(keyId);
583                        } else {584                            softKey = getSoftKey(xrp, attrKey);
585                        }
586                        if (null == softKey) return null;
587
588                        // Update the position for next key.
589                        mKeyXPos = softKey.mRightF;
590                        if ((int) mKeyXPos * mSkbWidth > mSkbWidth) {591                            return null;
592                        }
593                        // If the current xml event type becomes a starting tag,
594                        // it indicates that we have parsed too much to get
595                        // toggling states, and we started a new row. In this
596                        // case, the row starting position information should
597                        // be updated.
598                        if (mXmlEventType == XmlResourceParser.START_TAG) {599                            attr = xrp.getName();
600                            if (XMLTAG_ROW.compareTo(attr) == 0) {601                                mKeyYPos += attrRow.keyHeight;
602                                if ((int) mKeyYPos * mSkbHeight > mSkbHeight) {603                                    return null;
604                                }
605                            }
606                        }
607                        softKeyboard.addSoftKey(softKey);
608                    }}634    private SoftKey getSoftKey(XmlResourceParser xrp,
635            KeyCommonAttributes attrKey) throws XmlPullParserException,
636            IOException {637        int keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0);
638        String keyLabel = getString(xrp, XMLATTR_KEY_LABEL, null);
639        Drawable keyIcon = getDrawable(xrp, XMLATTR_KEY_ICON, null);
640        Drawable keyIconPopup = getDrawable(xrp, XMLATTR_KEY_ICON_POPUP, null);
641        int popupSkbId = xrp.getAttributeResourceValue(null,
642                XMLATTR_KEY_POPUP_SKBID, 0);
643
644        if (null == keyLabel && null == keyIcon) {645            keyIcon = mSkbTemplate.getDefaultKeyIcon(keyCode);
646            keyIconPopup = mSkbTemplate.getDefaultKeyIconPopup(keyCode);
647            if (null == keyIcon || null == keyIconPopup) return null;
648        }
649
650        // Dimension information must been initialized before
651        // getting toggle state, because mKeyYPos may be changed
652        // to next row when trying to get toggle state.//功能键的上下左右可以在这边设置
653        float left, right, top, bottom;
654        left = mKeyXPos;
655        right = left + attrKey.keyWidth;
656        top = mKeyYPos;
657        bottom = top + attrKey.keyHeight;
658
659        if (right - left < 2 * mKeyXMargin) return null;
660        if (bottom - top < 2 * mKeyYMargin) return null;
661
662        // Try to find if the next tag is
663        // {@link #XMLTAG_TOGGLE_STATE_OF_KEY}, if yes, try to
664        // create a toggle key.
665        boolean toggleKey = false;
666        mXmlEventType = xrp.next();
667        mNextEventFetched = true;
668
669        SoftKey softKey;
670        if (mXmlEventType == XmlResourceParser.START_TAG) {671            mAttrTmp = xrp.getName();
672            if (mAttrTmp.compareTo(XMLTAG_TOGGLE_STATE) == 0) {673                toggleKey = true;
674            }
675        }
676        if (toggleKey) {677            softKey = new SoftKeyToggle();
678            if (!((SoftKeyToggle) softKey).setToggleStates(getToggleStates(
679                    attrKey, (SoftKeyToggle) softKey, keyCode))) {680                return null;
681            }
682        } else {683            softKey = new SoftKey();
684        }
685
686        // Set the normal state
687        softKey.setKeyAttribute(keyCode, keyLabel, attrKey.repeat,
688                attrKey.balloon);
689        softKey.setPopupSkbId(popupSkbId);
690        softKey.setKeyType(mSkbTemplate.getKeyType(attrKey.keyType), keyIcon,
691                keyIconPopup);
692
693        softKey.setKeyDimensions(left, top, right, bottom);
694        return softKey;
695    }

2.键上显示的字符的大小位置

 /packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/Environment.java
65    /**
66     * The text size for normal keys. It is relative to the smaller one of
67     * screen width and height.普通按键的字符的大小比例
68     */
69    private static final float NORMAL_KEY_TEXT_SIZE_RATIO = 0.075f;
70
71    /**
72     * The text size for function keys. It is relative to the smaller one of
73     * screen width and height.功能按键的字符的大小比例
74     */
75    private static final float FUNCTION_KEY_TEXT_SIZE_RATIO = 0.055f;
76117    public void onConfigurationChanged(Configuration newConfig, Context context) {118        if (mConfig.orientation != newConfig.orientation) {119            WindowManager wm = (WindowManager) context
120                    .getSystemService(Context.WINDOW_SERVICE);
121            Display d = wm.getDefaultDisplay();
122            mScreenWidth = d.getWidth();
123            mScreenHeight = d.getHeight();
124
125            int scale;
126            if (mScreenHeight > mScreenWidth) {//竖屏模式下
127                mKeyHeight = (int) (mScreenHeight * KEY_HEIGHT_RATIO_PORTRAIT);
128                mCandidatesAreaHeight = (int) (mScreenHeight * CANDIDATES_AREA_HEIGHT_RATIO_PORTRAIT);
129                scale = mScreenWidth;
130            } else {//横屏模式下
131                mKeyHeight = (int) (mScreenHeight * KEY_HEIGHT_RATIO_LANDSCAPE);
132                mCandidatesAreaHeight = (int) (mScreenHeight * CANDIDATES_AREA_HEIGHT_RATIO_LANDSCAPE);
133                scale = mScreenHeight;
134            }
135            mNormalKeyTextSize = (int) (scale * NORMAL_KEY_TEXT_SIZE_RATIO);
136            mFunctionKeyTextSize = (int) (scale * FUNCTION_KEY_TEXT_SIZE_RATIO);
137            mNormalBalloonTextSize = (int) (scale * NORMAL_BALLOON_TEXT_SIZE_RATIO);
138            mFunctionBalloonTextSize = (int) (scale * FUNCTION_BALLOON_TEXT_SIZE_RATIO);
139            mKeyBalloonWidthPlus = (int) (scale * KEY_BALLOON_WIDTH_PLUS_RATIO);
140            mKeyBalloonHeightPlus = (int) (scale * KEY_BALLOON_HEIGHT_PLUS_RATIO);
141        }
142
143        mConfig.updateFrom(newConfig);
144    }
194    public int getKeyTextSize(boolean isFunctionKey) {195        if (isFunctionKey) {196            return mFunctionKeyTextSize;
197        } else {198            return mNormalKeyTextSize;
199        }
200    }

键盘的绘制时在SoftKeyboardView的onDraw中胡绘制的:

/packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/SoftKeyboardView.java
400    protected void onDraw(Canvas canvas) {401        if (null == mSoftKeyboard) return;
402
403        canvas.translate(mPaddingLeft, mPaddingTop);
404
405        Environment env = Environment.getInstance();//键盘中普通键盘中字符的大小和功能按键的大小都是通过Environment得到的
406        mNormalKeyTextSize = env.getKeyTextSize(false);
407        mFunctionKeyTextSize = env.getKeyTextSize(true);
408        // Draw the last soft keyboard
409        int rowNum = mSoftKeyboard.getRowNum();
410        int keyXMargin = mSoftKeyboard.getKeyXMargin();
411        int keyYMargin = mSoftKeyboard.getKeyYMargin();
412        for (int row = 0; row < rowNum; row++) {413            KeyRow keyRow = mSoftKeyboard.getKeyRowForDisplay(row);
414            if (null == keyRow) continue;
415            List<SoftKey> softKeys = keyRow.mSoftKeys;
416            int keyNum = softKeys.size();
417            for (int i = 0; i < keyNum; i++) {418                SoftKey softKey = softKeys.get(i);//根据键的类型来设置字符的大小
419                if (SoftKeyType.KEYTYPE_ID_NORMAL_KEY == softKey.mKeyType.mKeyTypeId) {420                    mPaint.setTextSize(mNormalKeyTextSize);
421                } else {422                    mPaint.setTextSize(mFunctionKeyTextSize);
423                }//绘制键
424                drawSoftKey(canvas, softKey, keyXMargin, keyYMargin);
425            }
426        }
427
428        if (mDimSkb) {429            mPaint.setColor(0xa0000000);
430            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
431        }
432
433        mDirtyRect.setEmpty();
434    }436    private void drawSoftKey(Canvas canvas, SoftKey softKey, int keyXMargin,
437            int keyYMargin) {438        Drawable bg;
439        int textColor;
440        if (mKeyPressed && softKey == mSoftKeyDown) {441            bg = softKey.getKeyHlBg();
442            textColor = softKey.getColorHl();
443        } else {444            bg = softKey.getKeyBg();
445            textColor = softKey.getColor();
446        }
447
448        if (null != bg) {449            bg.setBounds(softKey.mLeft + keyXMargin, softKey.mTop + keyYMargin,
450                    softKey.mRight - keyXMargin, softKey.mBottom - keyYMargin);
451            bg.draw(canvas);
452        }
453
454        String keyLabel = softKey.getKeyLabel();
455        Drawable keyIcon = softKey.getKeyIcon();
456        if (null != keyIcon) {457            Drawable icon = keyIcon;
458            int marginLeft = (softKey.width() - icon.getIntrinsicWidth()) / 2;
459            int marginRight = softKey.width() - icon.getIntrinsicWidth()
460                    - marginLeft;
461            int marginTop = (softKey.height() - icon.getIntrinsicHeight()) / 2;
462            int marginBottom = softKey.height() - icon.getIntrinsicHeight()
463                    - marginTop;//设置字符为图片的键的字符的宽高
464            icon.setBounds(softKey.mLeft + marginLeft,
465                    softKey.mTop + marginTop, softKey.mRight - marginRight,
466                    softKey.mBottom - marginBottom);
467            icon.draw(canvas);
468        } else if (null != keyLabel) {469            mPaint.setColor(textColor);
470            float x = softKey.mLeft
471                    + (softKey.width() - mPaint.measureText(keyLabel)) / 2.0f;
472            int fontHeight = mFmi.bottom - mFmi.top;
473            float marginY = (softKey.height() - fontHeight) / 2.0f;
474            float y = softKey.mTop + marginY - mFmi.top + mFmi.bottom / 1.5f;//设置普通字符的键的字符绘制的起始位置如果键盘上的字符靠键盘的上面可以将下面的y+1改成y+5
475            canvas.drawText(keyLabel, x, y + 1, mPaint);
476        }
477    }

3.禁用键盘的横屏全屏模式

这个功能可以直接重写如下方法即可:

 /packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/PinyinIME.java@Override
967    public boolean onEvaluateFullscreenMode() {976        return false;
977    }

4.修改CandidatesView的字体大小和颜色背景等

CandidatesView区域中总共分为三部分,左右箭头和中间显示中文的区域

中间的部分的绘制如下:

/packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/CandidateView.java
479    @Override
480    protected void onDraw(Canvas canvas) {481        super.onDraw(canvas);
482        // The invisible candidate view(the one which is not in foreground) can
483        // also be called to drawn, but its decoding result and candidate list
484        // may be empty.
485        if (null == mDecInfo || mDecInfo.isCandidatesListEmpty()) return;
486
487        // Calculate page. If the paging information is ready, the function will
488        // return at once.
489        calculatePage(mPageNo);
490
491        int pStart = mDecInfo.mPageStart.get(mPageNo);
492        int pSize = mDecInfo.mPageStart.get(mPageNo + 1) - pStart;
493        float candMargin = mCandidateMargin + mCandidateMarginExtra;
494        if (mActiveCandInPage > pSize - 1) {495            mActiveCandInPage = pSize - 1;
496        }
497
498        mCandRects.removeAllElements();
499
500        float xPos = mPaddingLeft;
501        int yPos = (getMeasuredHeight() -
502                (mFmiCandidates.bottom - mFmiCandidates.top)) / 2
503                - mFmiCandidates.top;//绘制每页之间的分隔符
504        xPos += drawVerticalSeparator(canvas, xPos);//循环获取每一页信息 一个字或者一个词为一页
505        for (int i = 0; i < pSize; i++) {506            float footnoteSize = 0;
507            String footnote = null;
508            if (mShowFootnote) {509                footnote = Integer.toString(i + 1);
510                footnoteSize = mFootnotePaint.measureText(footnote);
511                assert (footnoteSize < candMargin);
512            }
513            String cand = mDecInfo.mCandidatesList.get(pStart + i);
514            float candidateWidth = mCandidatesPaint.measureText(cand);
515            float centerOffset = 0;
516            if (candidateWidth < MIN_ITEM_WIDTH) {517                centerOffset = (MIN_ITEM_WIDTH - candidateWidth) / 2;
518                candidateWidth = MIN_ITEM_WIDTH;
519            }
520
521            float itemTotalWidth = candidateWidth + 2 * candMargin;
522            //绘制选中的页的背景
523            if (mActiveCandInPage == i && mEnableActiveHighlight) {524                mActiveCellRect.set(xPos, mPaddingTop + 1, xPos
525                        + itemTotalWidth, getHeight() - mPaddingBottom - 1);
526                mActiveCellDrawable.setBounds((int) mActiveCellRect.left,
527                        (int) mActiveCellRect.top, (int) mActiveCellRect.right,
528                        (int) mActiveCellRect.bottom);
529                mActiveCellDrawable.draw(canvas);
530            }
531
532            if (mCandRects.size() < pSize) mCandRects.add(new RectF());
533            mCandRects.elementAt(i).set(xPos - 1, yPos + mFmiCandidates.top,
534                    xPos + itemTotalWidth + 1, yPos + mFmiCandidates.bottom);
535             //绘制脚注,实际中footnote为null
536            // Draw footnote
537            if (mShowFootnote) {538                canvas.drawText(footnote, xPos + (candMargin - footnoteSize)
539                        / 2, yPos, mFootnotePaint);
540            }
541
542            // Left margin
543            xPos += candMargin;
544            if (candidateWidth > mContentWidth - xPos - centerOffset) {545                cand = getLimitedCandidateForDrawing(cand,
546                        mContentWidth - xPos - centerOffset);
547            }//设置选中的词的颜色和普通情况下的颜色
548            if (mActiveCandInPage == i && mEnableActiveHighlight) {549                mCandidatesPaint.setColor(mActiveCandidateColor);
550            } else {551                mCandidatesPaint.setColor(mNormalCandidateColor);
552            }//绘制中文区域的字符位置在xPos + centerOffset和 ypos
553            canvas.drawText(cand, xPos + centerOffset, yPos,
554                    mCandidatesPaint);
555
556            // Candidate and right margin
557            xPos += candidateWidth + candMargin;
558
559            // Draw the separator between candidates.//绘制分隔符
560            xPos += drawVerticalSeparator(canvas, xPos);
561        }
562
563        // Update the arrow status of the container.
564        if (null != mArrowUpdater && mUpdateArrowStatusWhenDraw) {565            mArrowUpdater.updateArrowStatus();
566            mUpdateArrowStatusWhenDraw = false;
567        }
568    }

从上面的绘制的代码可以知道如下元素:

中文区域的字体的颜色分为mActiveCandidateColor和mNormalCandidateColor一个代表选中的字体的颜色一个代表普通字体的颜色

           //选中的字符页的背景
238        mActiveCellDrawable = r.getDrawable(R.drawable.candidate_hl_bg);//字符页之间的分隔符
239        mSeparatorDrawable = r.getDrawable(R.drawable.candidates_vertical_line);//CandidatesView的左边距
240        mCandidateMargin = r.getDimension(R.dimen.candidate_margin_left_right);
241        //普通字符的颜色
242        mImeCandidateColor = r.getColor(R.color.candidate_color);//推荐字符颜色
243        mRecommendedCandidateColor = r.getColor(R.color.recommended_candidate_color);
244        mNormalCandidateColor = mImeCandidateColor;//选中字符的颜色
245        mActiveCandidateColor = r.getColor(R.color.active_candidate_color);

另外字符大小的设置代码如下:

363    private void onSizeChanged() {364        mContentWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
365        mContentHeight = (int) ((getMeasuredHeight() - mPaddingTop - mPaddingBottom) * 0.95f);
366        /**
367         * How to decide the font size if the height for display is given?
368         * Now it is implemented in a stupid way.
369         */
370        int textSize = 1;
371        mCandidatesPaint.setTextSize(textSize);
372        mFmiCandidates = mCandidatesPaint.getFontMetricsInt();//字体大小的递增计算的,基本填满了高度
373        while (mFmiCandidates.bottom - mFmiCandidates.top < mContentHeight) {374            textSize++;
375            mCandidatesPaint.setTextSize(textSize);
376            mFmiCandidates = mCandidatesPaint.getFontMetricsInt();
377        }
378        //apk使用的字体的大小 所以可以将下面这个textSize乘以一个系数即可缩小字体的大小
379        mImeCandidateTextSize = textSize;//推荐使用字体的大小
380        mRecommendedCandidateTextSize = textSize * 3 / 4;
381        if (null == mDecInfo) {382            mCandidateTextSize = mImeCandidateTextSize;
383            mCandidatesPaint.setTextSize(mCandidateTextSize);
384            mFmiCandidates = mCandidatesPaint.getFontMetricsInt();
385            mSuspensionPointsWidth =
386                mCandidatesPaint.measureText(SUSPENSION_POINTS);
387        } else {388            // Reset the decoding information to update members for painting.
389            setDecodingInfo(mDecInfo);
390        }
391
392        textSize = 1;
393        mFootnotePaint.setTextSize(textSize);
394        mFmiFootnote = mFootnotePaint.getFontMetricsInt();
395        while (mFmiFootnote.bottom - mFmiFootnote.top < mContentHeight / 2) {396            textSize++;
397            mFootnotePaint.setTextSize(textSize);
398            mFmiFootnote = mFootnotePaint.getFontMetricsInt();
399        }
400        textSize--;
401        mFootnotePaint.setTextSize(textSize);
402        mFmiFootnote = mFootnotePaint.getFontMetricsInt();
403
404        // When the size is changed, the first page will be displayed.
405        mPageNo = 0;
406        mActiveCandInPage = 0;
407    }

所以要调整中文区域的字体的大小在onSizeChange中改变mCandidateTextSize的值

5.添加退出输入法的按钮

由于输入法是点击back键收起的输入法,但是我们的平台没有back键,所以需要添加一个键来做收起的动作,我这边是将这个键添加到了上面这个candidates_container中,所以这样做必须要CandidatesWindow一直显示,当CandidateView不在时会遮挡内容看起来不美观,所以可以做这样一个处理,当CandidateView和左右箭头不在时将这个layout的背景设置成透明的即可如下:

/packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/CandidatesContainer.java
358    private void showArrow(ImageButton arrowBtn, boolean show) {359        if (show){360            arrowBtn.setVisibility(View.VISIBLE);
+             this.getBackground().setAlpha(255);} else {arrowBtn.setVisibility(View.INVISIBLE);
+             this.getBackground().setAlpha(0);}
362
363    }

要使CandidatesWindow一直显示可以做以下改动:

/packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/PinyinIME.java
1118    @Override
1119    public void onStartInputView(EditorInfo editorInfo, boolean restarting) {1120        if (mEnvironment.needDebug()) {1121            Log.d(TAG, "onStartInputView " + " contentType: "
1122                    + String.valueOf(editorInfo.inputType) + " Restarting:"
1123                    + String.valueOf(restarting));
1124        }
1125        updateIcon(mInputModeSwitcher.requestInputWithSkb(editorInfo));
1126        resetToIdleState(false);
1127        mSkbContainer.updateInputMode();//下面这个就是键盘开启时CandidatesView是否要开启,我们将之开启即可
1128        setCandidatesViewShown(true);
1129    }

另外就是实现收起键盘的功能,可以借用左右箭头的处理:可以直接调用PinyinIME的 requestHideSelf(0)即可将键盘收起但是CandidatesContainer没有持有PinyinIME的引用,所以还需要将PinyinIME service的引用在创建CandidatesContainer传给CandidatesContainer接着CandidatesContainer在收到按钮的点击事件后直接调用PinyinIME的 requestHideSelf(0)即可:

/packages/inputmethods/PinyinIME/src/com/android/inputmethod/pinyin/CandidatesContainer.java
365    public boolean onTouch(View v, MotionEvent event) {366        if (event.getAction() == MotionEvent.ACTION_DOWN) {367            if (v == mLeftArrowBtn) {368                mCvListener.onToRightGesture();
369            } else if (v == mRightArrowBtn) {370                mCvListener.onToLeftGesture();
371            }
372        } else if (event.getAction() == MotionEvent.ACTION_UP) {373            CandidateView cv = (CandidateView) mFlipper.getCurrentView();
374            cv.enableActiveHighlight(true);
+             if(v==mDownArrowBtn){+                      if(mService!=null){+                        mService.requestHideSelf(0);
+                    }
+               }
375        }
376
377        return false;
378    }

6.设置默认输入法

在初始化时会从setting中读取默认输入法,所以我们可以分成以下几步:

1.首先frameworks\base\packages\SettingsProvider\res\values\defaults.xml 增加下面语句
com.android.inputmethod.Pinyin/.PinyinIME

2.将其加入到数据库中可以用和其他默认值类似的方法:

在/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java中有一个函数loadGlobalSettings可以利用这个函数为default_input_method设置初始值:

在loadGlobalSettings加入下面这句函数:

loadStringSetting(stmt, Settings.Secure.ENABLED_INPUT_METHODS, R.string.def_input_method);

Android4.3 Google Pinyin输入法UI定制相关推荐

  1. Ubuntu安装google拼音输入法

    写在最前 好久没写文章了,随着近期时间的充裕,肯定会加快会博文更新的速度.言归正传,在安装英文Linux系统后(作为开发来说,本人更倾向于安装英文语言环境的Linux系统,这样各种提示,尤其是错误提示 ...

  2. ubuntu 13.10 64位安装及配置 google拼音输入法及Adobe flash player、mp3 插件安装、中文GBK编码等 -转

    以前一直在虚拟机下玩ubuntu,今天兴起,在硬盘上直接安装了ubuntu 13.10版本,安装时将电脑的一个盘清空,有68G,用于安装ubuntu,将下载来的系统镜像使用ultraiso制作启动盘, ...

  3. ubuntu下安装google拼音输入法

    ubuntu下安装google拼音输入法 step 1:设置root帐号密码 输入命令: sudo passwd root 根据提示输入root帐号密码. step 2:保证以root帐号权限进行后续 ...

  4. Google教我如何定制自己的View

    前言 今天我看了Google教程中有关定制View的相关内容,这是之前从来没有接触过的领域,在github上能经常看到一些大神自定义的View,比如按钮,ListView,好像他们天生就可以随心所欲的 ...

  5. 把搜狗输入法词库导入Google拼音输入法

    为PC端Google拼音输入法增加词库 为什么折腾词库 都在说百度.讯飞等输入法上传用户词库,为了安全建议大家使用google输入法之类,话说回来,要想使用智能联想功能是不是就得把你输入习惯放在他的里 ...

  6. 设计与算法 | Google Photos Web UI

    作者 / Antin Harasymiv, UX Engineer, Google * 很多时候,体验设计和算法的联系会比想象中要紧密得多.本文将从代码和体验两个层面和大家深度分享. 几年前,我有幸成 ...

  7. 新的Google中文输入法 pk Microsoft中文输入法

    今天才从网上发现Google的中文输入法,好像已经突出很久了,download下来试用了一下,果然不同反响.不愧是我所支持的Google,又为我们提供了一款贴心的产品.现在网络上有很多种输入法,但我以 ...

  8. xubuntu18.04安装Google拼音输入法

    安装小企鹅(fcitx) sudo apt install fcitx 安装google拼音输入法 sudo apt install fcitx-googlepinyin 用vim编辑xprofile ...

  9. Kali Linux安装Google中文输入法(只需5步)

    前言: 之前我在kali上安装过中文输入法,并且写了一篇博文(Kali Linux安装中文输入法全纪录),这篇博文里的步骤是当时一边摸索一边记录下的,思路有点混乱,而且还同时安装了两种输入法.今天又安 ...

最新文章

  1. centos 网卡聚合及Cisco交换机链路聚合
  2. 构成子网与构成超网的分析
  3. 七十四、完成Vue项目城市详细页,并实现打包
  4. org.apache.hadoop.hive.metastore.api.InvalidObjectException: Role public already exists.
  5. intellij运行spark的maven方式运行WordCount
  6. 五个常用的Linux监控脚本代码
  7. MFC制作打地鼠小游戏
  8. dhcp是哪一层的协议_随手记(3)常见的网络协议有哪些
  9. WordPress 数据库操作WPDB对象($wpdb)用法详解
  10. 中国城市名列表及code
  11. 计算机上机考试自我检查800字,【考试太差 自我反省检讨书800字】_考试成绩差自我反省检讨书范文3篇...
  12. 用于暴露感测的太阳传感器_凯利讯半导体
  13. Papervision3D材质
  14. openlayer4加载arcgis rest服务(遥感影像切片)
  15. RouterOS 重置密码
  16. 苹果手机自带软件删除了怎么恢复_苹果手机数据被删除如何来恢复数据???...
  17. 餐饮如何运用人工智能
  18. 蓝桥杯 兰顿蚂蚁【模拟】
  19. mysql如何获取今天的日期?
  20. chart.js报错“Canvas is already in use. Chart ...must be destroyed before the canvas can be reused ”

热门文章

  1. 解决导入maven项目之后pom.xml中的project标签报错:批量删除没有下载完全的pom依赖bat脚本
  2. odoo13-14电商插件
  3. 手工实现:SVM with Stochastic Gradient Descent
  4. 2018第七届CodeVita国际编程大赛
  5. 卸载linux 安装win10,如何卸载(或重新安装)Windows 10的Ubuntu Bash Shell | MOS86
  6. littlefs系列:重要的数据结构
  7. 用纯python脚本玩转UU加速器
  8. 服务器到底是个什么东东?跟电脑有啥区别?
  9. Surface Go使用体验——一文告诉你我为什么没有选择iPad
  10. python操作ppt