編輯:Android開發實例
3.CandidateView.java
- /*
- * Copyright (C) 2008-2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.example.android.softkeyboard;
- 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;
- public class CandidateView extends View {
- private static final int OUT_OF_BOUNDS = -1;
- private SoftKeyboard mService; //這個是這個candidateView的宿主類,也就是該view是為什麼輸入法服務的。
- private List<String> mSuggestions; //這個是建議。比如說當我們輸入一些字母之後輸入法希望根據輸入來進行聯想建議。
- private int mSelectedIndex; //這個是用戶選擇的詞的索引。
- private int mTouchX = OUT_OF_BOUNDS;
- private Drawable mSelectionHighlight; //這個是用來描繪選擇區域高亮的一個類
- private boolean mTypedWordValid; //鍵入的word是否合法正確。
- private Rect mBgPadding; //背景填充區域,決定將要在那個部分顯示?
- private static final int MAX_SUGGESTIONS = 32;
- private static final int SCROLL_PIXELS = 20;
- private int[] mWordWidth = new int[MAX_SUGGESTIONS]; //這個是對於候選詞的每個詞的寬度
- private int[] mWordX = new int[MAX_SUGGESTIONS];//這個是每個候選詞的X坐標。
- //有了這兩個變量,就能夠在屏幕上准確的繪制出該候選鍵
- 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);
- mSelectionHighlight = context.getResources().getDrawable(android.R.drawable.list_selector_background);
- //getResouces這個函數用來得到這個應用程序的所有資源,就連android自帶的資源也要如此
- mSelectionHighlight.setState(new int[] {
- //mSelectionHighlight類型是Drawable,而Drawable設置狀態就是這樣
- 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();
- //得到滑動開始的橫坐標
- sx += distanceX; //加上滑動的距離,這個滑動距離是最後一次call滑動之間的距離,很小,應該
- if (sx < 0) {
- sx = 0;
- }
- if (sx + getWidth() > mTotalWidth) {
- sx -= distanceX;
- }
- mTargetScrollX = sx; //記錄將要移動到的位置,後面會用到
- scrollTo(sx, getScrollY()); //這是處理滑動的函數,view類的函數。後面一個參數,說明Y軸永遠不變,如果你嘗試去改變一下,經測試,太好玩了
- invalidate(); //文檔中說的是使得整個VIew作廢,但是如果不用這句,會發生什麼?
- return true;
- }
- });
- setHorizontalFadingEdgeEnabled(true); //這後三行語句不是在GestureDetector函數中的,而是在構造函數中的,當候選View建立成功的時候就已經是下面的狀態了
- //拖動時刻左右兩邊的淡出效果
- setWillNotDraw(false);
- //當拖動的時候,依舊可以輸入並顯示
- setHorizontalScrollBarEnabled(true);
- setVerticalScrollBarEnabled(false);
- //作用暫時不明?
- }//這裡是CandidateView構造函數的完結
- /**
- * A connection back to the service to communicate with the text field
- * @param listener
- */
- public void setService(SoftKeyboard listener) { //自己定義的廢柴函數,使得私有變量mService的值得以改變
- mService = listener;
- }
- @Override
- public int computeHorizontalScrollRange() {
- //給別的類用的廢柴函數
- return mTotalWidth;
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredWidth = resolveSize(50, widthMeasureSpec);
- //猜測:如果第二參數是從onMeasure的參數中來的,就用第二變量
- //這個函數是個調和寬度函數,一般情況下用參數1值,除非第二個參數給其限制
- // Get the desired height of the icon menu view (last row of items does
- // not have a divider below)
- Rect padding = new Rect();
- mSelectionHighlight.getPadding(padding);
- //吳:高亮區域除了字以外,剩下的空隙,用getPadding得到,或許,這是由list_selector_background決定的。
- final int desiredHeight = ((int)mPaint.getTextSize()) + mVerticalPadding+ padding.top + padding.bottom;
- //這裡用來計算整個候選條的應有高度,當然,得考慮字體的高度,這也是重載的原因
- //mVerticalPadding參見第99行的定義,那個是用的系統是獻給定的高度
- // Maximum possible width and desired height
- setMeasuredDimension(measuredWidth,resolveSize(desiredHeight, heightMeasureSpec));
- //設置寬高,這是View類的函數。整個候選欄的大小都是由這個語句決定的
- }
- /**
- * If the canvas is null, then only touch calculations are performed to pick the target
- * candidate.
- */
- @Override
- //這是每個View對象繪制自己的函數,重載之。經測試:沒有這個函數的重載,則顯示不出字來,這個就是用來顯示字條
- protected void onDraw(Canvas canvas) {
- if (canvas != null) {
- super.onDraw(canvas);
- }
- mTotalWidth = 0;
- if (mSuggestions == null) return;
- if (mBgPadding == null) {
- //mBgPadding,Rect類對象
- mBgPadding = new Rect(0, 0, 0, 0);
- if (getBackground() != null) {
- getBackground().getPadding(mBgPadding);
- //這個View的背景,也就是整個候選區域的背景,一個Drawable類型,這跟高亮區有什麼關系?
- }
- }
- int x = 0; //第一個詞左側為0,測試知道:這個地方能改變文字的左側開端
- final int count = mSuggestions.size();
- final int height = getHeight();
- final Rect bgPadding = mBgPadding;
- final Paint paint = mPaint; //paint的功效就和畫筆一樣
- 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); //至今還沒發現什麼是不可得詞匯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;
- if (mTargetScrollX != getScrollX()) { //每個滑動,都會造成mTargetScrollX改變,因為他在動作監聽函數裡面賦值
- //意思是說:只要移動了。難道是說如果在移動完成之後進行的輸入,則進行下面操作?
- //如果在移動完成之後輸入,那麼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; //這個函數看不出任何作用,尤其是這兩步加20的,不知有何用意,待問
- if (sx >= mTargetScrollX) {
- sx = mTargetScrollX;
- requestLayout();
- }
- } else {
- sx -= SCROLL_PIXELS;
- if (sx <= mTargetScrollX) {
- sx = mTargetScrollX;
- requestLayout();
- }
- } //就是說,當調用這個函數的時候,最少也要移動20的距離
- sx = mTargetScrollX;
- scrollTo(sx, getScrollY()); //移動之 。 p.s不要把高亮區與候選欄相混,移動的,是候選欄,高亮區自從生成就亘古不變,直到消失
- invalidate();
- }
- public void setSuggestions(List<String> suggestions, boolean completions,boolean typedWordValid) {
- //此函數本類中出現就一次,會在別的類中調用,沒有內部調用
- clear();
- if (suggestions != null) {
- mSuggestions = new ArrayList<String>(suggestions); //新的建議集合字串就是傳過來的這個參數字串。
- }
- mTypedWordValid = typedWordValid;
- //確定此詞是否可用?
- scrollTo(0, 0); //每當有新的候選詞出現,view就會滑動到初始的位置
- mTargetScrollX = 0;
- // Compute the total width
- onDraw(null); //onDraw的參數為null的時候,他不再執行super裡面的onDraw
- invalidate();
- requestLayout();
- }
- public void clear() {
- mSuggestions = EMPTY_LIST; //前面定義了,這是一個空數組,將候選詞庫弄為空數組
- mTouchX = OUT_OF_BOUNDS; //把被觸摸的橫坐標定為一個負數,這樣的話就等於沒觸摸
- mSelectedIndex = -1;
- invalidate();
- }
- @Override
- public boolean onTouchEvent(MotionEvent me) {
- //這是觸屏選詞工作
- if (mGestureDetector.onTouchEvent(me)) {
- return true; //猜測,如果前面那個滑動監聽函數起了作用,就不用再乎這個函數後面的了,這是對的!
- //文檔中這樣解釋:GestureDetector.OnGestureListener使用的時候,這裡會返回
- //true,後面又說,前面定義的GestureDetector.SimpleOnGestureListener,
- //是GestureDetector.OnGestureListener的派生類
- } //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); //mService是傳過來的Softkeyboard類,這個函數應該解讀自別的類
- 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.
- * @param x
- */
- public void takeSuggestionAt(float x) { //本類中只出現了一次,在別的類中有調用
- mTouchX = (int) x; //此處也給mTouchX賦了非負值
- // To detect candidate
- onDraw(null);
- if (mSelectedIndex >= 0) {
- mService.pickSuggestionManually(mSelectedIndex);
- }
- invalidate();
- }
- private void removeHighlight() { //取消高亮區域的顯示,等待下次生成
- mTouchX = OUT_OF_BOUNDS; //把被觸摸的橫坐標定為一個負數,這樣的話就等於沒觸摸
- invalidate();
- }
- }
JSON代表JavaScript對象符號。它是一個獨立的數據交換格式,是XML的最佳替代品。本章介紹了如何解析JSON文件,並從中提取所需的信息。Android提供了四個
本文概述: 滑動解鎖九宮格的分析: 1、需要自定義控件; 2、需要重寫事件onTouchEvent(); 3、需要給九個點設置序號和坐標,這裡用
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我