FileDocCategorySizeDatePackage
Keyboard.javaAPI DocAndroid 1.5 API31904Wed May 06 22:41:54 BST 2009android.inputmethodservice

Keyboard

public class Keyboard extends Object
Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard consists of rows of keys.

The layout file for a keyboard contains XML that looks like the following snippet:

<Keyboard
android:keyWidth="%10p"
android:keyHeight="50px"
android:horizontalGap="2px"
android:verticalGap="2px" >
<Row android:keyWidth="32px" >
<Key android:keyLabel="A" />
...
</Row>
...
</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_verticalGap

Fields Summary
static final String
TAG
private static final String
TAG_KEYBOARD
private static final String
TAG_ROW
private static final String
TAG_KEY
public static final int
EDGE_LEFT
public static final int
EDGE_RIGHT
public static final int
EDGE_TOP
public static final int
EDGE_BOTTOM
public static final int
KEYCODE_SHIFT
public static final int
KEYCODE_MODE_CHANGE
public static final int
KEYCODE_CANCEL
public static final int
KEYCODE_DONE
public static final int
KEYCODE_DELETE
public static final int
KEYCODE_ALT
private CharSequence
mLabel
Keyboard label
private int
mDefaultHorizontalGap
Horizontal gap default for all rows
private int
mDefaultWidth
Default key width
private int
mDefaultHeight
Default key height
private int
mDefaultVerticalGap
Default gap between rows
private boolean
mShifted
Is the keyboard in the shifted state
private Key
mShiftKey
Key instance for the shift key, if present
private int
mShiftKeyIndex
Key index for the shift key, if present
private int
mKeyWidth
Current key width, while loading the keyboard
private int
mKeyHeight
Current key height, while loading the keyboard
private int
mTotalHeight
Total height of the keyboard, including the padding and keys
private int
mTotalWidth
Total width of the keyboard, including left side gaps and keys, but not any gaps on the right side.
private List
mKeys
List of keys in this keyboard
private List
mModifierKeys
List of modifier keys such as Shift & Alt, if any
private int
mDisplayWidth
Width of the screen available to fit the keyboard
private int
mDisplayHeight
Height of the screen
private int
mKeyboardMode
Keyboard mode, or zero, if none.
private static final int
GRID_WIDTH
private static final int
GRID_HEIGHT
private static final int
GRID_SIZE
private int
mCellWidth
private int
mCellHeight
private int[]
mGridNeighbors
private int
mProximityThreshold
private static float
SEARCH_DISTANCE
Number of key widths from current touch point to search for nearest keys.
Constructors Summary
public Keyboard(android.content.Context context, int xmlLayoutResId)
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.

        this(context, xmlLayoutResId, 0);
    
public Keyboard(android.content.Context context, int xmlLayoutResId, int modeId)
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

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        final Display display = wm.getDefaultDisplay();
        mDisplayWidth = display.getWidth();
        mDisplayHeight = display.getHeight();
        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));
    
public Keyboard(android.content.Context context, int layoutTemplateResId, CharSequence characters, int columns, int horizontalPadding)

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.

If the specified number of columns is -1, then the keyboard will fit as many keys as possible in each row.

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.

        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.width = mDefaultWidth;
            key.height = mDefaultHeight;
            key.gap = mDefaultHorizontalGap;
            key.label = String.valueOf(c);
            key.codes = new int[] { c };
            column++;
            x += key.width + key.gap;
            mKeys.add(key);
            if (x > mTotalWidth) {
                mTotalWidth = x;
            }
        }
        mTotalHeight = y + mDefaultHeight; 
    
Methods Summary
private voidcomputeNearestNeighbors()

        // 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;
            }
        }
    
protected android.inputmethodservice.Keyboard$KeycreateKeyFromXml(android.content.res.Resources res, android.inputmethodservice.Keyboard$Row parent, int x, int y, android.content.res.XmlResourceParser parser)

        return new Key(res, parent, x, y, parser);
    
protected android.inputmethodservice.Keyboard$RowcreateRowFromXml(android.content.res.Resources res, android.content.res.XmlResourceParser parser)

        return new Row(res, this, parser);
    
static intgetDimensionOrFraction(android.content.res.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;
    
public intgetHeight()
Returns the total height of the keyboard

return
the total height of the keyboard

        return mTotalHeight;
    
protected intgetHorizontalGap()

        return mDefaultHorizontalGap;
    
protected intgetKeyHeight()

        return mDefaultHeight;
    
protected intgetKeyWidth()

        return mDefaultWidth;
    
public java.util.ListgetKeys()

        return mKeys;
    
public intgetMinWidth()

        return mTotalWidth;
    
public java.util.ListgetModifierKeys()

        return mModifierKeys;
    
public int[]getNearestKeys(int x, int y)
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.

        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];
    
public intgetShiftKeyIndex()

        return mShiftKeyIndex;
    
protected intgetVerticalGap()

        return mDefaultVerticalGap;
    
public booleanisShifted()

        return mShifted;
    
private voidloadKeyboard(android.content.Context context, android.content.res.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);
                        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) {
                            mShiftKey = key;
                            mShiftKeyIndex = mKeys.size()-1;
                            mModifierKeys.add(key);
                        } else if (key.codes[0] == KEYCODE_ALT) {
                            mModifierKeys.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 voidparseKeyboardAttributes(android.content.res.Resources res, android.content.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();
    
protected voidsetHorizontalGap(int gap)

        mDefaultHorizontalGap = gap;
    
protected voidsetKeyHeight(int height)

        mDefaultHeight = height;
    
protected voidsetKeyWidth(int width)

        mDefaultWidth = width;
    
public booleansetShifted(boolean shiftState)

        if (mShiftKey != null) {
            mShiftKey.on = shiftState;
        }
        if (mShifted != shiftState) {
            mShifted = shiftState;
            return true;
        }
        return false;
    
protected voidsetVerticalGap(int gap)

        mDefaultVerticalGap = gap;
    
private voidskipToEndOfRow(android.content.res.XmlResourceParser parser)

        int event;
        while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
            if (event == XmlResourceParser.END_TAG 
                    && parser.getName().equals(TAG_ROW)) {
                break;
            }
        }