Reputation: 1876
I'm designing a soft keyboard and I want to change its height at run-time as the user choose between landscape and portrait mode. I know how to change key's height in xml, but I need to do it dynamically.
The only thing that came to my mind was to subclass from Keyboard and override its setKeysHeight (int height), but it seems useless as the whole keyboard stopped responding to my clicks and the height (though different from previously) didn't care about 'height' in the aforementioned function.
Any idea/workaround?
Upvotes: 14
Views: 11840
Reputation: 438
Bruce's solution does not work anymore. Bottom keys stop responding after resize. Look at Keyboard.java:
public int[] getNearestKeys(int x, int y) {
if (mGridNeighbors == null) computeNearestNeighbors();
So getNearestKeys does not trigger any computations, computeNearestNeighbors() is not called more than once (and it's private, so you can't call it directly).
Instead of changing size in existing object, I modified Keyboard.Key class:
public class FTKey extends Keyboard.Key {
FTKey(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) {
super(res, parent, x, y, parser);
this.height = (int) (height * FTPref.yScale);
this.y = this.height * FTPref.yScale; // This works only if all rows of the same height
}
In onStartInput() I re-create all keyboard objects anew every time:
FTPref.yScale = 0.7f;
mEnglishKeyboard = new FTKeyboard(this ...
mRussianKeyboard = new FTKeyboard(this ...
Overriding getHeight() to calculate total keyboard height is still required.
It will be tricky to have keys of different height, because you'll have to calculate not only total height, but y position of every button and it depends on the height of previous rows.
Upvotes: 0
Reputation: 5630
I use the following code with Android 6:
private void adjustKeyboardKeyHeight (MyKeyboard keyboard, int newKeyHeight) {
int oldKeyHeight = keyboard.getKeyHeight();
int verticalGap = keyboard.getVerticalGap();
int rows = 0;
for (Keyboard.Key key : keyboard.getKeys()) {
key.height = newKeyHeight;
int row = (key.y + verticalGap) / (oldKeyHeight + verticalGap);
key.y = row * newKeyHeight + (row - 1) * verticalGap;
rows = Math.max(rows, row + 1);
}
keyboard.setHeight(rows * newKeyHeight + (rows - 1) * verticalGap);
}
private static class MyKeyboard extends Keyboard {
private int height;
MyKeyboard (Context context, int xmlLayoutResId) {
super(context, xmlLayoutResId);
height = super.getHeight();
}
@Override public int getKeyHeight() {
return super.getKeyHeight();
}
@Override public int getVerticalGap() {
return super.getVerticalGap();
}
public void setHeight (int newHeight) {
height = newHeight;
}
@Override public int getHeight() {
return height;
}
}
Upvotes: 0
Reputation: 2574
Original solution posted at https://stackoverflow.com/a/9695482/1241783 but it doesn't come with explanation so here I extend it a bit.
1) Create a new class that extends the Keyboard class that overrides the getHeight() method.
@Override
public int getHeight() {
return getKeyHeight() * 3;
}
Note: the number 3 here is your total number of rows, if your keyboard has 5 rows, put 5.
If your keyboard row height is different for each row, here you need to calculate yourself and return the total height (unit is in pixels, took me a while to figure out that it is not dp so need to convert dp to pixel for all calculations) for example:
@Override
public int getHeight() {
return row1Height + row2Height + row3Height + row4Height + row5Height;
}
2) Create a new public function in the same class.
public void changeKeyHeight(double height_modifier)
{
int height = 0;
for(Keyboard.Key key : getKeys()) {
key.height *= height_modifier;
key.y *= height_modifier;
height = key.height;
}
setKeyHeight(height);
getNearestKeys(0, 0); //somehow adding this fixed a weird bug where bottom row keys could not be pressed if keyboard height is too tall.. from the Keyboard source code seems like calling this will recalculate some values used in keypress detection calculation
}
If you're not using height_modifier but set to specific height instead, you'll need to calculate key.y position yourself.
If your keyboard row height is different for each row, you may need to check the keys, determine the row it belongs and set the height to correct value if not the keys will overlap each other. Also store the row heights in private variable to be used in getHeight() above. PS: On certain configuration I couldn't press the bottom row keys after changing keyboard height, and I found that calling getNearestKeys() fixes that though I'm not exactly sure why.
Note: key.y is y position of the key, coordinate 0 starts from the top of the keyboard, and goes down as the value increases. e.g. Coordinate 100 points to 100 pixel from the top of the keyboard :)
3) Last step is to call changeKeyHeight in your main class that extends InputMethodService. Do it inside (override it) onStartInputView() as this is where the keyboard should be redrawn after you change the height (via preference or something).
If you're looking at the Android soft keyboard sample project, it will be like this:
@Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
super.onStartInputView(attribute, restarting);
// Change the key height here dynamically after getting your value from shared preference or something
mCurKeyboard.changeKeyHeight(1.5);
// Apply the selected keyboard to the input view.
mInputView.setKeyboard(mCurKeyboard);
mInputView.closing();
final InputMethodSubtype subtype = mInputMethodManager.getCurrentInputMethodSubtype();
mInputView.setSubtypeOnSpaceKey(subtype);
}
Cheers!
Extra: If you need a dp to pixel converter, here's the code:
private int convertDpToPx(int dp)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
Upvotes: 19