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