Android自定义键盘 - 预览视图约束到父布局

ban*_*ing 10 android android-layout android-custom-keyboard

我创建了一个自定义键盘,它工作正常 - 除了前两行键的预览视图显示不够高.它们的垂直位置受到父布局的约束.

这些屏幕截图说明了问题 - "0"和"8"的预览位置是好的,但对于"5"和"2",它不是:

键'0'的预览显示在按钮上方...

按钮'0'

键'8'的预览也显示在按钮上方...

按钮'8'

但键'5'的预览未显示在按钮上方...

按钮'5'

键'2'的预览未显示在按钮上方...

按钮'2'

如何克服,因此"5"和"2"的预览显示在它们各自键上方相同的距离处,与"0"和"8"相同.

这是我的keyboard.xml ...

<android.inputmethodservice.KeyboardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keyboard"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:keyPreviewLayout="@layout/keyboard_key_preview" />
Run Code Online (Sandbox Code Playgroud)

这是我的keyboard_key_preview.xml ...

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="@color/keyboard_preview_bg"
    android:textColor="@color/keyboard_preview_fg"
    android:textStyle="bold"
    android:textSize="@dimen/keyboard_preview_text_size" />
Run Code Online (Sandbox Code Playgroud)

这是我的keyboard_numeric.xml布局......

<Keyboard
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="33.33%p"
    android:keyHeight="@dimen/keyboard_key_height"
    android:horizontalGap="@dimen/keyboard_horizontal_gap"
    android:verticalGap="@dimen/keyboard_vertical_gap">
    <Row android:rowEdgeFlags="top">
        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/>
        <Key android:codes="50" android:keyLabel="2"/>
        <Key android:codes="51" android:keyLabel="3" android:keyEdgeFlags="right"/>
    </Row>
    <Row>
        <Key android:codes="52" android:keyLabel="4" android:keyEdgeFlags="left"/>
        <Key android:codes="53" android:keyLabel="5"/>
        <Key android:codes="54" android:keyLabel="6" android:keyEdgeFlags="right"/>
    </Row>
    <Row>
        <Key android:codes="55" android:keyLabel="7" android:keyEdgeFlags="left"/>
        <Key android:codes="56" android:keyLabel="8"/>
        <Key android:codes="57" android:keyLabel="9" android:keyEdgeFlags="right"/>
    </Row>
    <Row android:rowEdgeFlags="bottom">
        <Key android:codes="-5" android:keyIcon="@drawable/ic_backspace_white_24dp" android:isRepeatable="true" android:keyEdgeFlags="left" />
        <Key android:codes="48" android:keyLabel="0"/>
        <Key android:codes="-4" android:keyLabel="DONE" android:keyEdgeFlags="right"/>
    </Row>
</Keyboard>
Run Code Online (Sandbox Code Playgroud)

Che*_*amp 8

预览键实际上是PopupWindow在构造函数中创建的KeyboardView.(见这里的代码).

您遇到的问题是因为PopupWindow其父级正在剪切它.如果可以禁用剪辑,PopupWindow那么预览键将能够在键盘视图之外"弹出".如果在上面引用的代码中设置断点并执行以下代码,则可以看到此方法有效:

mPreviewPopup.setClippingEnabled(false)
Run Code Online (Sandbox Code Playgroud)

请参阅文档中的setClippingEnabledPopupWindow.默认情况下,启用剪裁.

setClippingEnabled

void setClippingEnabled(已启用布尔值)

允许弹出窗口超出屏幕范围.默认情况下,窗口被剪切到屏幕边界.将此设置为false将允许窗口准确定位.

它显示"屏幕",但它也适用于键盘窗口.

剩下的问题是:如何禁用剪辑?不幸的是,它mPreviewPopup是一个私有变量.反思将提供访问但不理想.我还没有找到另一种方法来禁用此私有的剪辑PopupWindow,所以这只是指向分辨率而不是分辨率本身.


更新:我再看看发生了什么.以下是我在各种API级别中发现的内容.

API 17-21:允许键盘预览弹出键盘边界.

API 22,23,25-26:键预览受限于键盘的边界,但确实完整显示.这就是OP所看到的.

API 24:键预览被约束到键盘的边界,但是被剪裁.(破碎)

因此,API 22发生了变化.我在API 21和API 22之间看到的唯一实质性差异是支持FLAG_LAYOUT_ATTACHED_IN_DECOR标志.(另请参阅setAttachedInDecor.)此代码确实处理弹出窗口的位置,因此可能与此问题有关.(我不再相信这是真的.)

我也发现也可能是相关的.(也许...)

setClippingEnabled()适用于API 22-26,允许预览键弹出键盘布局的边界.除了使用反射,我没有找到纠正问题的方法.

虽然新的约束行为可能是预期的行为,但这可能有资格获得错误报告.

下面是API 26的出现该问题的视频,然后我设置mClippingEnabled到标志falsePopupWindow.java,并显示修正行为.

在此输入图像描述


Sur*_*gch 5

对于将来来这里的人,我建议不要使用KeyboardView. 至少在撰写本文时(API 27),它已经很长时间没有更新了。上述问题所描述的问题只是其众多缺点之一。

这需要更多工作,但您可以制作自己的自定义视图来用作键盘。我在这个答案的末尾描述了这个过程。

当您在自定义键盘中创建弹出预览窗口时,您需要调用popupWindow.setClippingEnabled(false)以使弹出窗口显示在 Android API 22+ 的键盘上方。(感谢这个答案的见解。)

这是我最近的一个项目中的一个例子。

private void layoutAndShowPopupWindow(Key key, int xPosition) {
    popupWindow = new PopupWindow(popupView,
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    popupWindow.setClippingEnabled(false);// <-- let popup display above keyboard
    int location[] = new int[2];
    key.getLocationInWindow(location);
    int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    popupView.measure(measureSpec, measureSpec);
    int popupWidth = popupView.getMeasuredWidth();
    int spaceAboveKey = key.getHeight() / 4;
    int x = xPosition - popupWidth / popupView.getChildCount() / 2;
    int y = location[1] - popupView.getMeasuredHeight() - spaceAboveKey;
    popupWindow.showAtLocation(key, Gravity.NO_GRAVITY, x, y);

    // using popupWindow.showAsDropDown(key, 0, yOffset) might be
    // easier than popupWindow.showAtLocation() 
}
Run Code Online (Sandbox Code Playgroud)