android 輸入法,裡面還整合語音輸入
阿新 • • 發佈:2019-01-25
<?xml version="1.0" encoding="utf-8"?> <com.example.android.softkeyboard.LatinKeyboardView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/keyboard" android:layout_alignParentBottom="true" android:layout_width="fill_parent" android:layout_height="wrap_content" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.softkeyboard" xmlns:tools="http://schemas.android.com/tools" tools:ignore="UsesMinSdkAttributes,UsesMinSdkAttributes,MissingVersion"> <uses-permission android:name="android.permission.INTERNET"/> <application android:label="@string/ime_name" tools:ignore="MissingApplicationIcon"> <service android:name="MySoftKeyboard" android:permission="android.permission.BIND_INPUT_METHOD"> <intent-filter> <action android:name="android.view.InputMethod" /> </intent-filter> <meta-data android:name="android.view.im" android:resource="@xml/method" /> </service> </application> </manifest>
package com.example.android.softkeyboard; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; import java.util.List; @SuppressLint("WrongCall") public class CandidateView extends View { private static final int OUT_OF_BOUNDS = -1; //這個是這個candidateView的宿主類,也就是該view是為什麼輸入法服務的。 private MySoftKeyboard mService; //這個是建議。比如說當我們輸入一些字母之後輸入法希望根據輸入來進行聯想建議。 private List<String> mSuggestions; //這個是使用者選擇的詞的索引 private int mSelectedIndex; private int mTouchX = OUT_OF_BOUNDS; //這個是用來描繪選擇區域高亮的一個類 private Drawable mSelectionHighlight; //鍵入的word是否合法正確。 private boolean mTypedWordValid; //背景填充區域,決定將要在那個部分顯示? private Rect mBgPadding; private static final int MAX_SUGGESTIONS = 32; private static final int SCROLL_PIXELS = 20; //這個是對於候選詞的每個詞的寬度 private int[] mWordWidth = new int[MAX_SUGGESTIONS]; //這個是每個候選詞的X座標。 private int[] mWordX = new int[MAX_SUGGESTIONS]; //難道是兩個詞語之間的間隙?對了! private static final int X_GAP = 10; private static final List<String> EMPTY_LIST = new ArrayList<String>(); private int mColorNormal; private int mColorRecommended; private int mColorOther; private int mVerticalPadding; //所有關於繪製的資訊,比如線條的顏色等 private Paint mPaint; private boolean mScrolled; private int mTargetScrollX; private int mTotalWidth; private GestureDetector mGestureDetector; /** * Construct a CandidateView for showing suggested words for completion. * @param context * @param attrs */ public CandidateView(Context context) { //activity,inputmethodservice,這都是context的派生類 super(context); //getResouces這個函式用來得到這個應用程式的所有資源,就連android自帶的資源也要如此 mSelectionHighlight = context.getResources().getDrawable( android.R.drawable.list_selector_background); //mSelectionHighlight型別是Drawable,而Drawable設定狀態就是這樣 mSelectionHighlight.setState(new int[] { android.R.attr.state_enabled,//這行如果去掉,點選候選詞的時候是灰色,但是也可以用 android.R.attr.state_focused,//用處不明。。。。 android.R.attr.state_window_focused,//這行如果去掉,當點選候選詞的時候背景不會變成橙色 android.R.attr.state_pressed//點選候選詞語時候背景顏色深淺的變化,不知深層意義是什麼? }); Resources r = context.getResources(); setBackgroundColor(r.getColor(R.color.candidate_background)); //設定高亮區域的背景顏色,還是透明的,很美,很美,但為什麼是透明的還有待考證? //這個顏色,是非首選詞的顏色 mColorNormal = r.getColor(R.color.candidate_normal); //找到了,這個是顯示字型的顏色 mColorRecommended = r.getColor(R.color.candidate_recommended); //這個是候選詞語分割線的顏色 mColorOther = r.getColor(R.color.candidate_other); //這是系統定義的一個整型變數。用就可以了 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); //用手可以滑動,這是在建構函式裡面對滑動監聽的過載,猜測,這個函式與onTouchEvent函式應該是同時起作用? mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mScrolled = true; //得到滑動開始的橫座標 int sx = getScrollX(); //加上滑動的距離,這個滑動距離是最後一次call滑動之間的距離,很小,應該 sx += distanceX; if (sx < 0) { sx = 0; } if (sx + getWidth() > mTotalWidth) { sx -= distanceX; } //記錄將要移動到的位置,後面會用到 mTargetScrollX = sx; //這是處理滑動的函式,view類的函式。後面一個引數,說明Y軸永遠不變,如果你嘗試去改變一下,經測試,太好玩了 scrollTo(sx, getScrollY()); //文件中說的是使得整個VIew作廢,但是如果不用這句,會發生什麼? invalidate(); return true; } }); //這後三行語句不是在GestureDetector函式中的,而是在建構函式中的,當候選View建立成功的時候就已經是下面的狀態了 //拖動時刻左右兩邊的淡出效果 setHorizontalFadingEdgeEnabled(true); //當拖動的時候,依舊可以輸入並顯示 setWillNotDraw(false); //作用暫時不明? setHorizontalScrollBarEnabled(false); setVerticalScrollBarEnabled(false); } /** * A connection back to the service to communicate with the text field * @param listener */ public void setService(MySoftKeyboard listener) { //自己定義的廢柴函式,使得私有變數mService的值得以改變 mService = listener; } @Override public int computeHorizontalScrollRange() { return mTotalWidth; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //猜測:如果第二引數是從onMeasure的引數中來的,就用第二變數 //這個函式是個調和寬度函式,一般情況下用引數1值,除非第二個引數給其限制 int measuredWidth = resolveSize(50, widthMeasureSpec); // Get the desired height of the icon menu view (last row of items does // not have a divider below) //得到所需的圖示選單檢視的高度(最後一行的物品沒有下面的分頻器) Rect padding = new Rect(); //吳:高亮區域除了字以外,剩下的空隙,用getPadding得到,或許,這是由list_selector_background決定的。 mSelectionHighlight.getPadding(padding); final int desiredHeight = ((int)mPaint.getTextSize()) + mVerticalPadding + padding.top + padding.bottom; // Maximum possible width and desired height //最大可能的寬度和期望的高度 setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec)); } /** * If the canvas is null, then only touch calculations are performed to pick the target * candidate. * 如果畫布是null,則只接觸計算執行目標候選人。 */ @Override protected void onDraw(Canvas canvas) { //這是每個View物件繪製自己的函式,過載之。經測試:沒有這個函式的過載,則顯示不出字來,這個就是用來顯示字條 if (canvas != null) { super.onDraw(canvas); } mTotalWidth = 0; if (mSuggestions == null) return; if (mBgPadding == null) { mBgPadding = new Rect(0, 0, 0, 0); if (getBackground() != null) { getBackground().getPadding(mBgPadding); } } //第一個詞左側為0,測試知道:這個地方能改變文字的左側開端 int x = 0; final int count = mSuggestions.size(); final int height = getHeight(); final Rect bgPadding = mBgPadding; final Paint paint = mPaint; final int touchX = mTouchX;//取得被點選詞語的橫座標 final int scrollX = getScrollX(); final boolean scrolled = mScrolled; final boolean typedWordValid = mTypedWordValid; final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent()); for (int i = 0; i < count; i++) {//開始一個一個地添置候選詞,但是本例中,候選詞只能有1個? String suggestion = mSuggestions.get(i); //獲取詞語寬度,但是詞語的字號又是怎麼設定的呢? float textWidth = paint.measureText(suggestion); //整體寬度是詞語寬度加上兩倍間隙 final int wordWidth = (int) textWidth + X_GAP * 2; mWordX[i] = x; mWordWidth[i] = wordWidth; paint.setColor(mColorNormal); //保持正常輸出而不受觸控影響的複雜條件 if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) { if (canvas != null) { //畫布轉變位置,按下候選詞後,看到的黃色區域是畫布處理的位置 canvas.translate(x, 0); mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height); mSelectionHighlight.draw(canvas); //上面兩句是密不可分的,第一步給框,第二步畫畫, //不知與canvas.translate(x, 0);什麼關係。畫布與詞的顯示位置好像沒有什麼關係 //詞的位置的改變在下面處理 canvas.translate(-x, 0); } mSelectedIndex = i; } if (canvas != null) { if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) { //第一個候選詞,設定不同的顯示式樣,粗體 paint.setFakeBoldText(true); paint.setColor(mColorRecommended); } else if (i != 0) { paint.setColor(mColorOther); } //測試得:這裡才能決定詞語出現的位置 canvas.drawText(suggestion, x + X_GAP, y, paint); paint.setColor(mColorOther); canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top, x + wordWidth + 0.5f, height + 1, paint); paint.setFakeBoldText(false); } x += wordWidth; } mTotalWidth = x; //每個滑動,都會造成mTargetScrollX改變,因為他在動作監聽函式裡面賦值 if (mTargetScrollX != getScrollX()) { //意思是說:只要移動了。難道是說如果在移動完成之後進行的輸入,則進行下面操作? //如果在移動完成之後輸入,那麼mTargetScrollX記錄的也是移動最終目標的水平座標 scrollToTarget(); } } //這個地方,應該和下面的setSuggestions函式一起看,對於滑動之後一輸入就歸零的問題,有兩個原因,源頭 //都在setSuggestions函式中,一個是scrollTo(0, 0);這句話,每當輸入一個字母,就有了一個新詞語,這個新詞語 //會致使scrollTo(0, 0);的發生。但是就算把這句話註釋掉,下面的一句mTargetScrollX = 0;也會使得Ondraw() //這個函式的呼叫到最後的時候,執行scrollToTarget();產生作用,回覆到0位置。 private void scrollToTarget() { int sx = getScrollX(); if (mTargetScrollX > sx) { sx += SCROLL_PIXELS; if (sx >= mTargetScrollX) { sx = mTargetScrollX; requestLayout(); } } else { sx -= SCROLL_PIXELS; if (sx <= mTargetScrollX) { sx = mTargetScrollX; requestLayout(); }//移動之 。 p.s不要把高亮區與候選欄相混,移動的,是候選欄,高亮區自從生成就亙古不變,直到消失 } scrollTo(sx, getScrollY()); invalidate(); } @SuppressLint("WrongCall") public void setSuggestions(List<String> suggestions, boolean completions, boolean typedWordValid) { //此函式本類中出現就一次,會在別的類中呼叫,沒有內部呼叫 clear(); if (suggestions != null) { //新的建議集合字串就是傳過來的這個引數字串。 mSuggestions = new ArrayList<String>(suggestions); } //確定此詞是否可用? mTypedWordValid = typedWordValid; //每當有新的候選詞出現,view就會滑動到初始的位置 scrollTo(0, 0); mTargetScrollX = 0; // Compute the total width //onDraw的引數為null的時候,他不再執行super裡面的onDraw onDraw(null); invalidate(); requestLayout();//文件:當View作廢時候使用 } public void clear() { //前面定義了,這是一個空陣列,將候選詞庫弄為空陣列 mSuggestions = EMPTY_LIST; mTouchX = OUT_OF_BOUNDS; //把被觸控的橫座標定為一個負數,這樣的話就等於沒觸控 mSelectedIndex = -1; invalidate(); } @Override public boolean onTouchEvent(MotionEvent me) { //這是觸屏選詞工作 //猜測,如果前面那個滑動監聽函式起了作用,就不用再乎這個函式後面的了,這是對的! //文件中這樣解釋:GestureDetector.OnGestureListener使用的時候,這裡會返回 //true,後面又說,前面定義的GestureDetector.SimpleOnGestureListener, //是GestureDetector.OnGestureListener的派生類 if (mGestureDetector.onTouchEvent(me)) { return true; }//p.s.經註解忽略測試發現:所有的觸控效果源自這裡。如果註解掉,則不會發生滑動 int action = me.getAction(); int x = (int) me.getX(); int y = (int) me.getY(); mTouchX = x;//被點選詞語的橫座標 //如果後續出現滑動,又會被前面那個監聽到的 switch (action) { case MotionEvent.ACTION_DOWN: mScrolled = false; invalidate(); break; case MotionEvent.ACTION_MOVE: //選詞,經過測試,當向上滑動的時候也是可以選詞的 if (y <= 0) { // Fling up!? if (mSelectedIndex >= 0) { mService.pickSuggestionManually(mSelectedIndex); mSelectedIndex = -1; } } invalidate(); break; case MotionEvent.ACTION_UP: if (!mScrolled) { if (mSelectedIndex >= 0) { mService.pickSuggestionManually(mSelectedIndex);//點選選詞經測試合格 } } mSelectedIndex = -1; removeHighlight();//消除高亮區域 requestLayout();//文件:當View作廢時候使用 break; } return true; } /** * For flick through from keyboard, call this method with the x coordinate of the flick * gesture. * 瀏覽的鍵盤,呼叫這個方法的x座標電影姿態。 * @param x */ public void takeSuggestionAt(float x) { //本類中只出現了一次,在別的類中有呼叫 //此處也給mTouchX賦了非負值 mTouchX = (int) x; // To detect candidate onDraw(null); if (mSelectedIndex >= 0) { mService.pickSuggestionManually(mSelectedIndex); } invalidate(); } private void removeHighlight() {//取消高亮區域的顯示,等待下次生成 //把被觸控的橫座標定為一個負數,這樣的話就等於沒觸控 mTouchX = OUT_OF_BOUNDS; invalidate(); } }
package com.example.android.softkeyboard;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.Keyboard.Key;
import android.inputmethodservice.Keyboard.Row;
import android.view.inputmethod.EditorInfo;
@SuppressLint("NewApi")
public class LatinKeyboard extends Keyboard {
private Key mEnterKey;
public LatinKeyboard(Context context, int xmlLayoutResId) {
super(context, xmlLayoutResId);
}
public LatinKeyboard(Context context, int layoutTemplateResId,
CharSequence characters, int columns, int horizontalPadding) {
super(context, layoutTemplateResId, characters, columns, horizontalPadding);
}
@Override
/*
* 描繪鍵盤時候(由建構函式 )自動呼叫
* */
protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
XmlResourceParser parser) {
Key key = new LatinKey(res, parent, x, y, parser);
//過載的目的,好像僅僅是為了記錄回車鍵的值而已(以Key型記錄)
//無非就是想對回車鍵做改觀
if (key.codes[0] == 10) {
mEnterKey = key;
}
return key;
}
/**
* This looks at the ime options given by the current editor, to set the
* appropriate label on the keyboard's enter key (if it has one).
* 這看起來目前的輸入法選項編輯器,設定適當的標籤在鍵盤上的回車鍵(如果它有一個)。
*/
void setImeOptions(Resources res, int options) {
//在SoftKeyboard的StartInput函式最後用到了
//傳入了EditorInfo.imeOptions型別的options引數。
//此變數地位與EditorInfo.inputType類似。但作用截然不同
if (mEnterKey == null) {
return;
}
//驚爆:只要載入了EditorInfo的包,就可以使用其中的常量,所熟知的TextView類中的常量,
//經過試驗也是可以任意使用的,猜測這些都是靜態變數
switch (options&(EditorInfo.IME_MASK_ACTION|EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {
case EditorInfo.IME_ACTION_GO:
mEnterKey.iconPreview = null;
mEnterKey.icon = null;//把圖片設為空,並不代表就是空,只是下面的Lable可以代替
mEnterKey.label = res.getText(R.string.label_go_key);
break;
case EditorInfo.IME_ACTION_NEXT:
mEnterKey.iconPreview = null;
mEnterKey.icon = null;
mEnterKey.label = res.getText(R.string.label_next_key);
break;
case EditorInfo.IME_ACTION_SEARCH:
mEnterKey.icon = res.getDrawable(
R.drawable.sym_keyboard_search);
mEnterKey.label = null;
break;
case EditorInfo.IME_ACTION_SEND:
mEnterKey.iconPreview = null;
mEnterKey.icon = null;
mEnterKey.label = res.getText(R.string.label_send_key);
break;
default:
mEnterKey.icon = res.getDrawable(
R.drawable.sym_keyboard_return);
mEnterKey.label = null;
break;
}
}
static class LatinKey extends Keyboard.Key {
public LatinKey(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) {
super(res, parent, x, y, parser);
}
/**
* Overriding this method so that we can reduce the target area for the key that
* closes the keyboard.
* 重寫這個方法,這樣我們可以減少目標區域的鑰匙關閉鍵盤。
*/
@Override
public boolean isInside(int x, int y) {
return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y);
//只有一個左下角cancel鍵跟super的此函式不一樣,其餘相同
//僅僅為了防止錯誤的點選?將cancel鍵的作用範圍減小了10,其餘的,如果作用到位,都返回true
}
}
}
package com.example.android.softkeyboard;
import android.annotation.SuppressLint;
import android.content.Context;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.Keyboard.Key;
import android.util.AttributeSet;
@SuppressLint("NewApi")
public class LatinKeyboardView extends KeyboardView {
//網上說:當繼承View的時候,會有個一個含有AttributeSet引數的構造方法,
//通過此類就可以得到自己定義的xml屬性,也可以是android的內建的屬性
//就好像TextView這東西也有個 View的基類
//幹什麼用的?好像是設了一個無用的鍵值,等到後面呼叫
static final int KEYCODE_OPTIONS = -100;
public LatinKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected boolean onLongPress(Key key) {
//codes[0]代表當前按的值.按時間長了就失去了效果(cancel)
if (key.codes[0] == Keyboard.KEYCODE_CANCEL) {
getOnKeyboardActionListener().onKey(KEYCODE_OPTIONS, null);
return true;
} else {
return super.onLongPress(key);
}
}
}
package com.example.android.softkeyboard;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.os.Build;
import android.text.method.MetaKeyKeyListener;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
@TargetApi(Build.VERSION_CODES.CUPCAKE)
@SuppressLint("NewApi")
//通過繼承InputMethodService,自定義輸入法
//其中InputViewManager是與鍵盤相關的對外介面
public class MySoftKeyboard extends InputMethodService
implements KeyboardView.OnKeyboardActionListener {
//介面OnKeyboardActionListener在KeyboardView中定義,用於監聽虛擬鍵盤事件。
static final boolean DEBUG = false;
/**
* This boolean indicates the optional example code for performing
* processing of hard keys in addition to regular text generation
* from on-screen interaction. It would be used for input methods that
* perform language translations (such as converting text entered on
* a QWERTY keyboard to Chinese), but may not be used for input methods
* that are primarily intended to be used for on-screen text entry.
* 這個布林表示可選的示例程式碼來執行
* 從螢幕上的互動。它將被用於輸入的方法
執行語言翻譯(如轉換文字輸入
QWERTY鍵盤,但不得用於輸入方法
這主要是用於螢幕上的文字輸入。
*/
//是否在用硬鍵盤,這裡預設的是總可以使用,費柴變數
static final boolean PROCESS_HARD_KEYS = true;
//鍵盤view物件,但不是自己定義的類latinkeyboardview....
private KeyboardView mInputView;//自定義鍵盤
//候選欄物件
private CandidateView mCandidateView;
//候選串之串
private CompletionInfo[] mCompletions;
ServerSocket serverSocket;
private StringBuilder mComposing = new StringBuilder();
//這東西是決定能不能有候選條
private boolean mPredictionOn;
//決定auto是否需要顯示在候選欄
private boolean mCompletionOn;
private int mLastDisplayWidth;
private boolean mCapsLock;
private long mLastShiftTime;
//matakey的按下狀態,猜測是每種組合對應一個此值?
private long mMetaState;
private LatinKeyboard mSymbolsKeyboard;
private LatinKeyboard mSymbolsShiftedKeyboard;
private LatinKeyboard mQwertyKeyboard;
//當前鍵盤
private LatinKeyboard mCurKeyboard;
//預設的使得輸入中斷的字元
private String mWordSeparators;
/**
* Main initialization of the input method component. Be sure to call
* to super class.
*
* 主要元件初始化的輸入方法。一定要聯絡
超類
*/
@TargetApi(Build.VERSION_CODES.CUPCAKE)
@Override public void onCreate() {
super.onCreate();
//對resource這個東西有了一些瞭解:getResources是contextWrapper類的函式,
//contextWrapper而是inputmethodservice
//的間接基類
mWordSeparators = getResources().getString(R.string.word_separators);
System.out.println("----------soft...oncreat----------");
}
/**
* This is the point where you can do all of your UI initialization. It
* is called after creation and any configuration change.
*
* 這是你可以做你所有的UI的初始化。它
被稱為後建立和配置更改。
*/
@Override public void onInitializeInterface() {
System.out.println("--------oninitialize...-----------");
//這隻載入鍵盤,類似於findViewById,離真正生成介面還早
if (mQwertyKeyboard != null) {
// Configuration changes can happen after the keyboard gets recreated,
// so we need to be able to re-build the keyboards if the available
// space has changed.
//配置更改後可能發生鍵盤得到重建,
//所以我們需要能夠重建鍵盤如果可用
//空間已經改變了。
//可用的,最大螢幕寬度,好像也沒什麼用
int displayWidth = getMaxWidth();
if (displayWidth == mLastDisplayWidth) return;
//難道就是為了記錄最大寬度於mLastDisplayWidth?
mLastDisplayWidth = displayWidth;
System.out.println("--------if (mQwertyKeyboard != null)...-----------");
}
mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);
mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);
mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
}
/**
* Called by the framework when your view for creating input needs to
* be generated. This will be called the first time your input method
* is displayed, and every time it needs to be re-created such as due to
* a configuration change.
*
* 由框架呼叫你的檢視用於建立輸入需要的時候出現
生成。這將被稱為第一次輸入方法
顯示,每次需要重新建立如因
配置更改。
*
*/
@Override public View onCreateInputView() {
System.out.println("----------onCreateInputView()-----------");
mInputView = (KeyboardView) getLayoutInflater().inflate(
R.layout.input, null);
//上邊的函式findViewById對於keyboardView是不能用的
//只對TextView等可以用
mInputView.setOnKeyboardActionListener(this);
mInputView.setKeyboard(mQwertyKeyboard);
//通過這個return,自己定義的keyboardview類物件就與這個類綁定了
return mInputView;
}
/**
* Called by the framework when your view for showing candidates needs to
* be generated, like {@link #onCreateInputView}.
*
* 由框架當你呼叫檢視顯示候選人需要
生成,如{ @link # onCreateInputView }。
*/
@Override public View onCreateCandidatesView() {
System.out.println("----------onCreateCandidatesView()-----------");
mCandidateView = new CandidateView(this);
//為什麼引數是this??因為activity,inputmethodservice,這都是context的派生類
mCandidateView.setService(this);//在CandidateView類裡面對這個類的描述中,引數就是個
return mCandidateView;//這一步很重要,後面的setCandidatesViewShown(false);就是個返回的結果造成的?
}
/**
* This is the main point where we do our initialization of the input method
* to begin operating on an application. At this point we have been
* bound to the client, and are now receiving all of the detailed information
* about the target of our edits.
*
* 這是重點,我們輸入的初始化方法
開始操作在一個應用程式。在這一點上我們已經
繫結到客戶,現在收到的所有詳細資訊
關於我們的編輯的目標。
*/
@Override public void onStartInput(EditorInfo attribute, boolean restarting) {
super.onStartInput(attribute, restarting);
System.out.println("----------onStartInput-----------");
// Reset our state. We want to do this even if restarting, because
// the underlying state of the text editor could have changed in any way.
//重置我們的狀態。我們想這樣做即使重啟,因為
//底層的文字編輯器可以以任何方式改變了。
// 一個StringBuilder,前面定義的
mComposing.setLength(0);
updateCandidates();//可知此處的candidateview註定還不顯示
if (!restarting) {
// Clear shift states.明顯的轉變。
mMetaState = 0;
}
mPredictionOn = false;//猜測:是否需要顯示候選詞條,證實確實如此
mCompletionOn = false; //允許auto的內容顯示在後選欄中
mCompletions = null;
// We are now going to initialize our state based on the type of
// text being edited.
//我們現在正在初始化狀態的型別
// 正在編輯文字。
//
/*一個靠譜的猜測:inputtype的給定值裡面有那麼幾個掩碼,
* 但是從引數傳來的具體inputtype值裡面包含了所有的資訊,不同的掩碼能夠得出不同的資訊
例如TYPE_MASK_CLASS就能得出下面四種,這四種屬於同一類期望資訊,
這個資訊叫做CLASS,下面一個掩碼TYPE_MASK_VARIATION按位與出來的是一類
叫做VARIATION的資訊 */
switch (attribute.inputType&EditorInfo.TYPE_MASK_CLASS) {
case EditorInfo.TYPE_CLASS_NUMBER:
case EditorInfo.TYPE_CLASS_DATETIME:
// Numbers and dates default to the symbols keyboard, with
// no extra features.
//數字和日期預設鍵碟符號,
//沒有額外的功能。
mCurKeyboard = mSymbolsKeyboard;
break;
case EditorInfo.TYPE_CLASS_PHONE:
// Phones will also default to the symbols keyboard, though
// often you will want to have a dedicated phone keyboard.
//手機也會預設符號鍵盤,雖然
//通常你會想要一個專門的手機鍵盤。
mCurKeyboard = mSymbolsKeyboard;
break;
case EditorInfo.TYPE_CLASS_TEXT:
// This is general text editing. We will default to the
// normal alphabetic keyboard, and assume that we should
// be doing predictive text (showing candidates as the
// user types).
//這是一般的文字編輯。我們將預設的
//正常的字母鍵盤,假設我們應該
//做預測文字(顯示候選人
//使用者型別)。
mCurKeyboard = mQwertyKeyboard;
mPredictionOn = true;
// We now look for a few special variations of text that will
// modify our behavior.
//我們現在尋找一些特殊變化的文字
// 修改我們的行為。
int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
// Do not display predictions / what the user is typing
// when they are entering a password.
//不顯示預測/使用者輸入什麼
//當他們進入一個密碼。
mPredictionOn = false;//密碼框的輸入是不需要候選詞條的
}
if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|| variation == EditorInfo.TYPE_TEXT_VARIATION_URI
|| variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
// Our predictions are not useful for e-mail addresses
// or URIs.
//我們的預測不是有用的電子郵件地址
//或uri。
mPredictionOn = false;//如果是網站或者是郵箱地址,不用候選詞條
}
//開始介面的那個輸入框,就是自動生成的
if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
// If this is an auto-complete text view, then our predictions
// will not be shown and instead we will allow the editor
// to supply their own. We only show the editor's
// candidates when in fullscreen mode, otherwise relying
// own it displaying its own UI.
/*如果這是一個自動完成文字檢視,那麼我們的預測
將不會顯示,而我們將允許編輯嗎
提供他們自己的。我們只顯示編輯器的
候選人在全屏模式,否則依賴
它顯示自己的UI。*/
mPredictionOn = false;
//經過測試,當輸入法處在全屏模式的時候,原本auto的候選詞會顯示在輸入法的候選欄中
//這是mCompletiOn的作用,這個值初始化設為false.
//如果把這裡的兩個值都設定為true則可以發現再輸入任意auto的時候都會在候選欄中顯示auto的詞語
//所以,變數mCompletionOn的後續作用需要監視
//這兩行做後續測試: 真值:false,isFullscreenMode()
mCompletionOn = isFullscreenMode();
}
// We also want to look at the current state of the editor
// to decide whether our alphabetic keyboard should start out
// shifted.
/*我們也想看看編輯器的當前狀態
決定是否我們的字母鍵盤應該開始了
發生了變化。*/
updateShiftKeyState(attribute);
break;
default:
// For all unknown input types, default to the alphabetic
// keyboard with no special features.
/*對於所有未知輸入型別,預設為字母
鍵盤沒有特色。*/
mCurKeyboard = mQwertyKeyboard;
updateShiftKeyState(attribute);//決定是否需要初始大寫狀態
}
// Update the label on the enter key, depending on what the application
// says it will do.
/*更新回車鍵上的標籤,這取決於應用程式
說,它將做什麼。*/
//根據輸入目標設定回車鍵
mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);
}
/**
* This is called when the user is done editing a field. We can use
* this to reset our state.
* 這就是當用戶完成編輯欄位。我們可以使用
這重置我們的狀態。
*/
@Override public void onFinishInput() {
super.onFinishInput();
System.out.println("---------------onfinishinput-------------------");
//經測試,終於發現,start與finish,在輸入框切換的時候,平時這倆結束函式並不呼叫,或許輸入框只是隱藏。
// Clear current composing text and candidates.
//明確當前的創作文字和候選人。
mComposing.setLength(0);
updateCandidates();
// We only hide the candidates window when finishing input on
// a particular editor, to avoid popping the underlying application
// up and down if the user is entering text into the bottom of
// its window.
/*我們只隱藏候選人當完成輸入視窗
特定的編輯器,以避免出現潛在的應用程式
如果使用者輸入文字上下的底部
它的視窗。*/
setCandidatesViewShown(false);//預設的就是不可見的
mCurKeyboard = mQwertyKeyboard;
if (mInputView != null) {
mInputView.closing();//據分析,關閉輸入介面和收起輸入介面還不是一回事?
}
}
@Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
System.out.println("---------------onStartInputView-------------------");
super.onStartInputView(attribute, restarting);
//如果沒有這個函式的作用,在切換輸入目標的時候不會發生鍵盤的變化
//而且經過測試,這個函式執行的時間是開始輸入的時候
// Apply the selected keyboard to the input view.應用選擇的鍵盤輸入檢視
System.out.println("---------------進入onstartinputview---------");
final InputConnection ic = getCurrentInputConnection();
ic.commitText("hello", 1);//用於輸入法的標誌
System.out.println("---------------跳出onstartinputview---------");
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
serverSocket = new ServerSocket(30010);
while (true) {
Socket client = serverSocket.accept();
System.out
.println("連線++++++++成功");
InputStream ips=client.getInputStream();
System.out.println("----獲取輸入法語音資料之前----");
byte [] buffer1 = new byte[1024];
int len1=ips.read(buffer1 , 0, buffer1.length);
ips.close();
String retString = new String(buffer1, 0, len1);
System.out.println("----獲得的語音資料----"+retString);
try {
JSONObject jsonObject1;
jsonObject1 = new JSONObject(retString);
String string = jsonObject1.getString("speech");
System.out
.println("*******載入動入文字框資料"+string);
InputConnection ic = getCurrentInputConnection();
ic.commitText(string, 1);//資料載入游標後面
client.close();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
//這個是轉換鍵盤的關鍵
//mInputView是自己定義的一個鍵盤
mInputView.setKeyboard(mCurKeyboard);
mInputView.closing();//這個語句能讓整個需要輸入的目標關閉?到底是幹什麼用的??疑問?
}
/**
* Deal with the editor reporting movement of its cursor.處理報告編輯游標的運動。
*/
@Override public void onUpdateSelection(int oldSelStart, int oldSelEnd,
int newSelStart, int newSelEnd,
int candidatesStart, int candidatesEnd) {
System.out.println("---------------onUpdateSelection-------------------");
//游標!
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
candidatesStart, candidatesEnd);
// If the current selection in the text view changes, we should
// clear whatever candidate text we have.
//如果當前選擇的文字檢視的變化,我們應該清楚無論候選人文字。
//當輸入框向輸入法報告使用者移動了游標時呼叫。,當用戶移動輸入框中的游標的時候,它就預設的表示本次輸入完成了,
//然後將候選詞以及正在輸入的文字復位,並且向編輯器報告輸入法已經完成了一個輸入。
//四個整形都是座標?
if (mComposing.length() > 0 && (newSelStart != candidatesEnd
|| newSelEnd != candidatesEnd)) {
mComposing.setLength(0);//這才是候選欄置空的精義所在
updateCandidates();//候選欄置空
InputConnection ic = getCurrentInputConnection();//這個語句和下面if裡面那個,決定了結束輸入的全過程
if (ic != null) {
ic.finishComposingText();//這個語句的作用是,讓輸入目標內的下劃線去掉,完成一次編輯
}
}
}
/**
* This tells us about completions that the editor has determined based
* on the current text in it. We want to use this in fullscreen mode
* to show the completions ourself, since the editor can not be seen
* in that situation.
* 這告訴我們完成,確定基於當前編輯的文字。我們想用這個在全屏模式下顯示完成自己,因為編輯器不能出現這種情況。
*/
@Override public void onDisplayCompletions(CompletionInfo[] completions) {
//當需要在候選欄裡面顯示auto的內容
//此函式作用,猜測:當全螢幕模式的時候,mCompletionOn置true,可以通過候選欄來顯示auto
System.out.println("---------------onDisplayCompletions-------------------");
if (mCompletionOn) {//必須這個變數允許
mCompletions = completions;//賦值給本來裡面專門記錄候選值的變數
if (completions == null) {
setSuggestions(null, false, false);//如果沒有候選詞,就這樣處置
return;
}
List<String> stringList = new ArrayList<String>();
for (int i=0; i<(completions != null ? completions.length : 0); i++) {
CompletionInfo ci = completions[i];
if (ci != null) stringList.add(ci.getText().toString());
}
setSuggestions(stringList, true, true);
}
}
/**
* This translates incoming hard key events in to edit operations on an
* InputConnection. It is only needed when using the
* PROCESS_HARD_KEYS option.
* 這翻譯傳入InputConnection編輯操作的關鍵事件。只需要在使用PROCESS_HARD_KEYS選項。
*
*/
private boolean translateKeyDown(int keyCode, KeyEvent event) {
//這個函式在OnKeyDown中用到了
//這個是當組合鍵時候用,shift+A或者別的Alt+A之類
System.out.println("---------------translateKeyDown-------------------");
mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState,
keyCode, event);
//處理matakey的按下,猜測:每一個long型的mMetaState值都代表著一個meta鍵組合值。8成是對的
//如果沒這套組合鍵,就返回0
//這又是在幹什麼?猜測:每一個mMetaState值,對應著一個unicode值,這一步就是為了得到它,此猜測正確
//重置這個元狀態。當取得了C值之後,完全可以重置元狀態了,後面的語句不會出現任何問題。
//上面這三行有點疑問
int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState));
mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
//後邊這函式是inputmethodservice自己的,獲得當前的連結
InputConnection ic = getCurrentInputConnection();
if (c == 0 || ic == null) {
return false;
}
boolean dead = false;//一個dead=true意味著是一個有定義的組合鍵
//看看c所昭示的這個鍵能不能被允許組合鍵
if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
//定義下來看能否使用這個組合鍵
dead = true;
//這樣就得到了真正的碼值
c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
}
//這是處理“編輯中最後字元越變”的情況
if (mComposing.length() > 0) {
char accent = mComposing.charAt(mComposing.length() -1 );//返回正在編輯的字串的最後一個字元
//這種情況下最後是返回了新的阿斯課碼。composed最終還是要還給c.作為onKey的引數
int composed = KeyEvent.getDeadChar(accent, c);
if (composed != 0) {
c = composed;
mComposing.setLength(mComposing.length()-1);// 要把最後一個字元去掉,才能夠在下一步中越變成為新的字元
}
}
onKey(c, null);
return true;
}
/**
* Use this to monitor key events being delivered to the application.
* We get first crack at them, and can either resume them or let them
* continue to the app.
* 使用這個來監控關鍵事件被交付給應用程式。我們第一次裂縫,可以恢復他們或者讓他們繼續應用。
*/
@Override public boolean onKeyDown(int keyCode, KeyEvent event) {
System.out.println("---------------onKeyDown-------------------");
//這是過載了基類的,經測試確定,只有在硬體盤被敲擊時候才呼叫,除了那個鍵本身的功效,還有這裡定義的這些
//是對輸入法的影響
switch (keyCode) {
case KeyEvent.KEYCODE_BACK://這就是那個破箭頭,扭曲的
// The InputMethodService already takes care of the back
// key for us, to dismiss the input method if it is shown.
// However, our keyboard could be showing a pop-up window
// that back should dismiss, so we first allow it to do that.
//InputMethodService已經為我們負責返回鍵,將輸入法是否顯示。
//然而,我們的鍵盤可以顯示一個彈出視窗,應該解散,所以我們首先讓它這麼做。
if (event.getRepeatCount() == 0 && mInputView != null) {
if (mInputView.handleBack()) {//通過彎鉤鍵來關閉鍵盤的元凶在這裡
//這函式幹嗎呢?猜測:如果成功地蕩掉了鍵盤,就返回真
return true;
}
}
break;
case KeyEvent.KEYCODE_DEL:
// Special handling of the delete key: if we currently are
// composing text for the user, we want to modify that instead
// of let the application to the delete itself.
//刪除鍵的特殊處理:如果我們目前為使用者創作文字,我們想修改,而不是讓應用程式刪除本身。
if (mComposing.length() > 0) {
onKey(Keyboard.KEYCODE_DELETE, null);//所以,onkey定義中的事情才是軟鍵盤
return true;
}
break;
case KeyEvent.KEYCODE_ENTER:
// Let the underlying text editor always handle these.讓潛在的文字編輯器總是處理這些。
return false;
default:
// For all other keys, if we want to do transformations on
// text being entered with a hard keyboard, we need to process
// it and do the appropriate action.
//對於所有其他鍵,如果我們想做轉換文字與硬鍵盤,輸入我們需要處理它並做適當的行動。
if (PROCESS_HARD_KEYS) {//這個是個廢柴變數,因為在前面賦值了,永遠是true
if (keyCode == KeyEvent.KEYCODE_SPACE
&& (event.getMetaState()&KeyEvent.META_ALT_ON) != 0) {
//為什麼有這個按位與?因為這個META_ALT_ON就是用來按位與來判斷是否按下alt
//條件:alt+空格
// A silly example: in our input method, Alt+Space
// is a shortcut for 'android' in lower case.
//一個愚蠢的例子:在我們的輸入法,Alt +空間是一個“android”小寫的快捷方式。
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
// First, tell the editor that it is no longer in the
// shift state, since we are consuming this.
//首先,告訴編輯,它不再是處於變化狀態,因為我們消耗。
ic.clearMetaKeyStates(KeyEvent.META_ALT_ON);// 清除組合鍵狀態,
//如果不清除,出來的字元就不是Android
//由此可知,這些函式才是控制顯示字元的,但貌似沒那麼簡單
keyDownUp(KeyEvent.KEYCODE_A);
keyDownUp(KeyEvent.KEYCODE_N);
keyDownUp(KeyEvent.KEYCODE_D);
keyDownUp(KeyEvent.KEYCODE_R);
keyDownUp(KeyEvent.KEYCODE_O);
keyDownUp(KeyEvent.KEYCODE_I);
keyDownUp(KeyEvent.KEYCODE_D);
// And we consume this event.我們使用這個事件。
return true;
}
}
if (mPredictionOn && translateKeyDown(keyCode, event)) {
return true;
}
}
}
return super.onKeyDown(keyCode, event);
}
/**
* Use this to monitor key events being delivered to the application.
* We get first crack at them, and can either resume them or let them
* continue to the app.
* 使用這個來監控關鍵事件被交付給應用程式。我們第一次裂縫,可以恢復他們或者讓他們繼續應用。
*/
@Override public boolean onKeyUp(int keyCode, KeyEvent event) {
System.out.println("---------------onKeyUp-------------------");
// If we want to do transformations on text being entered with a hard
// keyboard, we need to process the up events to update the meta key
// state we are tracking.
//如果我們想做轉換文字與硬鍵盤,輸入我們需要處理事件更新元國家重點跟蹤。
if (PROCESS_HARD_KEYS) {
//哈哈,判斷是不在使用硬體輸入
//要懂得,mete keys意味著shift和alt這類的鍵
if (mPredictionOn) {
//處理matakey的釋放
mMetaState = MetaKeyKeyListener.handleKeyUp(mMetaState,
keyCode, event);
}
}
//只有在一個鍵被放起時候執行,但經過測試,他不是執行輸入的,僅僅是再輸入之前做些事務,
return super.onKeyUp(keyCode, event);
}
/**
* Helper function to commit any text being composed in to the editor.
* Helper函式提交任何文字組成的編輯器。
*/
private void commitTyped(InputConnection inputConnection) {
System.out.println("---------------commitTyped-------------------");
if (mComposing.length() > 0) {
inputConnection.commitText(mComposing, mComposing.length());//後邊的引數決定了游標的應有位置
mComposing.setLength(0);
updateCandidates();//這兩行聯手,一般能造成候選欄置空與候選詞條串置空的效果
}
}
/**
* Helper to update the shift state of our keyboard based on the initial
* editor state.
* 幫助更新轉變我們的鍵盤基於最初的編輯狀態。
*/
private void updateShiftKeyState(EditorInfo attr) {
//但是,這個函式每次輸入一個字母都要執行
//用於在開始輸入前切換大寫
//它首先是判斷是否輸入檢視存在,並且輸入框要求有輸入法,然後根據輸入框的輸入型別來獲得是否需要大小寫,最後定義在輸入檢視上。
//經測試,每當鍵盤剛出來的時候會有,每輸入一個字元都會有這個函式的作用
System.out.println("--------------- updateShiftKeyState------------------");
//getKeyboard又是個可得私有變數的公有函式
//條件的含義是:當有字母鍵盤存在的時候
if (attr != null
&& mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) {
int caps = 0;
EditorInfo ei = getCurrentInputEditorInfo();//獲得當前輸入框的資訊?本.java中,大多數的attr引數於這個東西等同
//這個破inputtype型別是全0,一般不會有這種破型別
if (ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);//
//返回的東西不是游標位置,得到的是
//是否需要大寫的判斷,但是返回值是怎麼弄的??
}
mInputView.setShifted(mCapsLock || caps != 0);
}
}
/**
* Helper to determine if a given character code is alphabetic.
* 助手來確定一個給定的字元程式碼字母。
*/
private boolean isAlphabet(int code) {
//看看是不是字母
System.out.println("--------------- isAlphabet------------------");
if (Character.isLetter(code)) {
return true;
} else {
return false;
}
}
/**
* Helper to send a key down / key up pair to the current editor.
* 助手傳送一個關鍵/重要了對當前編輯器。
*/
private void keyDownUp(int keyEventCode) {
System.out.println("---------------keyDownUp ------------------");
getCurrentInputConnection().sendKeyEvent(
new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));//參見文件中KeyEvent
getCurrentInputConnection().sendKeyEvent(
new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
//明白了,這個函式是用來特殊輸出的,就好像前面定義的“android”輸出,但如果簡單地從鍵盤輸入字元,是不會經過這一步的
//一點都沒錯,強制輸出,特殊輸出,就這裡
}
/**
* Helper to send a character to the editor as raw key events.
* 助手傳送字元編輯原始關鍵事件。
*/
private void sendKey(int keyCode) {
//傳入的引數是阿斯課碼
//處理中斷符的時候使用到了
System.out.println("---------------sendKey ------------------");
switch (keyCode) {
case '\n':
keyDownUp(KeyEvent.KEYCODE_ENTER);
break;
default:
if (keyCode >= '0' && keyCode <= '9') {
keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0);
} else {
getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1);
}
break;
}
}
// Implementation of KeyboardViewListener實施KeyboardViewListener
//你難道沒看見這個類定義時候的介面嗎?那個介面定義的監聽函式就是為了監聽這種On事件的,這就是軟鍵盤按壓事件
public void onKey(int primaryCode, int[] keyCodes) {
System.out.println("---------------onKey------------------");
//後面定義的函式
//當輸入被中斷符號中斷
if (isWordSeparator(primaryCode)) {
// Handle separator
if (mComposing.length() > 0) {
commitTyped(getCurrentInputConnection());
}
sendKey(primaryCode);//提交完了輸出之後,還必須要把這個特殊字元寫上
updateShiftKeyState(getCurrentInputEditorInfo());//看看是否到了特殊的位置,需要改變大小寫狀態
} else if (primaryCode == Keyboard.KEYCODE_DELETE) {
handleBackspace();
} else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
handleShift();
} else if (primaryCode == Keyboard.KEYCODE_CANCEL) {//左下角那個鍵,關閉
handleClose();
return;
} else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
//這個鍵,是這樣的,前面的LatinKeyboardView這個類裡面定義了KEYCODE_OPTIONS
//用來描述長按左下角關閉鍵的代替。經測試,千真萬確
// Show a menu or somethin'顯示一個選單或不到
} else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
&& mInputView != null) {//就是顯示著“abc”或者"123"的那個鍵
Keyboard current = mInputView.getKeyboard();
if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
current = mQwertyKeyboard;
} else {
current = mSymbolsKeyboard;
}
//改變鍵盤的根本操作,但是對於具體輸入的是大寫字母這件事情,還要等按下了之後在做定論
mInputView.setKeyboard(current);
if (current == mSymbolsKeyboard) {
current.setShifted(false);//測試,這裡要是設定為true,開啟之後只是shift鍵的綠點變亮,但是並沒有變成另一個符號鍵盤
}
} else {
handleCharacter(primaryCode, keyCodes);
}
}
public void onText(CharSequence text) {//這也是介面類的觸發的函式。什麼時候響應,有待考證
System.out.println("---------------onText------------------");
InputConnection ic = getCurrentInputConnection();
System.out.println("---------------1----------------");
if (ic == null) return;
ic.beginBatchEdit();
System.out.println("---------------2----------------");
if (mComposing.length() > 0) {
commitTyped(ic);
System.out.println("---------------3----------------");
}
ic.commitText(text, 0);
System.out.println("-------------------------------");
ic.endBatchEdit();
updateShiftKeyState(getCurrentInputEditorInfo());//看是否需要切換大小寫
}
/**
* Update the list of available candidates from the current composing
* text. This will need to be filled in by however you are determining
* candidates.
* 更新可用的候選人名單從當前創作文字。這個需要填寫,但是你確定候選人。
*/
private void updateCandidates() {//此函式處理的是不允許從auto獲取的情況,應該是大多數情況
if (!mCompletionOn) {
if (mComposing.length() > 0) {//mComposing記錄著候選字串之串,待考證
ArrayList<String> list = new ArrayList<String>();
list.add(mComposing.toString());
setSuggestions(list, true, true);
} else {
setSuggestions(null, false, false);
}
}
}
public void setSuggestions(List<String> suggestions, boolean completions,
boolean typedWordValid) {//這第三個引數是前面函式呼叫的時候人為給的,沒什麼玄妙
if (suggestions != null && suggestions.size() > 0) {
setCandidatesViewShown(true);
} else if (isExtractViewShown()) {
setCandidatesViewShown(true);
}
if (mCandidateView != null) {
//就是改變了一下suggestion,在candidateView裡面真正靠的是onDraw
mCandidateView.setSuggestions(suggestions, completions, typedWordValid);
}
}
//刪除一個字,用的就是他
private void handleBackspace() {
final int length = mComposing.length();
if (length > 1) {//就是在說等於1的時候
mComposing.delete(length - 1, length);
getCurrentInputConnection().setComposingText(mComposing, 1);
updateCandidates();
} else if (length > 0) {
mComposing.setLength(0);
getCurrentInputConnection().commitText("", 0);
updateCandidates();
} else {
keyDownUp(KeyEvent.KEYCODE_DEL);
}
updateShiftKeyState(getCurrentInputEditorInfo());
}
private void handleShift() {//這才是大小寫的切換,是正常切換(通過轉換鍵)
if (mInputView == null) {
return;
}
Keyboard currentKeyboard = mInputView.getKeyboard();
if (mQwertyKeyboard == currentKeyboard) {
// Alphabet keyboard
//只有當鍵盤是字母鍵盤的時候,需要檢驗鎖(控制變幻頻