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

Android自定义键盘-预览视图受父布局约束,android,android-layout,android-custom-keyboard,Android,Android Layout,Android Custom Keyboard,我已经创建了一个自定义键盘,它工作得很好-除了顶部两行键的预览视图显示得不够高。它们的垂直位置受父布局的约束 这些屏幕截图说明了问题所在-“0”和“8”的预览位置很好,但“5”和“2”的预览位置不好: 按键“0”的预览显示在按钮上方… 按键“8”的预览也显示在按钮上方… 但按键“5”的预览未显示在按钮上方… 并且按键“2”的预览未显示在按钮上方… 如何克服,因此“5”和“2”的预览显示在其各自键上方的距离与“0”和“8”的距离相同 这是我的键盘.xml <android.input

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

这些屏幕截图说明了问题所在-“0”和“8”的预览位置很好,但“5”和“2”的预览位置不好:

按键“0”的预览显示在按钮上方…

按键“8”的预览也显示在按钮上方…

但按键“5”的预览未显示在按钮上方…

并且按键“2”的预览未显示在按钮上方…

如何克服,因此“5”和“2”的预览显示在其各自键上方的距离与“0”和“8”的距离相同

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

这是我的键盘\u键\u预览.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" />
<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" />

这是我的键盘\u 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>

预览键实际上是在
键盘视图的构造函数中创建的
弹出窗口。(参见代码)

您看到的问题是因为父对象正在剪裁
PopupWindow
。如果可以为
PopupWindow
禁用剪辑,则预览键将能够在键盘视图之外“弹出”。如果在上面引用的代码处设置断点并执行以下代码,您可以看到这种方法是有效的:

mPreviewPopup.setClippingEnabled(false)
请参见文档中的
PopupWindow
。默认情况下,将启用剪裁

设置剪辑

void setClippingEnabled(启用布尔值)

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

它说的是“屏幕”,但它也适用于键盘窗口

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


更新:我又看了一眼发生了什么。下面是我在各种API级别中发现的内容

API 17-21:允许在键盘边界之外弹出按键预览。

API 22,23,25-26:按键预览被限制在键盘的边界上,但不会显示整个键盘。这就是OP所看到的。

API 24:键预览被约束到键盘的边界,但在其他情况下被剪裁。(坏了)

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

我还发现这也可能有关联。(也许…)

setClippingEnabled()
在API 22-26上工作,允许预览键弹出键盘布局的边界之外。除了使用反射,我看不到解决问题的方法

尽管新的受约束行为可能是预期的行为,但这可能符合bug报告的条件

下面是一个显示问题的API 26视频,然后我在
PopupWindow.java
中将
mClippingEnabled
标记设置为
false
,并显示正确的行为


在这个问题上花了几个小时之后,我终于找到了一个我觉得不太合适的解决方案。在自定义的
InputMethodService
中,维护对键盘视图的引用,
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筛选此重写方法的行为


感谢唐克提到
setpopuparent()
。如果愿意,您可以尝试重写不同的方法并使用相同的逻辑,但是,您不能在
onCreateInputView()
中执行此操作,因为键盘视图的父级尚未创建。

对于将来来到这里的任何人,我建议不要使用
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() 
}
private void layoutAndShowPopupWindow(Key-Key,int-xPosition){
popupWindow=新的popupWindow(popupView,
LinearLayout.LayoutParams.WRAP_内容,
LinearLayout.LayoutParams.WRAP_内容);

popupWindow.setClippingEnabled(false);//你找到解决方案了吗?在我的测试中,这种情况至少在Android 7.1和8.0中发生。但是,在5.1中没有发生。将弹出视图约束到键盘区域是iOS所做的,