如何将已弃用的 KeyboardView.java 和 Keyboard.java 正确复制到 android 项目?

How to properly copy deprecated KeyboardView.java and Keyboard.java to android project?

我正在尝试遵循 Google 关于 KeyboardView.java 的建议:https://developer.android.com/reference/android/inputmethodservice/KeyboardView

我已经复制并调整了 KeyboardView.java、Keyboard.java 和 com.android.internal.R 中标记为已弃用的部分,并创建了一个新的 res/values/styles.xml 包含样式定义的文件。

一切都构建得很好,但是当它到达显示自定义键盘的地步时, 我得到一个空指针异常。我可以看到问题出在此处复制的 KeyboardView 代码中(a.getIndexCount() 的计数为 0,因此 mKeyBackground 永远不会设置并在调用“[=38= 时导致空异常](mPadding)").我不知道我错过了什么?:

    public KeyboardView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a =
                context.obtainStyledAttributes(
                        attrs, R.styleable.KeyboardView, defStyle, 0);
        LayoutInflater inflate =
                (LayoutInflater) context
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        int previewLayout = 0;
        int keyTextSize = 0;

// n is set to 0
        int n = a.getIndexCount();

// since n = 0, the loop is never entered
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.KeyboardView_keyBackground:

// mKeyBackground is supposed to be set but isn't because n = 0
                    mKeyBackground = a.getDrawable(attr);
                    break;
                case R.styleable.KeyboardView_verticalCorrection:
                    mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
                    break;
                case R.styleable.KeyboardView_keyPreviewLayout:
                    previewLayout = a.getResourceId(attr, 0);
                    break;
                case R.styleable.KeyboardView_keyPreviewOffset:
                    mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
                    break;
                case R.styleable.KeyboardView_keyPreviewHeight:
                    mPreviewHeight = a.getDimensionPixelSize(attr, 80);
                    break;
                case R.styleable.KeyboardView_keyTextSize:
                    mKeyTextSize = a.getDimensionPixelSize(attr, 18);
                    break;
                case R.styleable.KeyboardView_keyTextColor:
                    mKeyTextColor = a.getColor(attr, 0xFF000000);
                    break;
                case R.styleable.KeyboardView_labelTextSize:
                    mLabelTextSize = a.getDimensionPixelSize(attr, 14);
                    break;
                case R.styleable.KeyboardView_popupLayout:
                    mPopupLayout = a.getResourceId(attr, 0);
                    break;
            }
        }

        // Get the settings preferences
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        mVibrateOn = sp.getBoolean(PREF_VIBRATE_ON, mVibrateOn);
        mSoundOn = sp.getBoolean(PREF_SOUND_ON, mSoundOn);
        mProximityCorrectOn = sp.getBoolean(PREF_PROXIMITY_CORRECTION, true);

        mPreviewPopup = new PopupWindow(context);
        if (previewLayout != 0) {
            mPreviewText = (TextView) inflate.inflate(previewLayout, null);
            mPreviewTextSizeLarge = (int) mPreviewText.getTextSize();
            mPreviewPopup.setContentView(mPreviewText);
            mPreviewPopup.setBackgroundDrawable(null);
        } else {
            mShowPreview = false;
        }

        mPreviewPopup.setTouchable(false);

        mPopupKeyboard = new PopupWindow(context);
        mPopupKeyboard.setBackgroundDrawable(null);
        //mPopupKeyboard.setClippingEnabled(false);

        mPopupParent = this;
        //mPredicting = true;

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(keyTextSize);
        mPaint.setTextAlign(Align.CENTER);
        mPadding = new Rect(0, 0, 0, 0);
        mMiniKeyboardCache = new HashMap<Key,View>();

// Program crashes here from null pointer since mKeyBackground isn't set
        mKeyBackground.getPadding(mPadding);

        resetMultiTap();
        initGestureDetector();
    }

这是 styles.xml 文件:

styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="KeyboardView" parent="android:Widget">
        <item name="android:background">@color/black</item>
        <item name="keyBackground">@drawable/background</item>
        <item name="keyTextSize">22sp</item>
        <item name="keyTextColor">@color/white</item>
        <item name="keyPreviewLayout">@layout/preview</item>
        <item name="keyPreviewOffset">-12dp</item>
        <item name="keyPreviewHeight">80dp</item>
        <item name="labelTextSize">14sp</item>
        <item name="popupLayout">@layout/preview</item>
        <item name="verticalCorrection">-10dp</item>
        <!-- <item name="shadowColor">@android:color/transparent</item> -->
        <!-- <item name="shadowRadius">2.75</item> -->
    </style>
</resources>

