Создание 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="[{&lt;" />

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;
Другие вопросы по тегам