I have created a custom keyboard, which works fine - except the preview views for the top two rows of keys are not displayed high enough. Their vertical position is being constrained by the parent layout.
These screenshots illustrate the problem - the position of the preview for '0' and '8' is good, but for '5' and '2' it is not:
The preview for key '0' is shown above the button...
The preview for key '8' is also shown above the button...
But the preview for key '5' is not shown above the button...
And the preview for key '2' is not shown above the button...
How to overcome, so the preview for '5' and '2' are displayed at the same distance above their respective key as it is for '0' and '8'.
Here is my 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" />
Here is my 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" />
Here is my keyboard_numeric.xml layout...
<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>
After spending hours on this issue, I was finally able to find a solution which I feel is reasonably un-hacky. In your custom
InputMethodService
, maintain a reference to your KeyboardView,mKeyboardView
, and then overrideonStartInputView()
. Then, grab the KeyboardView's parent, apply some padding to its top boundary, and finally, callsetPopupParent()
on the KeyboardView with this modified parent layout. Here's the code that I have:You can filter the behavior of this overriden method according to the APIs on which this problem exists.
Kudos to Tang Ke for mentioning
setPopupParent()
. You could try overriding a different method and using the same logic if you wanted, however, you cannot do it inonCreateInputView()
as the KeyboardView's parent will not have been created yet.For anyone coming here in the future, I recommend not using
KeyboardView
. At least at the time of this writing (API 27), it hasn't been updated for a long time. The problem described in the question above is just one of its many shortcomings.It is more work, but you can make your own custom view to use as a keyboard. I describe that process at the end of this answer.
When you create the popup preview window in your custom keyboard, you need to call
popupWindow.setClippingEnabled(false)
in order to get the popup window to display above the keyboard for Android API 22+. (Thanks to this answer for that insight.)Here is an example in context from one of my recent projects.
The preview key is actually a
PopupWindow
created in the constructor forKeyboardView
. (See the code here).The problem that you are seeing is because the
PopupWindow
is being clipped by its parent. If clipping can be disabled for thePopupWindow
then the preview key will be able to "pop" outside of the keyboard view. You can see that this approach works if you set a breakpoint at the above-referenced code and execute the following code:See setClippingEnabled in the documentation for
PopupWindow
. By default, clipping is enabled.It says "screen," but it applies to the keyboard window as well.
The remaining question is: How to get clipping disabled? Unfortunately,
mPreviewPopup
is a private variable. Reflection will give access but is not ideal. I have not found another way to disable clipping for this privatePopupWindow
, so this is just pointing a way to a resolution and not the resolution itself.Update: I took another look at what is going on. Here is what I found with the various API levels.
APIs 17-21: Key preview is allowed to pop outside of the boundaries of the keyboard.
APIs 22,23,25-26: The key preview is constrained to the boundaries of the keyboard but does display in its entirety. This is what the OP was seeing.
API 24: Key preview is constrained to the boundaries of the keyboard but is otherwise clipped. (Broken)
So, the change occurred with API 22. The only substantive difference I see between API 21 and API 22 is support for the FLAG_LAYOUT_ATTACHED_IN_DECOR flag. (Also see setAttachedInDecor.) This code does deal with the placement of the popup window
, so it may be related to this problem. (I no longer believe that this is true.)I also found this which also may be related. (Maybe...)
setClippingEnabled()
works on API 22-26 to permit the preview key to pop outside the boundaries of the keyboard layout. Other than using reflection, I don't see a way to correct the problem.This may qualify for a bug report although the new constrained behavior may be the expected behavior.
Here is a video of API 26 exhibiting the problem, then I set the
mClippingEnabled
flag tofalse
inPopupWindow.java
and show the corrected behavior.