为了自己学习方便,对Android的部分源码原样粘贴在这里,然后自己编辑,用中文注释了一下。不是原创代码。
/* * 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 android.inputmethodservice;import org.xmlpull.v1.XmlPullParserException;import android.content.Context;import android.content.res.Resources;import android.content.res.TypedArray;import android.content.res.XmlResourceParser;import android.graphics.drawable.Drawable;import android.text.TextUtils;import android.util.Log;import android.util.TypedValue;import android.util.Xml;import android.util.DisplayMetrics;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.StringTokenizer;/** * class Keyboard:该类的作用就是 加载一个描述键盘的XML文件、储存其中键的属性。 * Loads an XML description of a keyboard and stores the attributes of the keys.  * A keyboard consists of rows of keys. * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p> * <pre> * <Keyboard *         android:keyWidth="%10p" *         android:keyHeight="50px" *         android:horizontalGap="2px" *         android:verticalGap="2px" > *     <Row android:keyWidth="32px" > *         <Key android:keyLabel="A" /> *         ... *     </Row> *     ... * </Keyboard> * </pre> * @attr ref android.R.styleable#Keyboard_keyWidth * @attr ref android.R.styleable#Keyboard_keyHeight * @attr ref android.R.styleable#Keyboard_horizontalGap * @attr ref android.R.styleable#Keyboard_verticalGap */public class Keyboard {    static final String TAG = "Keyboard";        // Keyboard XML Tags(XML的标签,常量)    private static final String TAG_KEYBOARD = "Keyboard";    private static final String TAG_ROW = "Row";    private static final String TAG_KEY = "Key";    public static final int EDGE_LEFT = 0x01;    public static final int EDGE_RIGHT = 0x02;    public static final int EDGE_TOP = 0x04;    public static final int EDGE_BOTTOM = 0x08;    public static final int KEYCODE_SHIFT = -1;    public static final int KEYCODE_MODE_CHANGE = -2;    public static final int KEYCODE_CANCEL = -3;    public static final int KEYCODE_DONE = -4;    public static final int KEYCODE_DELETE = -5;    public static final int KEYCODE_ALT = -6;        /** Keyboard label(键的标签,UI上显示的字符,变量) **/    private CharSequence mLabel;    /** Horizontal gap default for all rows */    private int mDefaultHorizontalGap;        /** Default key width */    private int mDefaultWidth;    /** Default key height */    private int mDefaultHeight;    /** Default gap between rows */    private int mDefaultVerticalGap;    /** Is the keyboard in the shifted state */    private boolean mShifted;        /** Key instance for the shift key, if present */    private Key[] mShiftKeys = { null, null };    /** Key index for the shift key, if present */    private int[] mShiftKeyIndices = {-1, -1};    /** Current key width, while loading the keyboard */    private int mKeyWidth;        /** Current key height, while loading the keyboard */    private int mKeyHeight;        /** Total height of the keyboard, including the padding and keys */    private int mTotalHeight;        /**      * Total width of the keyboard, including left side gaps and keys, but not any gaps on the     * right side.     */    private int mTotalWidth;        /** List of keys in this keyboard */    private List<Key> mKeys;        /** List of modifier keys such as Shift & Alt, if any */    private List<Key> mModifierKeys;        /** Width of the screen available to fit the keyboard */    private int mDisplayWidth;    /** Height of the screen */    private int mDisplayHeight;    /** Keyboard mode, or zero, if none.  */    private int mKeyboardMode;    // Variables for pre-computing nearest keys.        private static final int GRID_WIDTH = 10;    private static final int GRID_HEIGHT = 5;    private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;    private int mCellWidth;    private int mCellHeight;    private int[][] mGridNeighbors;    private int mProximityThreshold;    /** Number of key widths from current touch point to search for nearest keys. */    private static float SEARCH_DISTANCE = 1.8f;    private ArrayList<Row> rows = new ArrayList<Row>();    /**     * class Row :定义每一行键的特征     * Container for keys in the keyboard.(键盘中键的容器) All keys in a row are at the same Y-coordinate.     * Some of the key size defaults can be overridden per row from what the {@link Keyboard}defines.      * @attr ref android.R.styleable#Keyboard_keyWidth     * @attr ref android.R.styleable#Keyboard_keyHeight     * @attr ref android.R.styleable#Keyboard_horizontalGap     * @attr ref android.R.styleable#Keyboard_verticalGap     * @attr ref android.R.styleable#Keyboard_Row_rowEdgeFlags     * @attr ref android.R.styleable#Keyboard_Row_keyboardMode     */    public static class Row {        /** Default width of a key in this row. */        public int defaultWidth;        /** Default height of a key in this row. */        public int defaultHeight;        /** Default horizontal gap between keys in this row. */        public int defaultHorizontalGap;        /** Vertical gap following this row. */        public int verticalGap;        ArrayList<Key> mKeys = new ArrayList<Key>();        /**         * Edge flags for this row of keys. Possible values that can be assigned are         * {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}           */        public int rowEdgeFlags;                /** The keyboard mode for this row */        public int mode;                private Keyboard parent;        public Row(Keyboard parent) {            this.parent = parent;        }                public Row(Resources res, Keyboard parent, XmlResourceParser parser) {//Xml资源解析器            this.parent = parent;            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),                     com.android.internal.R.styleable.Keyboard);            defaultWidth = getDimensionOrFraction(a,                     com.android.internal.R.styleable.Keyboard_keyWidth,                     parent.mDisplayWidth, parent.mDefaultWidth);            defaultHeight = getDimensionOrFraction(a,                     com.android.internal.R.styleable.Keyboard_keyHeight,                     parent.mDisplayHeight, parent.mDefaultHeight);            defaultHorizontalGap = getDimensionOrFraction(a,                    com.android.internal.R.styleable.Keyboard_horizontalGap,                     parent.mDisplayWidth, parent.mDefaultHorizontalGap);            verticalGap = getDimensionOrFraction(a,                     com.android.internal.R.styleable.Keyboard_verticalGap,                     parent.mDisplayHeight, parent.mDefaultVerticalGap);            a.recycle();            a = res.obtainAttributes(Xml.asAttributeSet(parser),                    com.android.internal.R.styleable.Keyboard_Row);            rowEdgeFlags = a.getInt(com.android.internal.R.styleable.Keyboard_Row_rowEdgeFlags, 0);            mode = a.getResourceId(com.android.internal.R.styleable.Keyboard_Row_keyboardMode,                    0);        }    }    /**     * class Key:定义单个键的位置和特征     * Class for describing the position and characteristics of a single key in the keyboard.     * @attr ref android.R.styleable#Keyboard_keyWidth     * @attr ref android.R.styleable#Keyboard_keyHeight     * @attr ref android.R.styleable#Keyboard_horizontalGap     * @attr ref android.R.styleable#Keyboard_Key_codes     * @attr ref android.R.styleable#Keyboard_Key_keyIcon     * @attr ref android.R.styleable#Keyboard_Key_keyLabel     * @attr ref android.R.styleable#Keyboard_Key_iconPreview     * @attr ref android.R.styleable#Keyboard_Key_isSticky     * @attr ref android.R.styleable#Keyboard_Key_isRepeatable     * @attr ref android.R.styleable#Keyboard_Key_isModifier     * @attr ref android.R.styleable#Keyboard_Key_popupKeyboard     * @attr ref android.R.styleable#Keyboard_Key_popupCharacters     * @attr ref android.R.styleable#Keyboard_Key_keyOutputText     * @attr ref android.R.styleable#Keyboard_Key_keyEdgeFlags     */    public static class Key {        /**          * 这个键的所有键值(code)都应该生成,第0个是总重要的。         * All the key codes (unicode or custom code) that this key could generate,          * zero'th being the most important.         */        public int[] codes;                /** Label to display(每个键显示的Label) */        public CharSequence label;                /** Icon to display instead of a label. Icon takes precedence(优先) over a label */        public Drawable icon;        /** Preview version of the icon, for the preview popup(预览弹出框) */        public Drawable iconPreview;        /** Width of the key, not including the gap */        public int width;        /** Height of the key, not including the gap */        public int height;        /** The horizontal gap before this key */        public int gap;        /** Whether this key is sticky(粘滞键), i.e., a toggle key */        public boolean sticky;        /** X coordinate of the key in the keyboard layout */        public int x;        /** Y coordinate of the key in the keyboard layout */        public int y;        /** The current pressed state of this key */        public boolean pressed;        /** If this is a sticky key, is it on? 如果是粘滞键,on应该是粘滞状态*/        public boolean on;        /** Text to output when pressed. This can be multiple characters, like ".com" */        public CharSequence text;        /** Popup characters */        public CharSequence popupCharacters;        /**          * 标志(标志寄存器)(bit mask 位掩码),明确的指出键的边界,以便检测出超出键边界的触摸事件。         * Flags that specify the anchoring to edges of the keyboard for detecting touch events         * that are just out of the boundary of the key. This is a bit mask of          * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, {@link Keyboard#EDGE_TOP} and         * {@link Keyboard#EDGE_BOTTOM}.         */        public int edgeFlags;//(0000 1111:表示在按键范围内)        /** Whether this is a modifier key(辅助按键,修改其他键的功能), such as Shift or Alt */        public boolean modifier;        /** The keyboard that this key belongs to */        private Keyboard keyboard;        /**          * If this key pops up a mini keyboard, this is the resource id for the XML layout for that         * keyboard.         */        public int popupResId;        /** Whether this key repeats(重复) itself when held down(按下) */        public boolean repeatable;        //定义各种KEY_STATE所包含的按键的基本状态        private final static int[] KEY_STATE_NORMAL_ON = {             android.R.attr.state_checkable,             android.R.attr.state_checked        };                private final static int[] KEY_STATE_PRESSED_ON = {             android.R.attr.state_pressed,             android.R.attr.state_checkable,             android.R.attr.state_checked         };                private final static int[] KEY_STATE_NORMAL_OFF = {             android.R.attr.state_checkable         };                private final static int[] KEY_STATE_PRESSED_OFF = {             android.R.attr.state_pressed,             android.R.attr.state_checkable         };                private final static int[] KEY_STATE_NORMAL = {        };                private final static int[] KEY_STATE_PRESSED = {            android.R.attr.state_pressed        };        /** Create an empty key with no attributes. */        public Key(Row parent) {            keyboard = parent.parent;            height = parent.defaultHeight;            width = parent.defaultWidth;            gap = parent.defaultHorizontalGap;            edgeFlags = parent.rowEdgeFlags;        }                /** Create a key with the given top-left(左上角) coordinate and extract(获取;提取;提拔)         *  its attributes from the XML parser.         * @param res resources associated with the caller's context(与调用者的Context相关的资源)         * @param parent the row that this key belongs to. The row must already be attached to(联在一起)         * a {@link Keyboard}.(必须是在键盘中定义了的行)         * @param x the x coordinate of the top-left         * @param y the y coordinate of the top-left         * @param parser the XML parser containing the attributes for this key         */        public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser) {            this(parent);            this.x = x;            this.y = y;                        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),                     com.android.internal.R.styleable.Keyboard);            width = getDimensionOrFraction(a,                     com.android.internal.R.styleable.Keyboard_keyWidth,                    keyboard.mDisplayWidth, parent.defaultWidth);            height = getDimensionOrFraction(a,                     com.android.internal.R.styleable.Keyboard_keyHeight,                    keyboard.mDisplayHeight, parent.defaultHeight);            gap = getDimensionOrFraction(a,                     com.android.internal.R.styleable.Keyboard_horizontalGap,                    keyboard.mDisplayWidth, parent.defaultHorizontalGap);            a.recycle();            a = res.obtainAttributes(Xml.asAttributeSet(parser),                    com.android.internal.R.styleable.Keyboard_Key);            this.x += gap;            TypedValue codesValue = new TypedValue();//键的code的类型            a.getValue(com.android.internal.R.styleable.Keyboard_Key_codes,                     codesValue);//Returns true if the value was retrieved, else false.            if (codesValue.type == TypedValue.TYPE_INT_DEC || codesValue.type == TypedValue.TYPE_INT_HEX)             {                codes = new int[] { codesValue.data };            }             else if (codesValue.type == TypedValue.TYPE_STRING)             {                codes = parseCSV(codesValue.string.toString());            }                        iconPreview = a.getDrawable(com.android.internal.R.styleable.Keyboard_Key_iconPreview);            if (iconPreview != null) {                iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),                         iconPreview.getIntrinsicHeight());//Return the intrinsic height of the//underlying drawable object. Returns -1 if it has no intrinsic height, such as with a solid color.            }            popupCharacters = a.getText(                    com.android.internal.R.styleable.Keyboard_Key_popupCharacters);            popupResId = a.getResourceId(                    com.android.internal.R.styleable.Keyboard_Key_popupKeyboard, 0);            repeatable = a.getBoolean(                    com.android.internal.R.styleable.Keyboard_Key_isRepeatable, false);            modifier = a.getBoolean(                    com.android.internal.R.styleable.Keyboard_Key_isModifier, false);            sticky = a.getBoolean(                    com.android.internal.R.styleable.Keyboard_Key_isSticky, false);            edgeFlags = a.getInt(com.android.internal.R.styleable.Keyboard_Key_keyEdgeFlags, 0);//Return attribute int value, or defValue if not defined.            edgeFlags |= parent.rowEdgeFlags;//a|=b等价于a=a|b            icon = a.getDrawable(                    com.android.internal.R.styleable.Keyboard_Key_keyIcon);            if (icon != null) {                icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());            }            label = a.getText(com.android.internal.R.styleable.Keyboard_Key_keyLabel);//CharSequence holding string data. May be styled. Returns null if the attribute is not defined.            text = a.getText(com.android.internal.R.styleable.Keyboard_Key_keyOutputText);                        if (codes == null && !TextUtils.isEmpty(label)) {//Returns true if the string(label) is null or 0-length.                codes = new int[] { label.charAt(0) };//Returns the character at the specified index, with the first character having index zero.            }            a.recycle();        }                /**         * Informs the key that it has been pressed, in case it needs to change its appearance or         * state.         * @see #onReleased(boolean)         */        public void onPressed() {            pressed = !pressed;        }                /**         * Changes the pressed state of the key. If it is a sticky key, it will also change the         * toggled state of the key if the finger was release inside.         * @param inside whether the finger was released inside the key         * @see #onPressed()         */        public void onReleased(boolean inside) {            pressed = !pressed;//释放,非按下状态            if (sticky) {                on = !on;//(粘滞键)弹起状态(!on)            }        }**//解析CSV,暂时没看懂。**        int[] parseCSV(String value) {            int count = 0;            int lastIndex = 0;            if (value.length() > 0) {                count++;                                /**                * 方法:int java.lang.String.indexOf(String subString, int start)* 源码:附在本文的最后面(附码1)* 说明:Searches in this string(value) for the index of the specified string. * The search for the string starts at the specified offset and moves * towards the end of this string.* 这里就是在value中,从第lastIndex + 1字符开始,向后查找“,”,查到一个count++。* value.indexOf(",", lastIndex + 1)在查找结束的时候,返回-1。* 最终的count = ","的个数 + 1.*/                while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {                    count++;                }            }            int[] values = new int[count];            count = 0;            //Constructs a new StringTokenizer(分词器) for the parameter string(字符串参数:value) using the specified delimiters(分隔符:",").            StringTokenizer st = new StringTokenizer(value, ",");            while (st.hasMoreTokens()) {//Returns true if unprocessed tokens remain.                try {                //st.nextToken(): Returns the next token in the string as a String.                //Integer.parseInt(st.nextToken()): Parses the specified string as a signed decimal integer(有符十进制整数) value. The ASCII character \u002d ('-') is recognized as the minus sign.                    values[count++] = Integer.parseInt(st.nextToken());                } catch (NumberFormatException nfe) {                    Log.e(TAG, "Error parsing keycodes " + value);                }            }            return values;        }        /**         * Detects if a point falls inside this key.         * @param x the x-coordinate of the point          * @param y the y-coordinate of the point         * @return whether or not the point falls inside the key. If the key is attached to an edge,         * it will assume that all points between the key and the edge are considered to be inside         * the key.         */        public boolean isInside(int x, int y) {            boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;            boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;            boolean topEdge = (edgeFlags & EDGE_TOP) > 0;            boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;            if ((x >= this.x || (leftEdge && x <= this.x + this.width))                     && (x < this.x + this.width || (rightEdge && x >= this.x))                     && (y >= this.y || (topEdge && y <= this.y + this.height))                    && (y < this.y + this.height || (bottomEdge && y >= this.y))) {                return true;            } else {                return false;            }        }        /**         * Returns the square of the distance(距离的平方) between the center of the key         *  and the given point.         * @param x the x-coordinate of the point         * @param y the y-coordinate of the point         * @return the square of the distance of the point from the center of the key         */        public int squaredDistanceFrom(int x, int y) {            int xDist = this.x + width / 2 - x;            int yDist = this.y + height / 2 - y;            return xDist * xDist + yDist * yDist;        }                /**         * Returns the drawable state for the key, based on the current state and type of the key.         * @return the drawable state of the key.         * @see android.graphics.drawable.StateListDrawable#setState(int[])         */        public int[] getCurrentDrawableState() {            int[] states = KEY_STATE_NORMAL;            if (on) {//粘滞键处于粘滞状态                if (pressed) {                    states = KEY_STATE_PRESSED_ON;                } else {                    states = KEY_STATE_NORMAL_ON;                }            } else {                if (sticky) {                    if (pressed) {                        states = KEY_STATE_PRESSED_OFF;                    } else {                        states = KEY_STATE_NORMAL_OFF;                    }                } else {                    if (pressed) {                        states = KEY_STATE_PRESSED;                    }                }            }            return states;        }    }    /**     * Creates a keyboard from the given xml key layout file.     * @param context the application or service context     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.     */    public Keyboard(Context context, int xmlLayoutResId) {        this(context, xmlLayoutResId, 0);    }    /**     * Creates a keyboard from the given xml key layout file. Weeds out rows     * that have a keyboard mode defined but don't match the specified mode.     * @param context the application or service context     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.     * @param modeId keyboard mode identifier     * @param width sets width of keyboard     * @param height sets height of keyboard     */    public Keyboard(Context context, int xmlLayoutResId, int modeId, int width, int height) {        mDisplayWidth = width;        mDisplayHeight = height;        mDefaultHorizontalGap = 0;        mDefaultWidth = mDisplayWidth / 10;        mDefaultVerticalGap = 0;        mDefaultHeight = mDefaultWidth;        mKeys = new ArrayList<Key>();        mModifierKeys = new ArrayList<Key>();        mKeyboardMode = modeId;        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));    }    /**     * Creates a keyboard from the given xml key layout file. Weeds out rows     * that have a keyboard mode defined but don't match the specified mode.      * @param context the application or service context     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.     * @param modeId keyboard mode identifier     */    public Keyboard(Context context, int xmlLayoutResId, int modeId) {        DisplayMetrics dm = context.getResources().getDisplayMetrics();        mDisplayWidth = dm.widthPixels;        mDisplayHeight = dm.heightPixels;        //Log.v(TAG, "keyboard's display metrics:" + dm);        mDefaultHorizontalGap = 0;        mDefaultWidth = mDisplayWidth / 10;        mDefaultVerticalGap = 0;        mDefaultHeight = mDefaultWidth;        mKeys = new ArrayList<Key>();        mModifierKeys = new ArrayList<Key>();        mKeyboardMode = modeId;        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));    }    /**     * <p>Creates a blank keyboard from the given resource file and      * populates(定位,安置) it with the specified characters in left-to-right, top-to-bottom fashion,     * using the specified number of columns.     * </p>     * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as     * possible in each row.</p>     * @param context the application or service context     * @param layoutTemplateResId the layout template(模板) file, containing no keys.     * @param characters the list of characters(需要显示的字符) to display on the keyboard. One key will be created     * for each character.     * @param columns the number of columns of keys to display. If this number is greater than the      * number of keys that can fit in a row, it will be ignored. If this number is -1, the      * keyboard will fit as many keys as possible in each row.     */    public Keyboard(Context context, int layoutTemplateResId,             CharSequence characters, int columns, int horizontalPadding) {        this(context, layoutTemplateResId);        int x = 0;        int y = 0;        int column = 0;        mTotalWidth = 0;                Row row = new Row(this);        row.defaultHeight = mDefaultHeight;        row.defaultWidth = mDefaultWidth;        row.defaultHorizontalGap = mDefaultHorizontalGap;        row.verticalGap = mDefaultVerticalGap;        row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;        for (int i = 0; i < characters.length(); i++) {            char c = characters.charAt(i);            if (column >= maxColumns                     || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {                x = 0;                y += mDefaultVerticalGap + mDefaultHeight;                column = 0;            }            final Key key = new Key(row);            key.x = x;            key.y = y;            key.label = String.valueOf(c);            key.codes = new int[] { c };            column++;            x += key.width + key.gap;            mKeys.add(key);            row.mKeys.add(key);            if (x > mTotalWidth) {                mTotalWidth = x;            }        }        mTotalHeight = y + mDefaultHeight;        rows.add(row);    }    final void resize(int newWidth, int newHeight) {        int numRows = rows.size();        for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {            Row row = rows.get(rowIndex);            int numKeys = row.mKeys.size();            int totalGap = 0;            int totalWidth = 0;            for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {                Key key = row.mKeys.get(keyIndex);                if (keyIndex > 0) {                    totalGap += key.gap;                }                totalWidth += key.width;            }            if (totalGap + totalWidth > newWidth) {                int x = 0;                float scaleFactor = (float)(newWidth - totalGap) / totalWidth;                for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {                    Key key = row.mKeys.get(keyIndex);                    key.width *= scaleFactor;                    key.x = x;                    x += key.width + key.gap;                }            }        }        mTotalWidth = newWidth;        // TODO: This does not adjust the vertical placement according to the new size.        // The main problem in the previous code was horizontal placement/size, but we should        // also recalculate the vertical sizes/positions when we get this resize call.    }        public List<Key> getKeys() {        return mKeys;    }        public List<Key> getModifierKeys() {        return mModifierKeys;    }        protected int getHorizontalGap() {        return mDefaultHorizontalGap;    }        protected void setHorizontalGap(int gap) {        mDefaultHorizontalGap = gap;    }    protected int getVerticalGap() {        return mDefaultVerticalGap;    }    protected void setVerticalGap(int gap) {        mDefaultVerticalGap = gap;    }    protected int getKeyHeight() {        return mDefaultHeight;    }    protected void setKeyHeight(int height) {        mDefaultHeight = height;    }    protected int getKeyWidth() {        return mDefaultWidth;    }        protected void setKeyWidth(int width) {        mDefaultWidth = width;    }    /**     * Returns the total height of the keyboard     * @return the total height of the keyboard     */    public int getHeight() {        return mTotalHeight;    }        public int getMinWidth() {        return mTotalWidth;    }    public boolean setShifted(boolean shiftState) {        for (Key shiftKey : mShiftKeys) {            if (shiftKey != null) {                shiftKey.on = shiftState;            }        }        if (mShifted != shiftState) {            mShifted = shiftState;            return true;        }        return false;    }    public boolean isShifted() {        return mShifted;    }    /**     * @hide     */    public int[] getShiftKeyIndices() {        return mShiftKeyIndices;    }    public int getShiftKeyIndex() {        return mShiftKeyIndices[0];    }        private void computeNearestNeighbors() {        // Round-up so we don't have any pixels outside the grid        mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;        mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;        mGridNeighbors = new int[GRID_SIZE][];        int[] indices = new int[mKeys.size()];        final int gridWidth = GRID_WIDTH * mCellWidth;        final int gridHeight = GRID_HEIGHT * mCellHeight;        for (int x = 0; x < gridWidth; x += mCellWidth) {            for (int y = 0; y < gridHeight; y += mCellHeight) {                int count = 0;                for (int i = 0; i < mKeys.size(); i++) {                    final Key key = mKeys.get(i);                    if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||                            key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||                            key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)                                 < mProximityThreshold ||                            key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {                        indices[count++] = i;                    }                }                int [] cell = new int[count];                System.arraycopy(indices, 0, cell, 0, count);                mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;            }        }    }        /**     * Returns the indices of the keys that are closest to the given point.     * @param x the x-coordinate of the point     * @param y the y-coordinate of the point     * @return the array of integer indices for the nearest keys to the given point. If the given     * point is out of range, then an array of size zero is returned.     */    public int[] getNearestKeys(int x, int y) {        if (mGridNeighbors == null) computeNearestNeighbors();        if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {            int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);            if (index < GRID_SIZE) {                return mGridNeighbors[index];            }        }        return new int[0];    }    protected Row createRowFromXml(Resources res, XmlResourceParser parser) {        return new Row(res, this, parser);    }        protected Key createKeyFromXml(Resources res, Row parent, int x, int y,             XmlResourceParser parser) {        return new Key(res, parent, x, y, parser);    }    private void loadKeyboard(Context context, XmlResourceParser parser) {        boolean inKey = false;        boolean inRow = false;        boolean leftMostKey = false;        int row = 0;        int x = 0;        int y = 0;        Key key = null;        Row currentRow = null;        Resources res = context.getResources();        boolean skipRow = false;        try {            int event;            while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {                if (event == XmlResourceParser.START_TAG) {                    String tag = parser.getName();                    if (TAG_ROW.equals(tag)) {                        inRow = true;                        x = 0;                        currentRow = createRowFromXml(res, parser);                        rows.add(currentRow);                        skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;                        if (skipRow) {                            skipToEndOfRow(parser);                            inRow = false;                        }                   } else if (TAG_KEY.equals(tag)) {                        inKey = true;                        key = createKeyFromXml(res, currentRow, x, y, parser);                        mKeys.add(key);                        if (key.codes[0] == KEYCODE_SHIFT) {                            // Find available shift key slot(位置) and put this shift key in it                            for (int i = 0; i < mShiftKeys.length; i++) {                                if (mShiftKeys[i] == null) {                                    mShiftKeys[i] = key;                                    mShiftKeyIndices[i] = mKeys.size()-1;                                    break;                                }                            }                            mModifierKeys.add(key);                        } else if (key.codes[0] == KEYCODE_ALT) {                            mModifierKeys.add(key);                        }                        currentRow.mKeys.add(key);                    } else if (TAG_KEYBOARD.equals(tag)) {                        parseKeyboardAttributes(res, parser);                    }                } else if (event == XmlResourceParser.END_TAG) {                    if (inKey) {                        inKey = false;                        x += key.gap + key.width;                        if (x > mTotalWidth) {                            mTotalWidth = x;                        }                    } else if (inRow) {                        inRow = false;                        y += currentRow.verticalGap;                        y += currentRow.defaultHeight;                        row++;                    } else {                        // TODO: error or extend?                    }                }            }        } catch (Exception e) {            Log.e(TAG, "Parse error:" + e);            e.printStackTrace();        }        mTotalHeight = y - mDefaultVerticalGap;    }    private void skipToEndOfRow(XmlResourceParser parser)             throws XmlPullParserException, IOException {        int event;        while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {            if (event == XmlResourceParser.END_TAG                     && parser.getName().equals(TAG_ROW)) {                break;            }        }    }        private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),                 com.android.internal.R.styleable.Keyboard);        mDefaultWidth = getDimensionOrFraction(a,                com.android.internal.R.styleable.Keyboard_keyWidth,                mDisplayWidth, mDisplayWidth / 10);        mDefaultHeight = getDimensionOrFraction(a,                com.android.internal.R.styleable.Keyboard_keyHeight,                mDisplayHeight, 50);        mDefaultHorizontalGap = getDimensionOrFraction(a,                com.android.internal.R.styleable.Keyboard_horizontalGap,                mDisplayWidth, 0);        mDefaultVerticalGap = getDimensionOrFraction(a,                com.android.internal.R.styleable.Keyboard_verticalGap,                mDisplayHeight, 0);        mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);        mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison        a.recycle();    }        static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {        TypedValue value = a.peekValue(index);        if (value == null) return defValue;        if (value.type == TypedValue.TYPE_DIMENSION) {            return a.getDimensionPixelOffset(index, defValue);        } else if (value.type == TypedValue.TYPE_FRACTION) {            // Round it to avoid values like 47.9999 from getting truncated            return Math.round(a.getFraction(index, base, base, defValue));        }        return defValue;    }}


附码1:package java.lang.String.indexOf(String subString, int start)

    /**     * Searches in this string for the index of the specified string. The search     * for the string starts at the specified offset and moves towards the end     * of this string.     *     * @param subString     *            the string to find.     * @param start     *            the starting offset.     * @return the index of the first character of the specified string in this     *         string, -1 if the specified string is not a substring.     * @throws NullPointerException     *             if {@code subString} is {@code null}.     */    public int indexOf(String subString, int start) {        if (start < 0) {            start = 0;        }        int subCount = subString.count;        int _count = count;        if (subCount > 0) {            if (subCount + start > _count) {                return -1;            }            char[] target = subString.value;            int subOffset = subString.offset;            char firstChar = target[subOffset];            int end = subOffset + subCount;            while (true) {                int i = indexOf(firstChar, start);                if (i == -1 || subCount + i > _count) {                    return -1; // handles subCount > count || start >= count                }                int o1 = offset + i, o2 = subOffset;                char[] _value = value;                while (++o2 < end && _value[++o1] == target[o2]) {                    // Intentionally empty                }                if (o2 == end) {                    return i;                }                start = i + 1;            }        }        return start < _count ? start : _count;    }


附码2:package java.util;

/* *  Licensed to the Apache Software Foundation (ASF) under one or more *  contributor license agreements.  See the NOTICE file distributed with *  this work for additional information regarding copyright ownership. *  The ASF licenses this file to You 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 java.util;/** * Breaks a string into tokens; new code should probably use {@link String#split}. * * <blockquote> * <pre> * // Legacy code: * StringTokenizer st = new StringTokenizer("a:b:c", ":"); * while (st.hasMoreTokens()) { *     System.err.println(st.nextToken()); * } * * // New code: * for (String token : "a:b:c".split(":")) { *     System.err.println(token); * } * </pre> * </blockquote> * * @since 1.0 */public class StringTokenizer implements Enumeration<Object> {    private String string;    private String delimiters;    private boolean returnDelimiters;    private int position;    /**     * Constructs a new {@code StringTokenizer} for the parameter string using     * whitespace as the delimiter. The {@code returnDelimiters} flag is set to     * {@code false}.     *     * @param string     *            the string to be tokenized.     */    public StringTokenizer(String string) {        this(string, " \t\n\r\f", false);    }    /**     * Constructs a new {@code StringTokenizer} for the parameter string using     * the specified delimiters. The {@code returnDelimiters} flag is set to     * {@code false}. If {@code delimiters} is {@code null}, this constructor     * doesn't throw an {@code Exception}, but later calls to some methods might     * throw a {@code NullPointerException}.     *     * @param string     *            the string to be tokenized.     * @param delimiters     *            the delimiters to use.(分隔符们:分隔符组成的字符串,例如:" \t\n\r\f")     */    public StringTokenizer(String string, String delimiters) {        this(string, delimiters, false);//false:分隔符本身不放入tokens中    }    /**     * Constructs a new {@code StringTokenizer} for the parameter string using     * the specified delimiters, returning the delimiters as tokens if the     * parameter {@code returnDelimiters} is {@code true}. If {@code delimiters}     * is null this constructor doesn't throw an {@code Exception}, but later     * calls to some methods might throw a {@code NullPointerException}.     *     * @param string     *            the string to be tokenized.     * @param delimiters     *            the delimiters to use.     * @param returnDelimiters     *            {@code true} to return each delimiter as a token.     */    public StringTokenizer(String string, String delimiters,            boolean returnDelimiters) {        if (string != null) {            this.string = string;            this.delimiters = delimiters;            this.returnDelimiters = returnDelimiters;            this.position = 0;        } else            throw new NullPointerException();    }    /**     * Returns the number of unprocessed tokens remaining in the string.     *     * @return number of tokens that can be retreived before an {@code     *         Exception} will result from a call to {@code nextToken()}.     */    public int countTokens() {        int count = 0;        boolean inToken = false;        for (int i = position, length = string.length(); i < length; i++) {            if (delimiters.indexOf(string.charAt(i), 0) >= 0) {                if (returnDelimiters)                    count++;                if (inToken) {                    count++;                    inToken = false;                }            } else {                inToken = true;            }        }        if (inToken)            count++;        return count;    }    /**     * Returns {@code true} if unprocessed tokens remain. This method is     * implemented in order to satisfy the {@code Enumeration} interface.     *     * @return {@code true} if unprocessed tokens remain.     */    public boolean hasMoreElements() {        return hasMoreTokens();    }    /**     * Returns {@code true} if unprocessed tokens remain.     *     * @return {@code true} if unprocessed tokens remain.     */    public boolean hasMoreTokens() {        if (delimiters == null) {            throw new NullPointerException();        }        int length = string.length();        if (position < length) {            if (returnDelimiters)                return true; // there is at least one character and even if            // it is a delimiter it is a token            // otherwise find a character which is not a delimiter            for (int i = position; i < length; i++)                if (delimiters.indexOf(string.charAt(i), 0) == -1)                    return true;        }        return false;    }    /**     * Returns the next token in the string as an {@code Object}. This method is     * implemented in order to satisfy the {@code Enumeration} interface.     *     * @return next token in the string as an {@code Object}     * @throws NoSuchElementException     *                if no tokens remain.     */    public Object nextElement() {        return nextToken();    }    /**     * Returns the next token in the string as a {@code String}.     *     * @return next token in the string as a {@code String}.     * @throws NoSuchElementException     *                if no tokens remain.     */    public String nextToken() {        if (delimiters == null) {            throw new NullPointerException();        }        int i = position;        int length = string.length();        if (i < length) {            if (returnDelimiters) {                if (delimiters.indexOf(string.charAt(position), 0) >= 0)                    return String.valueOf(string.charAt(position++));                for (position++; position < length; position++)                    if (delimiters.indexOf(string.charAt(position), 0) >= 0)                        return string.substring(i, position);                return string.substring(i);            }            while (i < length && delimiters.indexOf(string.charAt(i), 0) >= 0)                i++;            position = i;            if (i < length) {                for (position++; position < length; position++)                    if (delimiters.indexOf(string.charAt(position), 0) >= 0)                        return string.substring(i, position);                return string.substring(i);            }        }        throw new NoSuchElementException();    }    /**     * Returns the next token in the string as a {@code String}. The delimiters     * used are changed to the specified delimiters.     *     * @param delims     *            the new delimiters to use.     * @return next token in the string as a {@code String}.     * @throws NoSuchElementException     *                if no tokens remain.     */    public String nextToken(String delims) {        this.delimiters = delims;        return nextToken();    }}



更多相关文章

  1. androidのadb input使用
  2. Android中ExpandableListView的使用
  3. Android(安卓)原生 MediaPlayer 和 MediaCodec 的区别和联系(二)
  4. Android(安卓)原生 MediaPlayer 和 MediaCodec 的区别和联系(二)
  5. android 上层wifi模块调用分析
  6. Android(安卓)SDK自带教程之BluetoothChat
  7. android 自定义导航控件
  8. Android中shape的使用
  9. Android编译源码时出现的错误: “_FORTIFY_SOURCE”重定义

随机推荐

  1. Android中使用自定义view实现轮播图
  2. Android 跳转到应用市场,评价App
  3. android 的 setTag
  4. 启动图启动界面的简单实现
  5. Android 自带Apps 学习---AlarmClock
  6. Android(Java):http options
  7. Android小知识库
  8. android 8.0 設置Ethernet的靜態IP
  9. Android解决HAXM安装的问题
  10. 网络连接ConnectivityManager