編輯:關於Android編程
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 ListmSuggestions; //這個是用戶選擇的詞的索引 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 EMPTY_LIST = new ArrayList (); 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 suggestions, boolean completions, boolean typedWordValid) { //此函數本類中出現就一次,會在別的類中調用,沒有內部調用 clear(); if (suggestions != null) { //新的建議集合字串就是傳過來的這個參數字串。 mSuggestions = new ArrayList (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; } ListstringList = new ArrayList (); 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 list = new ArrayList (); list.add(mComposing.toString()); setSuggestions(list, true, true); } else { setSuggestions(null, false, false); } } } public void setSuggestions(List 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 //只有當鍵盤是字母鍵盤的時候,需要檢驗鎖(控制變幻頻率,不能過快) checkToggleCapsLock(); mInputView.setShifted(mCapsLock || !mInputView.isShifted()); } else if (currentKeyboard == mSymbolsKeyboard) { mSymbolsKeyboard.setShifted(true); //所謂的setShift,僅僅指的是那個鍵盤的大小寫鍵變化,經測試,只要android:code=-1就有這種綠點效果 mInputView.setKeyboard(mSymbolsShiftedKeyboard); mSymbolsShiftedKeyboard.setShifted(true); } else if (currentKeyboard == mSymbolsShiftedKeyboard) { mSymbolsShiftedKeyboard.setShifted(false); mInputView.setKeyboard(mSymbolsKeyboard); mSymbolsKeyboard.setShifted(false); } } private void handleCharacter(int primaryCode, int[] keyCodes) {//primayCode是鍵的阿斯課碼值 if (isInputViewShown()) { if (mInputView.isShifted()) { primaryCode = Character.toUpperCase(primaryCode); //這才真正把這個字符變成了大寫的效果,經測試,沒有就不行 //把鍵盤換成大寫的了還不夠,那只是從View上解決了問題,一定要這樣一句才行 } } if (isAlphabet(primaryCode) && mPredictionOn) { //輸入的是個字母,而且允許候選欄顯示 mComposing.append((char) primaryCode);//append(添加)就是把當前的輸入的一個字符放到mComposing裡面來 getCurrentInputConnection().setComposingText(mComposing, 1);//在輸入目標中也顯示最新得到的mComposing. updateShiftKeyState(getCurrentInputEditorInfo());//每當輸入完結,都要檢驗是否需要變到大寫 updateCandidates(); } else { //比如說當輸入的是“‘”這個符號的時候,就會掉用這個 //結果就是remove掉所有編輯中的字符,第二個參數的正負,決定著 //光標位置的不同 getCurrentInputConnection().commitText( String.valueOf((char) primaryCode), 1); } } private void handleClose() { //關閉鍵盤件的作用就在這裡,左下角那個.,記住!!!!!左下角那個,不是彎鉤鍵!!!! commitTyped(getCurrentInputConnection()); requestHideSelf(0);//關掉輸入法的區域,這才是關閉的王道.似乎這句包含了上面那句的作用(測試結果) mInputView.closing();//這個函數不懂什麼意思待問?? 哪裡都測試,哪裡都沒有用處?? } private void checkToggleCapsLock() { long now = System.currentTimeMillis();//記錄上次變幻的時間 if (mLastShiftTime + 800 > now) {//不允許頻繁地換大小寫? mCapsLock = !mCapsLock; mLastShiftTime = 0; } else { mLastShiftTime = now; } } private String getWordSeparators() { return mWordSeparators; } public boolean isWordSeparator(int code) { String separators = getWordSeparators();//檢查所屬入的字符有沒有在這些字符裡面 return separators.contains(String.valueOf((char)code)); } public void pickDefaultCandidate() { pickSuggestionManually(0); } public void pickSuggestionManually(int index) { if (mCompletionOn && mCompletions != null && index >= 0 && index < mCompletions.length) { CompletionInfo ci = mCompletions[index]; getCurrentInputConnection().commitCompletion(ci); if (mCandidateView != null) { mCandidateView.clear(); } updateShiftKeyState(getCurrentInputEditorInfo()); } else if (mComposing.length() > 0) { // If we were generating candidate suggestions for the current // text, we would commit one of them here. But for this sample, // we will just commit the current text. //如果我們產生候選人建議為當前文本,我們會提交其中一人在這裡。但對於此示例,我們將只提交當前文本 commitTyped(getCurrentInputConnection()); } } //著下面6個函數,完全是因為聲明了那個接口類,所以必須要包含這幾個函數,還有上面的幾個函數,但是實際上這些函數可以沒有意義 public void swipeRight() { if (mCompletionOn) { pickDefaultCandidate(); } } public void swipeLeft() { handleBackspace(); } public void swipeDown() { handleClose(); } public void swipeUp() { } public void onPress(int primaryCode) { } public void onRelease(int primaryCode) { } }
|
|
|
<key android:codes="34" android:keylabel="" "=""> |
symbols_shift.xml
最近在做一個直播的android手機app,難點在於流媒體的處理,主要是對流媒體進行編碼與傳輸,在此用H264編碼,傳輸協議采用RTMP,流媒體服務器用nginx並進行配
在一篇博客中看到一篇文章,感覺描述的還可以:在前面的博客中介紹的都是使用java開發Android應用,這篇博客將介紹java通過使用jni調用c語言做開發為了更加形象的
導語 本文主要是圍繞android直播助手的功能做了一些研究,因為之前對Android多媒體相關的內容知之甚少,只有概念,於是查閱了相關資料並做以總結。由於我對音視頻相關
學習技術最好的方式就是實戰,看書看不到的東西太多了,實際操作時會碰到各種書本裡提不到的問題,解決這些問題會迅速提升你的能力,你是一個solider,最好成長的方式就是實戰