Создание SoftKeyboard с несколькими / альтернативными символами на клавишу
Я следовал за примерами на http://developer.android.com/ относительно методов ввода и играл с примером приложения SoftKeyboard. Вместе они дают более чем достаточно информации о создании простой клавиатуры.
Что я не вижу в API, так это возможность создавать альтернативные / несколько символов для каждой клавиши, которая доступна на стандартной клавиатуре (клавиатура LatinIME).
Изображение выше - результат длительного нажатия на клавишу "а". Когда вы долго нажимаете клавишу, можно заполнить всплывающее окно альтернативными символами.
Также можно дать всплывающую подсказку о некоторых клавишах, которая предложит пользователю нажать и удерживать клавишу, чтобы открыть всплывающее меню.
До сих пор я не нашел ни одного источника информации о том, как это достигается, надеюсь, кто-то сможет дать мне преимущество, до тех пор, пока я не буду следовать исходному коду встроенной клавиатуры и посмотреть, смогу ли я выполнить обратный инжиниринг. Это.
Изменить: Помогло бы, если бы ссылка developer.android.com на клавиатуру LatinIME не ссылалась на изображение овцы:) Фактический исходный код для LatinIME.java.
Редактировать 2: В качестве ссылки больше, чем что-либо еще, я полагаю, что это последовательность, которую выполняет обычное действие longPress для отображения всплывающей клавиатуры в KeyboardView.java:
onTouchEvent()
onModifiedTouchEvent()
mHandkler.handleMessage() with MSG_LONGPRESS
openPopupIfRequired()
onLongPress()
Изменить 3:
Я до сих пор не понял, как добавить метки в ключи? Ответ предполагает, что он не встроен в API, и на самом деле я не нашел код для этого. Однако клавиатура в 2.3.4 (API 10) показывает, что эта функциональность реализуется:
Очень хотел бы выяснить, как это происходит, но это нигде onDraw()
метод, который я вижу, - который заставляет меня поверить, что он пишется вне элемента KeyboardView. Я не могу найти layout
файл, используемый для отображения элемента KeyboardView на встроенной клавиатуре. Если кто-нибудь знает, где его найти, возможно, это даст мне подсказку, которая мне нужна.
Изменить 4: Перемещенная клавиша Предварительный просмотр вопроса здесь, поскольку он немного не по теме:
Как отключить окно предварительного просмотра клавиши SoftKeyboard?
6 ответов
Реализация всплывающего окна с альтернативным ключом:
Для каждой клавиши, для которой вы хотите иметь всплывающую клавиатуру, вы должны определить popupCharacters и popupKeyboard:
/res/xml/[Keyboard].xml
<Key android:keyLabel="("
android:popupKeyboard="@xml/keyboard_popup_template"
android:popupCharacters="[{<" />
popupKeyboard
XML-представление клавиатуры, используемой во всплывающем окне, содержащее альтернативные клавиши:
/res/xml/keyboard_popup_template.xml
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
android:keyHeight="56dp">
</Keyboard>
Стилизация всплывающего окна с альтернативным ключом:
Если вы хотите изменить макет / стиль всплывающего окна (по умолчанию это @android: layout / keyboard_popup_keyboard.xml), вы можете указать android:popupLayout
атрибут, который указывает на файл макета:
<android.inputmethodservice.KeyboardView
android:id="@+id/keyboard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#FF272727"
android:popupLayout="@layout/keyboard_popup_keyboard" />
Реализация наложения предварительного просмотра ключей:
Единственное решение, которое мне удалось собрать вместе, чтобы показать предварительный просмотр ключа (без полной перезаписи исходного кода KeyboardView), приведено ниже:
Упаковка <KeyboardView>
пометить с <FrameLayout>
с высотой, определенной умножением keyHeight на количество строк. Внутри этого тега я просто создал LinearLayout для хранения строк, затем LinearLayout для каждой строки, содержащей TextView с весом, равным значению% p, указанному для каждого <Key>
:
<TextView android:text="!" style="@style/Custom.Widget.KeyboardKeyOverlay" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="10"/>
И в стиле:
<style name="CustomTheme.Widget.KeyboardKeyOverlay">
<item name="android:background">@android:color/transparent</item>
<item name="android:textColor">#FFAAAAAA</item>
<item name="android:paddingRight">6dp</item>
<item name="android:paddingTop">4dp</item>
<item name="android:textSize">10sp</item>
<item name="android:gravity">right</item>
<item name="android:textStyle">bold</item>
</style>
Который производит это:
Я не буду счастлив, пока мне не удастся реализовать это так же, как системная клавиатура!
Судя по моей собственной попытке кодирования программной клавиатуры, я обнаружил, что:
- Ницца / шик обычно требует, чтобы вы продлили
KeyboardView
и в основном написать большие части кода для рисования. К сожалению, вы не можете сделать это, переопределив некоторые ключевые методы, так как почти все является приватным. Возможно, вы захотите взглянуть (и позаимствовать некоторый код из:(base)/core/java/android/inputmethodservice/KeyboardView.java
(репозиторий ядра Android)(apps)/other/LatinIME/LatinKeyboardView.java
(репозиторий основных приложений для Android)
Обратите внимание, что на сайте android.kernel.org есть овца, которая сообщает, что хранилище закрыто из-за взломщиков, но в других местах есть зеркала кода (к сожалению, ссылки потеряны)
- База
KeyboardView
не поддерживает скрытые подсказки клавиш, вы должны написать свой собственный KeyboardView, чтобы получить возможность переопределить метод onDraw().
Теперь о том, что вы можете сделать:
Вы можете обойти эту проблему, предоставив картинки для ключей: используйте xml
<Key ... android:keyIcon="@drawable/this_key_icon_file />
за это. К сожалению, у вас наверняка будут плохие результаты для писем с этим методом (проблемы с разрешением).Вы можете использовать (и настроить внешний вид) всплывающую клавиатуру, которая появляется при длительном нажатии.
Объявить шаблон клавиатуры res/xml/kbd_popup_template.xml
:
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
android:keyHeight="@dimen/key_height">
</Keyboard>
Объявите строковые значения, содержащие ключи, которые вы хотите на этой клавиатуре res/values/strings.xml
:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="alternates_for_a">àáâãäåæ</string>
</ressources>
Затем используйте оба в определении раскладки клавиатуры:
<Key android:codes="97" android:keyLabel="a"
android:popupKeyboard="@xml/kbd_popup_template"
android:popupCharacters="@string/alternates_for_a" />
Вы также можете использовать функцию двойного касания, тройного касания,..., чтобы сгенерировать альтернативы нажатой клавише. Для этого просто используйте список для кодов клавиш Android:
<Key android:codes="97,224,230" .../>
будет производить 97=' a
"для одного крана, 224=" à
"для двойного нажатия и 230=" æ
'для тройного крана.
Длительность рассмотрения двойного касания в исходном коде Android составляет 800 мс. Это, к сожалению, жестко закодировано (и я чувствую себя немного высоко).
Имейте в виду, что при двойном нажатии, он в основном отправляет a
"сначала, затем, на втором кране он отправляет" à
". Некоторым приложениям это не понравится.
Эта всплывающая клавиатура с кнопкой закрытия раздражает, когда у нас есть только один всплывающий символ. Более простой способ - переопределить метод onLongPress класса KeyboardView следующим образом.
@Override
protected boolean onLongPress(Key key) {
if (key.codes[0] == '1') {
getOnKeyboardActionListener().onKey('!', null);
return true;
}
}
Если вы хотите, чтобы над клавишей был текст, вы можете сделать это с помощью метода onDraw() в вашем классе, который переопределяет KeyboardView.
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
...
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(18);
paint.setColor(Color.WHITE);
//get all your keys and draw whatever you want
List <Keyboard.Key> keys = getKeyboard().getKeys();
for(Keyboard.Key key: keys) {
if(key.label != null) {
if (key.label.toString().equals("q") || key.label.toString().equals("Q"))
canvas.drawText(String.valueOf(1), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("w") || key.label.toString().equals("W"))
canvas.drawText(String.valueOf(2), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("e") || key.label.toString().equals("E"))
canvas.drawText(String.valueOf(3), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("r") || key.label.toString().equals("R"))
canvas.drawText(String.valueOf(4), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("t") || key.label.toString().equals("T"))
canvas.drawText(String.valueOf(5), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("y") || key.label.toString().equals("Y"))
canvas.drawText(String.valueOf(6), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("u") || key.label.toString().equals("U"))
canvas.drawText(String.valueOf(7), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("i") || key.label.toString().equals("I"))
canvas.drawText(String.valueOf(8), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("o") || key.label.toString().equals("o"))
canvas.drawText(String.valueOf(9), key.x + (key.width / 2) + 10, key.y + 25, paint);
else if (key.label.toString().equals("p") || key.label.toString().equals("P"))
canvas.drawText(String.valueOf(0), key.x + (key.width / 2) + 10, key.y + 25, paint);
else
{}
}
}
}
Для тех, кто пытается отклонить всплывающую клавиатуру, нажав за пределами ее области просмотра, мне повезло, поставив TouchListener
на KeyboardView
внутри класса расширяется InputMethodService
public class YourIME extends InputMethodService{
@Override
public View onCreateInputView() {
mInputView = (LatinKeyboardView) getLayoutInflater().inflate(R.layout.input, null);
setLatinKeyboard(mQwertyKeyboard);
mInputView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
mInputView.closing(); // Close popup keyboard if it's showing
}
return false;
}
});
return mInputView;
}
// The rest of your ime ...
}
Основываясь на ответах здесь, я сделал следующее:
public class MyKeyboardView extends KeyboardView {
private Keyboard.Key longPressedKey = null;
public MyKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyKeyboardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(35);
paint.setColor(Color.BLACK);
//get all your keys and draw whatever you want
List<Keyboard.Key> keys = getKeyboard().getKeys();
for (Keyboard.Key key : keys) {
if (key.popupCharacters != null && key.popupCharacters.length() > 0) {
canvas.drawText(key.popupCharacters.toString(), key.x + (key.width / 2) + 10, key.y + 30, paint);
}
}
}
@Override
protected boolean onLongPress(Keyboard.Key key) {
if (key.popupCharacters != null && key.popupCharacters.length() > 0)
longPressedKey = key;
return super.onLongPress(key);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
if (longPressedKey != null) {
// TODO: Try detecting what key the finger is above if there are many popupCharacters
getOnKeyboardActionListener().onKey((longPressedKey.popupCharacters.charAt(0)), null);
dismissPopupKeyboard();
longPressedKey = null;
}
}
return true; // Required for recieving subsequent events (ACTION_MOVE, ACTION_UP)
}
private void dismissPopupKeyboard() {
// Because of KeyboardView.dismissPopupKeyboard() is private, we are doing this trick (KeyboardView.click() calls dismissPopupKeyboard()).
// TODO: Make this like a normal human. We will need to create the popup on our own like the way the keyboard does it.
onClick(new View(getContext()));
}
и
При использовании KeyboardView изменил его на MyKeyboardView, с этим дополнительным параметром -
android:popupLayout="@layout/keyboard_popup_keyboard"
И это keyboard_popup_keyboard.xml (важно, чтобы не было кнопки отмены)
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<android.inputmethodservice.KeyboardView
android:id="@android:id/keyboardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/popup_key_color"
android:keyPreviewLayout="@layout/key_preview"
android:keyPreviewOffset="0px"
android:keyPreviewHeight="@dimen/key_preview_height"
android:keyTextColor="@color/darkGray"
android:keyBackground="@drawable/key_background_selector"
android:shadowRadius="0.0"
android:horizontalGap="0px"
android:verticalGap="0px" />
</LinearLayout>
Если вы хотите, чтобы над клавишей был текст, вы можете сделать это с помощью метода onDraw() в вашем классе, который расширяет KeyboardView. Я сделал что-то подобное, возможно, это могло бы помочь кому-то
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("LatinKeyboardView", "onDraw");
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(30);
paint.setColor(Color.LTGRAY);
List<Key> keys = getKeyboard().getKeys();
for (Key key : keys) {
if (key.label != null) {
switch (key.codes[0]) {
//qQ
case 81:
case 113:
case 1602:
case 1618:
canvas.drawText(String.valueOf(1), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//wW
case 87:
case 119:
case 1608:
case 1572:
canvas.drawText(String.valueOf(2), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//eE
case 69:
case 101:
case 1593:
case 1617:
canvas.drawText(String.valueOf(3), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//rR
case 82:
case 114:
case 1585:
case 1681:
canvas.drawText(String.valueOf(4), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//tT
case 84:
case 116:
case 1578:
case 1657:
canvas.drawText(String.valueOf(5), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//yY
case 89:
case 121:
case 1746:
case 1552:
canvas.drawText(String.valueOf(6), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//uU
case 85:
case 117:
case 1569:
case 1574:
canvas.drawText(String.valueOf(7), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//iI
case 73:
case 105:
case 1740:
case 1648:
canvas.drawText(String.valueOf(8), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//oO
case 79:
case 111:
case 1729:
case 1731:
canvas.drawText(String.valueOf(9), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//pP
case 80:
case 112:
case 1662:
case 1615:
canvas.drawText(String.valueOf(0), key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//aA
case 65:
case 97:
case 1575:
case 1570:
canvas.drawText("@", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//sS
case 83:
case 115:
case 1587:
case 1589:
canvas.drawText("#", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//dD
case 68:
case 100:
case 1583:
case 1672:
canvas.drawText("$", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//fF
case 70:
case 102:
case 1601:
case 1613:
canvas.drawText("%", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//gG
case 71:
case 103:
case 1711:
case 1594:
canvas.drawText("&", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//hH
case 72:
case 104:
case 1726:
case 1581:
canvas.drawText("-", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//jJ
case 74:
case 106:
case 1580:
case 1590:
canvas.drawText("+", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//kK
case 75:
case 107:
case 1705:
case 1582:
canvas.drawText("(", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//lL
case 76:
case 108:
case 1604:
case 1614:
canvas.drawText(")", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//zZ
case 90:
case 122:
case 1586:
case 1584:
canvas.drawText("*", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//xX
case 88:
case 120:
case 1588:
case 1679:
canvas.drawText("\"", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//cC
case 67:
case 99:
case 1670:
case 1579:
canvas.drawText("\'", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//vV
case 86:
case 118:
case 1591:
case 1592:
canvas.drawText(":", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//bB
case 66:
case 98:
case 1576:
case 1616:
canvas.drawText(";", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//nN
case 78:
case 110:
case 1606:
case 1722:
canvas.drawText("!", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
//mM
case 77:
case 109:
case 1605:
case 1611:
canvas.drawText("?", key.x + (key.width - keyXAxis), key.y + keyYAxis, paint);
break;
}
}
}
}
отрегулируйте эти оси по вашему выбору
int keyXAxis = 25;
int keyYAxis = 50;