Как я могу использовать пользовательский шрифт во входной области метода ввода?

Мы разрабатываем приложение, в котором нам нужно использовать собственный шрифт (a Typeface, загруженный из активов приложения) в EditText. Android-метод ввода сообщает следующее:

Когда фокус ввода перемещается в редактируемое текстовое поле или выходит из него, Android показывает или скрывает метод ввода (например, экранную клавиатуру), если это необходимо. Система также принимает решения о том, как ваш пользовательский интерфейс и текстовое поле появляются над методом ввода. Например, , когда вертикальное пространство на экране ограничено, текстовое поле может заполнить все пространство над методом ввода.

Это смелая часть, которая нас подводит. Фраза "текстовое поле может заполнить...", похоже, вводит в заблуждение, поскольку используемое текстовое поле не является EditText, которое мы настраиваем с помощью нашего пользовательского шрифта. ( ПРИМЕЧАНИЕ: ответы до сих пор объясняют, как установить пользовательский шрифт в EditText. Мы уже настраиваем пользовательский шрифт в EditText.. Пожалуйста, прочитайте вопрос тщательно перед ответом.) Здесь скриншот со скрытым способом ввода:

the dialog

Здесь, что происходит, когда отображается мягкая клавиатура, и вертикальное пространство ограничено:

constrained vertical space

Как вы можете видеть, шрифт в текстовом поле над методом ввода не является нашим пользовательским шрифтом (я предполагаю, что это шрифт Roboto по умолчанию). Это происходит для каждой мягкой клавиатуры, которую мы пробовали в качестве метода ввода.

Я хочу подчеркнуть, что когда пространство ограничено, этот вид над клавиатурой генерируется внутри системы, а не каким-либо из нашего кода.

Основной вопрос: есть ли способ (и, если да, что это такое?), чтобы контролировать внешний вид этого (из-за отсутствия лучшей терминологии) прокси-представления — как минимум, чтобы заставить его использовать наш собственный шрифт

Это был бы дополнительный бонус, если мы могли бы также контролировать макет и внешний вид всей области прокси над клавиатурой (включая кнопку "Готово" ). Мы используем вариацию метода, описанного в этой теме, чтобы наша деятельность использовала локаль, отличную от той, которая установлена ​​в системе, но локаль действия явно не является здесь применяется. (Если бы это было так, кнопка была бы слева и читала бы "בוצע", как это происходит, если я изменю языковой стандарт устройства на иврит. [EDIT: Я могу получить правильную метку кнопки, используя атрибут android:imeActionLabel на EditText. Однако я не знаю, как контролировать направленность макета.])

РЕДАКТИРОВАТЬ По запросу, здесь, как я создаю свой диалог (соответствующие фрагменты, взятые из DialogFragment):

public Dialog onCreateDialog(Bundle savedInstanceState) {
    final Dialog dlg = new Dialog(getActivity(), android.R.style.Theme_Holo_Light_Dialog_NoActionBar);
    dlg.setContentView(R.layout.edit_note_dialog);
    mAnimator = (ViewAnimator) dlg.findViewById(R.id.animator);
    final Typeface hebrew = SystemData.getHebrewFont();
    mNoteEditor = (EditText) dlg.findViewById(R.id.note_field);
    mNoteEditor.setTypeface(hebrew);

    // etc. (setting fonts for other elements, adding listeners, etc.)
}

А вот макет:

<?xml version="1.0" encoding="utf-8"?>
<ViewAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/animator"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:paddingTop="10dp"
        android:text="@string/loading" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:background="@android:color/black"
            android:gravity="center"
            android:text="@string/edit_note_title"
            android:textColor="@android:color/white"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/citation"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/title"
            android:layout_centerHorizontal="true"
            android:background="@android:color/black"
            android:gravity="center"
            android:textColor="@android:color/white"
            android:textSize="16sp" />

        <LinearLayout
            android:id="@+id/actions"
            style="?android:attr/buttonBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_margin="5dp" >

            <Button
                android:id="@+id/cancel"
                style="?android:attr/buttonBarButtonStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="8dp"
                android:gravity="center"
                android:minWidth="32dp"
                android:text="@string/cancel"
                android:textSize="@dimen/nav_interior_item_size" />

            <Button
                android:id="@+id/close"
                style="?android:attr/buttonBarButtonStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="8dp"
                android:gravity="center"
                android:minWidth="32dp"
                android:text="@string/save"
                android:textSize="@dimen/nav_interior_item_size" />
        </LinearLayout>

        <Button
            android:id="@+id/undo"
            style="?android:attr/buttonBarButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentStart="true"
            android:layout_margin="5dp"
            android:text="@string/edit_note_undo"
            android:textSize="@dimen/nav_interior_item_size" />

        <Button
            android:id="@+id/redo"
            style="?android:attr/buttonBarButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentEnd="true"
            android:layout_margin="5dp"
            android:text="@string/edit_note_redo"
            android:textSize="@dimen/nav_interior_item_size" />

        <EditText
            android:id="@+id/note_field"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/actions"
            android:layout_below="@+id/citation"
            android:focusableInTouchMode="true"
            android:gravity="top"
            android:hint="@string/edit_note_hint"
            android:imeActionLabel="@string/done"
            android:inputType="textMultiLine|textNoSuggestions"
            android:lineSpacingMultiplier="1.2" />
    </RelativeLayout>