这是我的原件com.android.internal.R(在values/attrs.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="KeyboardView">
        <!-- Default KeyboardView style. -->
        <attr name="keyboardViewStyle" format="reference" />
        <!-- Image for the key. This image needs to be a StateListDrawable, with the following
             possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,
             checkable+checked+pressed. -->
        <attr name="keyBackground" format="reference" />
        <!-- Size of the text for character keys. -->
        <attr name="keyTextSize" format="dimension" />
        <!-- Size of the text for custom keys with some text and no icon. -->
        <attr name="labelTextSize" format="dimension" />
        <!-- Color to use for the label in a key. -->
        <attr name="keyTextColor" format="color" />
        <!-- Layout resource for key press feedback. -->
        <attr name="keyPreviewLayout" format="reference" />
        <!-- Vertical offset of the key press feedback from the key. -->
        <attr name="keyPreviewOffset" format="dimension" />
        <!-- Height of the key press feedback popup. -->
        <attr name="keyPreviewHeight" format="dimension" />
        <!-- Amount to offset the touch Y coordinate by, for bias correction. -->
        <attr name="verticalCorrection" format="dimension" />
        <!-- Layout resource for popup keyboards. -->
        <attr name="popupLayout" format="reference" />
        <!-- <attr name="shadowColor" /> -->
        <!-- <attr name="shadowRadius" /> -->
    </declare-styleable>
    <declare-styleable name="KeyboardViewPreviewState">
        <!-- State for {@link android.inputmethodservice.KeyboardView KeyboardView}
                key preview background. -->
        <attr name="state_long_pressable" format="boolean" />
    </declare-styleable>
    <declare-styleable name="Keyboard">
        <!-- Default width of a key, in pixels or percentage of display width. -->
        <attr name="keyWidth" format="dimension|fraction" />
        <!-- Default height of a key, in pixels or percentage of display width. -->
        <attr name="keyHeight" format="dimension|fraction" />
        <!-- Default horizontal gap between keys. -->
        <attr name="horizontalGap" format="dimension|fraction" />
        <!-- Default vertical gap between rows of keys. -->
        <attr name="verticalGap" format="dimension|fraction" />
    </declare-styleable>
    <declare-styleable name="Keyboard_Row">
        <!-- Row edge flags. -->
        <attr name="rowEdgeFlags">
            <!-- Row is anchored to the top of the keyboard. -->
            <flag name="top" value="4" />
            <!-- Row is anchored to the bottom of the keyboard. -->
            <flag name="bottom" value="8" />
        </attr>
        <!-- Mode of the keyboard. If the mode doesn't match the
             requested keyboard mode, the row will be skipped. -->
        <attr name="keyboardMode" format="reference" />
    </declare-styleable>
    <declare-styleable name="Keyboard_Key">
        <!-- The unicode value or comma-separated values that this key outputs. -->
        <attr name="codes" format="integer|string" />
        <!-- The XML keyboard layout of any popup keyboard. -->
        <attr name="popupKeyboard" format="reference" />
        <!-- The characters to display in the popup keyboard. -->
        <attr name="popupCharacters" format="string" />
        <!-- Key edge flags. -->
        <attr name="keyEdgeFlags">
            <!-- Key is anchored to the left of the keyboard. -->
            <flag name="left" value="1" />
            <!-- Key is anchored to the right of the keyboard. -->
            <flag name="right" value="2" />
        </attr>
        <!-- Whether this is a modifier key such as Alt or Shift. -->
        <attr name="isModifier" format="boolean" />
        <!-- Whether this is a toggle key. -->
        <attr name="isSticky" format="boolean" />
        <!-- Whether long-pressing on this key will make it repeat. -->
        <attr name="isRepeatable" format="boolean" />
        <!-- The icon to show in the popup preview. -->
        <attr name="iconPreview" format="reference" />
        <!-- The string of characters to output when this key is pressed. -->
        <attr name="keyOutputText" format="string" />
        <!-- The label to display on the key. -->
        <attr name="keyLabel" format="string" />
        <!-- The icon to display on the key instead of the label. -->
        <attr name="keyIcon" format="reference" />
        <!-- Mode of the keyboard. If the mode doesn't match the
             requested keyboard mode, the key will be skipped. -->
        <attr name="keyboardMode" />
    </declare-styleable>

</resources>

未使用包含默认值的 KeyboardView style

虽然您可以在 XML layout 中手动设置每个值(如您在评论中所述),但像这样

<your.package.name.KeyboardView
    …
    app:keyBackground="@drawable/bg_container"
    …
    />

或者直接设置整个style,像这样

<your.package.name.KeyboardView
    …
    style="@style/KeyboardView"
    …
    />

两者都需要在每次使用 View 时设置这些值。

更好的方法是在 KeyboardView.java:

中设置默认值
public KeyboardView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    TypedArray a = context.obtainStyledAttributes(attrs,
                       R.styleable.KeyboardView,
                       R.attr.keyboardViewStyle,
                       R.style.KeyboardView);
    // …
}