Android4.3 Google Pinyin输入法UI定制
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定制相关推荐
- Ubuntu安装google拼音输入法
写在最前 好久没写文章了,随着近期时间的充裕,肯定会加快会博文更新的速度.言归正传,在安装英文Linux系统后(作为开发来说,本人更倾向于安装英文语言环境的Linux系统,这样各种提示,尤其是错误提示 ...
- ubuntu 13.10 64位安装及配置 google拼音输入法及Adobe flash player、mp3 插件安装、中文GBK编码等 -转
以前一直在虚拟机下玩ubuntu,今天兴起,在硬盘上直接安装了ubuntu 13.10版本,安装时将电脑的一个盘清空,有68G,用于安装ubuntu,将下载来的系统镜像使用ultraiso制作启动盘, ...
- ubuntu下安装google拼音输入法
ubuntu下安装google拼音输入法 step 1:设置root帐号密码 输入命令: sudo passwd root 根据提示输入root帐号密码. step 2:保证以root帐号权限进行后续 ...
- Google教我如何定制自己的View
前言 今天我看了Google教程中有关定制View的相关内容,这是之前从来没有接触过的领域,在github上能经常看到一些大神自定义的View,比如按钮,ListView,好像他们天生就可以随心所欲的 ...
- 把搜狗输入法词库导入Google拼音输入法
为PC端Google拼音输入法增加词库 为什么折腾词库 都在说百度.讯飞等输入法上传用户词库,为了安全建议大家使用google输入法之类,话说回来,要想使用智能联想功能是不是就得把你输入习惯放在他的里 ...
- 设计与算法 | Google Photos Web UI
作者 / Antin Harasymiv, UX Engineer, Google * 很多时候,体验设计和算法的联系会比想象中要紧密得多.本文将从代码和体验两个层面和大家深度分享. 几年前,我有幸成 ...
- 新的Google中文输入法 pk Microsoft中文输入法
今天才从网上发现Google的中文输入法,好像已经突出很久了,download下来试用了一下,果然不同反响.不愧是我所支持的Google,又为我们提供了一款贴心的产品.现在网络上有很多种输入法,但我以 ...
- xubuntu18.04安装Google拼音输入法
安装小企鹅(fcitx) sudo apt install fcitx 安装google拼音输入法 sudo apt install fcitx-googlepinyin 用vim编辑xprofile ...
- Kali Linux安装Google中文输入法(只需5步)
前言: 之前我在kali上安装过中文输入法,并且写了一篇博文(Kali Linux安装中文输入法全纪录),这篇博文里的步骤是当时一边摸索一边记录下的,思路有点混乱,而且还同时安装了两种输入法.今天又安 ...
最新文章
- centos 网卡聚合及Cisco交换机链路聚合
- 构成子网与构成超网的分析
- 七十四、完成Vue项目城市详细页,并实现打包
- org.apache.hadoop.hive.metastore.api.InvalidObjectException: Role public already exists.
- intellij运行spark的maven方式运行WordCount
- 五个常用的Linux监控脚本代码
- MFC制作打地鼠小游戏
- dhcp是哪一层的协议_随手记(3)常见的网络协议有哪些
- WordPress 数据库操作WPDB对象($wpdb)用法详解
- 中国城市名列表及code
- 计算机上机考试自我检查800字,【考试太差 自我反省检讨书800字】_考试成绩差自我反省检讨书范文3篇...
- 用于暴露感测的太阳传感器_凯利讯半导体
- Papervision3D材质
- openlayer4加载arcgis rest服务(遥感影像切片)
- RouterOS 重置密码
- 苹果手机自带软件删除了怎么恢复_苹果手机数据被删除如何来恢复数据???...
- 餐饮如何运用人工智能
- 蓝桥杯 兰顿蚂蚁【模拟】
- mysql如何获取今天的日期?
- chart.js报错“Canvas is already in use. Chart ...must be destroyed before the canvas can be reused ”
热门文章
- 解决导入maven项目之后pom.xml中的project标签报错:批量删除没有下载完全的pom依赖bat脚本
- odoo13-14电商插件
- 手工实现:SVM with Stochastic Gradient Descent
- 2018第七届CodeVita国际编程大赛
- 卸载linux 安装win10,如何卸载(或重新安装)Windows 10的Ubuntu Bash Shell | MOS86
- littlefs系列:重要的数据结构
- 用纯python脚本玩转UU加速器
- 服务器到底是个什么东东?跟电脑有啥区别?
- Surface Go使用体验——一文告诉你我为什么没有选择iPad
- python操作ppt