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

时间:2017-10-04 12:33:17

标签: android android-layout android-custom-keyboard

我创建了一个自定义键盘,效果很好 - 除了前两行键的预览视图显示得不够高。它们的垂直位置受到父布局的约束。

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

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

Button '0'

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

Button '8'

但关键' 5'的预览没有显示在按钮上方......

Button '5'

关键' 2'的预览没有显示在按钮上方......

Button '2'

如何克服,所以预览' 5'和' 2'显示在它们各自的键上方相同的距离,就像它用于' 0' 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" />

这是我的 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" />

这是我的 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>

3 个答案:

答案 0 :(得分:7)

预览键实际上是在PopupWindow的构造函数中创建的KeyboardView。 (参见代码here)。

您看到的问题是因为PopupWindow被其父级裁剪。如果可以为PopupWindow禁用剪裁,则预览键将能够弹出&#34;弹出&#34;在键盘视图之外。如果在上面引用的代码中设置断点并执行以下代码,则可以看到此方法有效:

mPreviewPopup.setClippingEnabled(false)

请参阅PopupWindow文档中的setClippingEnabled。默认情况下,启用剪裁。

  

setClippingEnabled

     

void setClippingEnabled(boolean enabled)

     

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

它说&#34;屏幕,&#34;但它也适用于键盘窗口。

剩下的问题是:如何禁用剪裁?不幸的是,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。)此代码确实处理弹出窗口的位置,因此可能与此问题有关。 (我不再相信这是真的。)

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

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

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

以下是展示该问题的API 26视频,然后我在mClippingEnabled中将false标记设置为PopupWindow.java并显示已更正的行为。

enter image description here

答案 1 :(得分:1)

在这个问题上花了好几个小时之后,我终于找到了一个我觉得合情合理的解决方案。在自定义InputMethodService中,维护对KeyboardView的引用,mKeyboardView,然后覆盖onStartInputView()。然后,抓住KeyboardView的父级,在其顶部边界应用一些填充,最后使用此修改后的父级布局在KeyboardView上调用setPopupParent()。这是我的代码:

@Override
public void onStartInputView(EditorInfo info, boolean restarting){
    ViewGroup originalParent = (ViewGroup) mKeyboardView.getParent();
    if (originalParent != null) {
        originalParent.setPadding(0,LAYOUT_PADDING, 0, 0);
        mKeyboardView.setPopupParent(originalParent);
    }
}

您可以根据存在此问题的API过滤此重写方法的行为。

感谢唐珂提及setPopupParent()。您可以尝试覆盖不同的方法并使用相同的逻辑,但是,您不能在onCreateInputView()中执行此操作,因为还没有创建KeyboardView的父级。

答案 2 :(得分:1)

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

这是更多的工作,但您可以将自己的自定义视图用作键盘。我在this answer结束时描述了这个过程。

在自定义键盘中创建弹出预览窗口时,需要调用popupWindow.setClippingEnabled(false)才能让弹出窗口显示在Android API 22+的键盘上方。 (感谢this answer提供的见解。)

以下是我最近的一个项目的背景示例。

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() 
}