Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> android sdk中 softkeyboard的自己解析(3)

android sdk中 softkeyboard的自己解析(3)

編輯:Android開發實例

3.CandidateView.java


 

  1. /*  
  2. * Copyright (C) 2008-2009 Google Inc.  
  3. *   
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not  
  5. * use this file except in compliance with the License. You may obtain a copy of  
  6. * the License at  
  7. *   
  8. * http://www.apache.org/licenses/LICENSE-2.0  
  9. *   
  10. * Unless required by applicable law or agreed to in writing, software  
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT  
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the  
  13. * License for the specific language governing permissions and limitations under  
  14. * the License.  
  15. */ 
  16.  
  17. package com.example.android.softkeyboard;  
  18.  
  19. import android.content.Context;  
  20. import android.content.res.Resources;  
  21. import android.graphics.Canvas;  
  22. import android.graphics.Paint;  
  23. import android.graphics.Rect;  
  24. import android.graphics.drawable.Drawable;  
  25. import android.view.GestureDetector;  
  26. import android.view.MotionEvent;  
  27. import android.view.View;  
  28.  
  29. import java.util.ArrayList;  
  30. import java.util.List;  
  31.  
  32. public class CandidateView extends View {  
  33.  
  34. private static final int OUT_OF_BOUNDS = -1;  
  35.  
  36. private SoftKeyboard mService;    //這個是這個candidateView的宿主類,也就是該view是為什麼輸入法服務的。  
  37. private List<String> mSuggestions; //這個是建議。比如說當我們輸入一些字母之後輸入法希望根據輸入來進行聯想建議。  
  38. private int mSelectedIndex;   //這個是用戶選擇的詞的索引。  
  39. private int mTouchX = OUT_OF_BOUNDS;  
  40. private Drawable mSelectionHighlight; //這個是用來描繪選擇區域高亮的一個類  
  41. private boolean mTypedWordValid;    //鍵入的word是否合法正確。  
  42.  
  43. private Rect mBgPadding;       //背景填充區域,決定將要在那個部分顯示?  
  44.  
  45. private static final int MAX_SUGGESTIONS = 32;  
  46. private static final int SCROLL_PIXELS = 20;  
  47.  
  48. private int[] mWordWidth = new int[MAX_SUGGESTIONS];  //這個是對於候選詞的每個詞的寬度  
  49. private int[] mWordX = new int[MAX_SUGGESTIONS];//這個是每個候選詞的X坐標。  
  50. //有了這兩個變量,就能夠在屏幕上准確的繪制出該候選鍵  
  51.  
  52. private static final int X_GAP = 10;  //難道是兩個詞語之間的間隙?對了!  
  53.  
  54. private static final List<String> EMPTY_LIST = new ArrayList<String>();  
  55.  
  56. private int mColorNormal;  
  57. private int mColorRecommended;  
  58. private int mColorOther;  
  59. private int mVerticalPadding;  
  60. private Paint mPaint;    //所有關於繪制的信息,比如線條的顏色等  
  61. private boolean mScrolled;  
  62. private int mTargetScrollX;  
  63.  
  64. private int mTotalWidth;  
  65.  
  66. private GestureDetector mGestureDetector;  
  67.  
  68. /**  
  69. * Construct a CandidateView for showing suggested words for completion.  
  70. * @param context  
  71. * @param attrs  
  72. */ 
  73. public CandidateView(Context context) {  
  74. //activity,inputmethodservice,這都是context的派生類  
  75. super(context);  
  76. mSelectionHighlight = context.getResources().getDrawable(android.R.drawable.list_selector_background);       
  77. //getResouces這個函數用來得到這個應用程序的所有資源,就連android自帶的資源也要如此  
  78. mSelectionHighlight.setState(new int[] {  
  79. //mSelectionHighlight類型是Drawable,而Drawable設置狀態就是這樣  
  80. android.R.attr.state_enabled,     //這行如果去掉,點擊候選詞的時候是灰色,但是也可以用  
  81. android.R.attr.state_focused,      //用處不明。。。。  
  82. android.R.attr.state_window_focused,   //這行如果去掉,當點擊候選詞的時候背景不會變成橙色  
  83. android.R.attr.state_pressed   //點擊候選詞語時候背景顏色深淺的變化,不知深層意義是什麼?  
  84. });  
  85.  
  86. Resources r = context.getResources();  
  87.  
  88. setBackgroundColor(r.getColor(R.color.candidate_background));  
  89. //設置高亮區域的背景顏色,還是透明的,很美,很美,但為什麼是透明的還有待考證?  
  90.  
  91. mColorNormal = r.getColor(R.color.candidate_normal);  
  92. //這個顏色,是非首選詞的顏色  
  93. mColorRecommended = r.getColor(R.color.candidate_recommended);  
  94. //找到了,這個是顯示字體的顏色  
  95. mColorOther = r.getColor(R.color.candidate_other);  
  96. //這個是候選詞語分割線的顏色  
  97.  
  98. mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding);  
  99. //這是系統定義的一個整型變量。用就可以了  
  100.  
  101. mPaint = new Paint();         //畫筆  
  102. mPaint.setColor(mColorNormal);  
  103. mPaint.setAntiAlias(true);    //這行如果沒有,那麼字體的線條就不一樣  
  104. mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height));  
  105. mPaint.setStrokeWidth(0);  
  106.  
  107. //用手可以滑動,這是在構造函數裡面對滑動監聽的重載,猜測,這個函數與onTouchEvent函數應該是同時起作用?  
  108. mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {  
  109. @Override 
  110. public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {  
  111. mScrolled = true;  
  112. int sx = getScrollX();  
  113. //得到滑動開始的橫坐標  
  114. sx += distanceX;   //加上滑動的距離,這個滑動距離是最後一次call滑動之間的距離,很小,應該  
  115. if (sx < 0) {  
  116. sx = 0;  
  117. }  
  118. if (sx + getWidth() > mTotalWidth) {                      
  119. sx -= distanceX;  
  120. }  
  121. mTargetScrollX = sx;            //記錄將要移動到的位置,後面會用到  
  122. scrollTo(sx, getScrollY());     //這是處理滑動的函數,view類的函數。後面一個參數,說明Y軸永遠不變,如果你嘗試去改變一下,經測試,太好玩了  
  123. invalidate();                   //文檔中說的是使得整個VIew作廢,但是如果不用這句,會發生什麼?  
  124. return true;  
  125. }  
  126. });  
  127. setHorizontalFadingEdgeEnabled(true);       //這後三行語句不是在GestureDetector函數中的,而是在構造函數中的,當候選View建立成功的時候就已經是下面的狀態了  
  128. //拖動時刻左右兩邊的淡出效果  
  129. setWillNotDraw(false);  
  130. //當拖動的時候,依舊可以輸入並顯示  
  131. setHorizontalScrollBarEnabled(true);  
  132. setVerticalScrollBarEnabled(false);  
  133. //作用暫時不明?  
  134.  
  135.  
  136. }//這裡是CandidateView構造函數的完結  
  137.  
  138. /**  
  139. * A connection back to the service to communicate with the text field  
  140. * @param listener  
  141. */ 
  142. public void setService(SoftKeyboard listener) { //自己定義的廢柴函數,使得私有變量mService的值得以改變  
  143. mService = listener;  
  144. }  
  145.  
  146. @Override 
  147. public int computeHorizontalScrollRange() {  
  148. //給別的類用的廢柴函數  
  149. return mTotalWidth;    
  150. }  
  151.  
  152. @Override 
  153. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  154.  
  155. int measuredWidth = resolveSize(50, widthMeasureSpec);  
  156. //猜測:如果第二參數是從onMeasure的參數中來的,就用第二變量  
  157. //這個函數是個調和寬度函數,一般情況下用參數1值,除非第二個參數給其限制  
  158. // Get the desired height of the icon menu view (last row of items does  
  159. // not have a divider below)  
  160. Rect padding = new Rect();  
  161. mSelectionHighlight.getPadding(padding);  
  162. //吳:高亮區域除了字以外,剩下的空隙,用getPadding得到,或許,這是由list_selector_background決定的。  
  163. final int desiredHeight = ((int)mPaint.getTextSize()) + mVerticalPadding+ padding.top + padding.bottom;  
  164. //這裡用來計算整個候選條的應有高度,當然,得考慮字體的高度,這也是重載的原因  
  165. //mVerticalPadding參見第99行的定義,那個是用的系統是獻給定的高度  
  166. // Maximum possible width and desired height  
  167. setMeasuredDimension(measuredWidth,resolveSize(desiredHeight, heightMeasureSpec));  
  168. //設置寬高,這是View類的函數。整個候選欄的大小都是由這個語句決定的  
  169. }  
  170.  
  171. /**  
  172. * If the canvas is null, then only touch calculations are performed to pick the target  
  173. * candidate.  
  174. */ 
  175.  
  176. @Override 
  177. //這是每個View對象繪制自己的函數,重載之。經測試:沒有這個函數的重載,則顯示不出字來,這個就是用來顯示字條  
  178. protected void onDraw(Canvas canvas) {  
  179. if (canvas != null) {  
  180. super.onDraw(canvas);  
  181. }  
  182. mTotalWidth = 0;  
  183. if (mSuggestions == null) return;  
  184.  
  185. if (mBgPadding == null) {  
  186. //mBgPadding,Rect類對象  
  187. mBgPadding = new Rect(0, 0, 0, 0);  
  188. if (getBackground() != null) {  
  189. getBackground().getPadding(mBgPadding);  
  190. //這個View的背景,也就是整個候選區域的背景,一個Drawable類型,這跟高亮區有什麼關系?  
  191. }  
  192. }  
  193. int x = 0;  //第一個詞左側為0,測試知道:這個地方能改變文字的左側開端  
  194. final int count = mSuggestions.size();   
  195. final int height = getHeight();  
  196. final Rect bgPadding = mBgPadding;  
  197. final Paint paint = mPaint;    //paint的功效就和畫筆一樣  
  198. final int touchX = mTouchX;                              //取得被點擊詞語的橫坐標  
  199. final int scrollX = getScrollX();  
  200. final boolean scrolled = mScrolled;  
  201. final boolean typedWordValid = mTypedWordValid;  
  202. final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent());  
  203.  
  204. for (int i = 0; i < count; i++) {   //開始一個一個地添置候選詞,但是本例中,候選詞只能有1個?  
  205. String suggestion = mSuggestions.get(i);  
  206. float textWidth = paint.measureText(suggestion);  
  207. //獲取詞語寬度,但是詞語的字號又是怎麼設定的呢?  
  208. final int wordWidth = (int) textWidth + X_GAP * 2;  
  209. //整體寬度是詞語寬度加上兩倍間隙  
  210. mWordX[i] = x;  
  211. mWordWidth[i] = wordWidth;       
  212. paint.setColor(mColorNormal);  
  213. if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) {  
  214. //保持正常輸出而不受觸摸影響的復雜條件  
  215. if (canvas != null) {  
  216. canvas.translate(x, 0);  //畫布轉變位置,按下候選詞後,看到的黃色區域是畫布處理的位置  
  217. mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height);  
  218. mSelectionHighlight.draw(canvas);  
  219. //上面兩句是密不可分的,第一步給框,第二步畫畫,不知與canvas.translate(x, 0);什麼關系。畫布與詞的顯示位置好像沒有什麼關系  
  220. //詞的位置的改變在下面處理  
  221. canvas.translate(-x, 0);  
  222. }  
  223. mSelectedIndex = i;  
  224. }  
  225.  
  226. if (canvas != null) {  
  227. if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) {  
  228. paint.setFakeBoldText(true);      //第一個候選詞,設置不同的顯示式樣,粗體  
  229. paint.setColor(mColorRecommended);//設置顯示字體顏色  
  230. } else if (i != 0) {  
  231. paint.setColor(mColorOther);    //至今還沒發現什麼是不可得詞匯mColorOther顏色的詞匯至今沒有看見,待考證?  
  232. }  
  233. canvas.drawText(suggestion, x + X_GAP, y, paint);   //測試得:這裡才能決定詞語出現的位置  
  234.  
  235. paint.setColor(mColorOther);   
  236. canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top,   
  237. x + wordWidth + 0.5f, height + 1, paint);   //這裡就是畫那條分割線,還有上面的顏色設置  
  238. paint.setFakeBoldText(false);      //為了讓以後的字體不再是粗體  
  239. }  
  240. x += wordWidth;  
  241. }  
  242. mTotalWidth = x;  
  243. if (mTargetScrollX != getScrollX()) {        //每個滑動,都會造成mTargetScrollX改變,因為他在動作監聽函數裡面賦值  
  244. //意思是說:只要移動了。難道是說如果在移動完成之後進行的輸入,則進行下面操作?  
  245. //如果在移動完成之後輸入,那麼mTargetScrollX記錄的也是移動最終目標的水平坐標  
  246. scrollToTarget();  
  247. }  
  248. //這個地方,應該和下面的setSuggestions函數一起看,對於滑動之後一輸入就歸零的問題,有兩個原因,源頭  
  249. //都在setSuggestions函數中,一個是scrollTo(0, 0);這句話,每當輸入一個字母,就有了一個新詞語,這個新詞語  
  250. //會致使scrollTo(0, 0);的發生。但是就算把這句話注釋掉,下面的一句mTargetScrollX = 0;也會使得Ondraw()  
  251. //這個函數的調用到最後的時候,執行scrollToTarget();產生作用,回復到0位置。  
  252.  
  253. }  
  254.  
  255. private void scrollToTarget() {  
  256. //此函數不明  
  257. int sx = getScrollX();  
  258. if (mTargetScrollX > sx) {   //右移?  
  259. sx += SCROLL_PIXELS;                        //這個函數看不出任何作用,尤其是這兩步加20的,不知有何用意,待問  
  260. if (sx >= mTargetScrollX) {  
  261. sx = mTargetScrollX;  
  262. requestLayout();  
  263. }  
  264. } else {  
  265. sx -= SCROLL_PIXELS;  
  266. if (sx <= mTargetScrollX) {  
  267. sx = mTargetScrollX;  
  268. requestLayout();  
  269. }  
  270. }                                   //就是說,當調用這個函數的時候,最少也要移動20的距離  
  271. sx = mTargetScrollX;  
  272. scrollTo(sx, getScrollY());     //移動之  。 p.s不要把高亮區與候選欄相混,移動的,是候選欄,高亮區自從生成就亘古不變,直到消失  
  273. invalidate();  
  274. }  
  275.  
  276. public void setSuggestions(List<String> suggestions, boolean completions,boolean typedWordValid) {  
  277. //此函數本類中出現就一次,會在別的類中調用,沒有內部調用  
  278. clear();  
  279. if (suggestions != null) {  
  280. mSuggestions = new ArrayList<String>(suggestions);    //新的建議集合字串就是傳過來的這個參數字串。  
  281. }  
  282. mTypedWordValid = typedWordValid;  
  283. //確定此詞是否可用?  
  284. scrollTo(0, 0);                //每當有新的候選詞出現,view就會滑動到初始的位置  
  285. mTargetScrollX = 0;  
  286. // Compute the total width  
  287. onDraw(null);         //onDraw的參數為null的時候,他不再執行super裡面的onDraw  
  288. invalidate();  
  289. requestLayout();  
  290. }  
  291.  
  292. public void clear() {  
  293. mSuggestions = EMPTY_LIST;         //前面定義了,這是一個空數組,將候選詞庫弄為空數組  
  294. mTouchX = OUT_OF_BOUNDS;           //把被觸摸的橫坐標定為一個負數,這樣的話就等於沒觸摸  
  295. mSelectedIndex = -1;         
  296. invalidate();  
  297. }  
  298.  
  299. @Override    
  300. public boolean onTouchEvent(MotionEvent me) {  
  301. //這是觸屏選詞工作  
  302. if (mGestureDetector.onTouchEvent(me)) {  
  303. return true;                             //猜測,如果前面那個滑動監聽函數起了作用,就不用再乎這個函數後面的了,這是對的!  
  304. //文檔中這樣解釋:GestureDetector.OnGestureListener使用的時候,這裡會返回  
  305. //true,後面又說,前面定義的GestureDetector.SimpleOnGestureListener,  
  306. //是GestureDetector.OnGestureListener的派生類  
  307. }                                             //p.s.經注解忽略測試發現:所有的觸摸效果源自這裡。如果注解掉,則不會發生滑動  
  308.  
  309. int action = me.getAction();  
  310. int x = (int) me.getX();  
  311. int y = (int) me.getY();  
  312. mTouchX = x;           //被點擊詞語的橫坐標  
  313.  
  314. switch (action) {                                     //如果後續出現滑動,又會被前面那個監聽到的  
  315. case MotionEvent.ACTION_DOWN:  
  316. mScrolled = false;                                   
  317. invalidate();  
  318. break;  
  319. case MotionEvent.ACTION_MOVE:  
  320. if (y <= 0) {                                       //選詞,經過測試,當向上滑動的時候也是可以選詞的  
  321. // Fling up!?  
  322. if (mSelectedIndex >= 0) {  
  323. mService.pickSuggestionManually(mSelectedIndex);  //mService是傳過來的Softkeyboard類,這個函數應該解讀自別的類  
  324. mSelectedIndex = -1;  
  325. }  
  326. }  
  327. invalidate();  
  328. break;  
  329. case MotionEvent.ACTION_UP:                          
  330. if (!mScrolled) {  
  331. if (mSelectedIndex >= 0) {  
  332. mService.pickSuggestionManually(mSelectedIndex);   //點擊選詞經測試合格  
  333. }  
  334. }  
  335. mSelectedIndex = -1;  
  336. removeHighlight();        //消除高亮區域  
  337. requestLayout();          //文檔:當View作廢時候使用  
  338. break;  
  339. }  
  340. return true;  
  341. }  
  342.  
  343. /**  
  344. * For flick through from keyboard, call this method with the x coordinate of the flick   
  345. * gesture.  
  346. * @param x  
  347. */ 
  348. public void takeSuggestionAt(float x) {      //本類中只出現了一次,在別的類中有調用  
  349. mTouchX = (int) x;                       //此處也給mTouchX賦了非負值  
  350. // To detect candidate  
  351. onDraw(null);  
  352. if (mSelectedIndex >= 0) {  
  353. mService.pickSuggestionManually(mSelectedIndex);  
  354. }  
  355. invalidate();  
  356. }  
  357.  
  358. private void removeHighlight() {  //取消高亮區域的顯示,等待下次生成  
  359. mTouchX = OUT_OF_BOUNDS;         //把被觸摸的橫坐標定為一個負數,這樣的話就等於沒觸摸  
  360. invalidate();  
  361. }  

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved