当前位置: 代码迷 >> Android >> android -自定义数目字软键盘的设计与实现(1)
  详细解决方案

android -自定义数目字软键盘的设计与实现(1)

热度:39   发布时间:2016-04-28 07:35:32.0
android --自定义数字软键盘的设计与实现(1)

相信很多情况下我们看到过一些数字的输入,弹出来的并不是系统自带的键盘。这就是自定义的软键盘,软键盘的一个好处就是简单,操作方便。如何实现一个自定义的软键盘呢??其实这个过程是比较简单的,只要把几个关键的原理搞明白了,你就会发现真的很简单,很方便!看一下效果图:


这篇博客主要介绍一下实现的相关原理,下一节就会把具体实现的步骤和大家分享一下!

实现软键盘主要用到了系统的两个类Keyboard和KeyboardView:

Keyboard类源码的介绍是: Listener for virtual keyboard events.即用于监听虚拟键盘。

至于Keyboard类的映射机制,这里就不需要说了,本人感觉也没必要了解的太深,这里要介绍一些他,在接下来需要的几个重要的内容。我们看一下他的源码:

package com.android.inputmethod.keyboard;

import android.util.Log;
import android.util.SparseArray;
import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.latin.CollectionUtils;

/**
 * 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>
 * &lt;Keyboard
 *         latin:keyWidth="%10p"
 *         latin:keyHeight="50px"
 *         latin:horizontalGap="2px"
 *         latin:verticalGap="2px" &gt;
 *     &lt;Row latin:keyWidth="32px" &gt;
 *         &lt;Key latin:keyLabel="A" /&gt;
 *         ...
 *     &lt;/Row&gt;
 *     ...
 * &lt;/Keyboard&gt;
 * </pre>
 */
public class Keyboard {
    private static final String TAG = Keyboard.class.getSimpleName();


    /** Some common keys code. Must be positive.
     * These should be aligned with values/keycodes.xml
     */
    public static final int CODE_ENTER = '\n';
    public static final int CODE_TAB = '\t';
    public static final int CODE_SPACE = ' ';
    public static final int CODE_PERIOD = '.';
    public static final int CODE_DASH = '-';
    public static final int CODE_SINGLE_QUOTE = '\'';
    public static final int CODE_DOUBLE_QUOTE = '"';
    public static final int CODE_QUESTION_MARK = '?';
    public static final int CODE_EXCLAMATION_MARK = '!';
    // TODO: Check how this should work for right-to-left languages. It seems to stand
    // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
    // managed by the font? Or is it a different char?
    public static final int CODE_CLOSING_PARENTHESIS = ')';
    public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
    public static final int CODE_CLOSING_CURLY_BRACKET = '}';
    public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
    /** Special keys code. Must be negative.
     * These should be aligned with KeyboardCodesSet.ID_TO_NAME[],
     * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[]
     */
    public static final int CODE_SHIFT = -1;
    public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
    public static final int CODE_OUTPUT_TEXT = -3;
    public static final int CODE_DELETE = -4;
    public static final int CODE_SETTINGS = -5;
    public static final int CODE_SHORTCUT = -6;
    public static final int CODE_ACTION_ENTER = -7;
    public static final int CODE_ACTION_NEXT = -8;
    public static final int CODE_ACTION_PREVIOUS = -9;
    public static final int CODE_LANGUAGE_SWITCH = -10;
    public static final int CODE_RESEARCH = -11;
    // Code value representing the code is not specified.
    public static final int CODE_UNSPECIFIED = -12;
    public final KeyboardId mId;
    public final int mThemeId;


    /** Total height of the keyboard, including the padding and keys */
    public final int mOccupiedHeight;
    /** Total width of the keyboard, including the padding and keys */
    public final int mOccupiedWidth;
    /** The padding above the keyboard */
    public final int mTopPadding;
    /** Default gap between rows */
    public final int mVerticalGap;

    /** Per keyboard key visual parameters */
    public final KeyVisualAttributes mKeyVisualAttributes;
    public final int mMostCommonKeyHeight;
    public final int mMostCommonKeyWidth;
    /** More keys keyboard template */
    public final int mMoreKeysTemplate;
    /** Maximum column for more keys keyboard */
    public final int mMaxMoreKeysKeyboardColumn;
    /** Array of keys and icons in this keyboard */
    public final Key[] mKeys;
    public final Key[] mShiftKeys;
    public final Key[] mAltCodeKeysWhileTyping;
    public final KeyboardIconsSet mIconsSet;
    private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray();
    private final ProximityInfo mProximityInfo;
    private final boolean mProximityCharsCorrectionEnabled;
    public Keyboard(final KeyboardParams params) {
    /// M: @{
        ProximityInfo tmp;
        /// @}
        mId = params.mId;
        mThemeId = params.mThemeId;
        mOccupiedHeight = params.mOccupiedHeight;
        mOccupiedWidth = params.mOccupiedWidth;
        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
        mMoreKeysTemplate = params.mMoreKeysTemplate;
        mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
        mKeyVisualAttributes = params.mKeyVisualAttributes;
        mTopPadding = params.mTopPadding;
        mVerticalGap = params.mVerticalGap;
        mKeys = params.mKeys.toArray(new Key[params.mKeys.size()]);
        mShiftKeys = params.mShiftKeys.toArray(new Key[params.mShiftKeys.size()]);
        mAltCodeKeysWhileTyping = params.mAltCodeKeysWhileTyping.toArray(
                new Key[params.mAltCodeKeysWhileTyping.size()]);
        mIconsSet = params.mIconsSet;
        /// M: Modified by MTK to avoid OOME thrown to DVM on LCA
        //     since there is an int array sized about 32KB allocated
        //     within ProximityInfo constructor. @{
        try {
            tmp = new ProximityInfo(params.mId.mLocale.toString(),
                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
        } catch (OutOfMemoryError e) {
            Log.w(TAG, "OutOfMemoryError at ProximityInfo");
            /// M: It`s dangerous here, since this approach will generate
            //     a proximity useless, this will cause no responce on VK.
            // TODO: It`s better to unbind LatinIME here or provide another
            //       keyboard here.
            tmp = ProximityInfo.createDummyProximityInfo();
        }
        mProximityInfo = tmp;
        /// @}
        mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
    }
    public boolean hasProximityCharsCorrection(final int code) {
        if (!mProximityCharsCorrectionEnabled) {
            return false;
        }
        // Note: The native code has the main keyboard layout only at this moment.
        // TODO: Figure out how to handle proximity characters information of all layouts.
        final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = (
                mId.mElementId == KeyboardId.ELEMENT_ALPHABET
                || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED);
        return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code);
    }
    public ProximityInfo getProximityInfo() {
        return mProximityInfo;
    }
    public Key getKey(final int code) {
        if (code == CODE_UNSPECIFIED) {
            return null;
        }
        synchronized (mKeyCache) {
            final int index = mKeyCache.indexOfKey(code);
            if (index >= 0) {
                return mKeyCache.valueAt(index);
            }
            for (final Key key : mKeys) {
                if (key.mCode == code) {
                    mKeyCache.put(code, key);
                    return key;
                }
            }
            mKeyCache.put(code, null);
            return null;
        }
    }
    public boolean hasKey(final Key aKey) {
        if (mKeyCache.indexOfValue(aKey) >= 0) {
            return true;
        }


        for (final Key key : mKeys) {
            if (key == aKey) {
                mKeyCache.put(key.mCode, key);
                return true;
            }
        }
        return false;
    }
    public static boolean isLetterCode(final int code) {
        return code >= CODE_SPACE;
    }
    @Override
    public String toString() {
        return mId.toString();
    }
    /**
     * Returns the array 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 the nearest keys to the given point. If the given
     * point is out of range, then an array of size zero is returned.
     */
    public Key[] getNearestKeys(final int x, final int y) {
        // Avoid dead pixels at edges of the keyboard
        final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
        final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
        return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
    }
    public static String printableCode(final int code) {
        switch (code) {
        case CODE_SHIFT: return "shift";
        case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
        case CODE_OUTPUT_TEXT: return "text";
        case CODE_DELETE: return "delete";
        case CODE_SETTINGS: return "settings";
        case CODE_SHORTCUT: return "shortcut";
        case CODE_ACTION_ENTER: return "actionEnter";
        case CODE_ACTION_NEXT: return "actionNext";
        case CODE_ACTION_PREVIOUS: return "actionPrevious";
        case CODE_LANGUAGE_SWITCH: return "languageSwitch";
        case CODE_UNSPECIFIED: return "unspec";
        case CODE_TAB: return "tab";
        case CODE_ENTER: return "enter";
        default:
            if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code);
            if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
            if (code < 0x100) return String.format("'%c'", code);
            return String.format("'\\u%04x'", code);
        }
    }
}

我们在设置每一个按键的code时,就是根据keyboard类中定义的一些属性,比如回退,删除,清空等都是固定。

知道了

    public static final int CODE_SHIFT = -1;
    public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
    public static final int CODE_OUTPUT_TEXT = -3;
    public static final int CODE_DELETE = -4;
    public static final int CODE_SETTINGS = -5;
    public static final int CODE_SHORTCUT = -6;
    public static final int CODE_ACTION_ENTER = -7;
    public static final int CODE_ACTION_NEXT = -8;
    public static final int CODE_ACTION_PREVIOUS = -9;
    public static final int CODE_LANGUAGE_SWITCH = -10;
    public static final int CODE_RESEARCH = -11;
    // Code value representing the code is not specified.
    public static final int CODE_UNSPECIFIED = -12;

我们就不会有太多的疑惑了!也是说,我们自定义的每一个按键都将会有一个codes值,比如回退我们就写成:<Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"/>

在监听就是:if (primaryCode == Keyboard.KEYCODE_DELETE){}.这就表示,监听回退事件了!


KeyboardView类源码的介绍是: A view that renders a virtual [email protected] Keyboard}. It handles rendering of keys and detecting key presses and touch movements.即它处理绘制钥匙和检测按键和触摸动作。这个类的代码比较多,我们就不贴出来,它里面有很多方法,在我们自定义的软键盘很多属性,就需要我们用这个类来设置比如:

keyboardView = (KeyboardView) act.findViewById(R.id.keyboard_view);  
keyboardView.setKeyboard(k);  
keyboardView.setEnabled(true);  
keyboardView.setPreviewEnabled(true);
keyboardView.setVisibility(View.VISIBLE);
keyboardView.setOnKeyboardActionListener(listener);

了解一些源码,就可以是我们知道我们为什么要这样写,为什么要这样做了!

我们还需要建一个xml文件,来布局我们的视图,一般是在res文件夹中建一个名为xml的文件夹,在里面新建立一个xml布局文件。

每一个按键的属性主要包括 android:codes=" " android:keyLabel=" "。activity就是根据codes的值来监听的。一些可以自定义设置,一些需要是keyboard中设置好的,要保持一致。

好了,今天就先说到这里,下次再继续说建立一个完整的项目实例的过程!



  相关解决方案