</ViewAnimator>

Ответы

Ответ 1

Было бы просто, если бы у вас был экземпляр InputMethodService. Существует способ установить theme или даже установить извлеченный вид.

Проблема заключается в том, что вам нужно создать пользовательскую реализацию Input Method, и пользователям придется выбирать ее в системных настройках.

Update:

Вы можете попробовать:

    • android:windowSoftInputMode="adjustResize"
    • сделать прокручиваемое представление

или

    • используйте IME_FLAG_NO_EXTRACT_UI
    • установите OnEditorActionListener на EditText, чтобы на клавиатуре появилась кнопка действия.
    • создавать пользовательские макеты или скрывать/показывать элементы, когда высота экрана слишком мала. Например, скрыть кнопки внизу и показать кнопки с правой стороны. Он может выглядеть так же, как в режиме просмотра экстракта по умолчанию.

макетщик для диалога или EditText:

    mViewContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //unregister the layout listener if needed.
            int heightPixels = mViewContainer.getHeight();
            Resources resources = mContext.getResources();
            int heightDip = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, heightPixels, resources.getDisplayMetrics());
            if (heightDip < MIN_HEIGHT) {
                mBottomButtons.setVisibility(View.GONE);
                mSideButtons.setVisibility(View.VISIBLE);
            } else {
                mBottomButtons.setVisibility(View.VISIBLE);
                mSideButtons.setVisibility(View.GONE);                    
            }
        }
    });

Изменение видимости на GONE запросит ретрансляцию, и слушатель снова запустится, что может привести к циклу.

Ответ 2

Единственный способ, которым я могу видеть, - отключить полноэкранный режим редактирования для вашего EditText, который автоматически срабатывает, когда ваш EditText недостаточно высок.

mNoteEditor.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);

Вы можете исправить проблемы изменения размера диалогового окна с помощью

dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

Возможно, ваш диалог должен быть заключен в ScrollView.

Ответ 3

Я думаю, что, как утверждают другие, это, к сожалению, выше IME и может быть невозможно через поддерживаемые API.

Остерегайтесь взломанного и непроверенного решения:

Копаясь в исходном коде Android, я обнаружил, что каждый IME содержит реализацию AbstractInputMethodService. Android обеспечивает стандартную реализацию (android.inputmethodservice.InputMethodService), которая, если вы посмотрите, имеет общедоступный метод setExtractView и метод onCreateExtractTextView, который надувает com.android.internal.R.layout.input_method_extract_view.xml, который, в свою очередь, содержит com.android.internal.R.id.inputExtractEditText! Таким образом, хакерское решение будет состоять в том, чтобы надеяться, что для большинства ваших IME-интерфейсов InputMethodServices вашего клиента этот же XML (который, насколько я могу судить, был в Android некоторое время), и вы можете попытаться получить ссылку на этот EditText на его ID и изменить его шрифт. К сожалению, я не уверен, что в иерархии/окне представления добавлены представления InputMethodService, и если они доступны из вашего процесса приложения/активности.

Источник для InputMethodService: https://github.com/android/platform_frameworks_base/blob/master/core/java/android/inputmethodservice/InputMethodService.java

Источник для компоновки XML: https://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/input_method_extract_view.xml

Если вы действительно хотите попасть в проблему, возможно, есть способ получить ссылку на службу IME и использовать отражение, чтобы изменить извлеченное представление. Стандартная InputMethodService содержит частную ссылку на mExtractEditText:)

Я не знаю ответа, который вы ищете, но, возможно, он поместит вас в правильном направлении... или в каком-то направлении.

Ответ 4

Если у вас есть один редактируемый элемент в диалоге, как насчет обработки изменений ориентации и

  • в случае портретной ориентации, покажите диалог
  • в случае ландшафтной ориентации, покажите полноэкранный фрагмент с помощью EditText и Buttons (как указывали другие, отключите полноэкранный режим редактирования для EditText), запросите фокус для EditText, покажите клавиатуру.
  • в случае переключения ориентации отклонить Диалог/показать фрагмент с уже введенным текстом (и наоборот)

Это может быть концептуально хорошо, потому что если пользователь хочет ввести текст, он/она всегда будет в полноэкранном режиме редактирования в альбомной ориентации. И поскольку в исходном диалоге есть только один редактируемый вид, вам явно не нужен диалог, чтобы выбрать что-нибудь еще для редактирования (например, другой EditText).