Criar um método de entrada

Um Editor de método de entrada (IME) é um controle de usuário que permite aos usuários inserir texto. O Android oferece um framework de método de entrada extensível que permite que os aplicativos forneçam aos usuários métodos de entrada alternativos, como teclados na tela ou entrada de fala. Depois de instalar os IMEs, o usuário pode selecionar um nas configurações do sistema e usá-lo em todo o sistema. Somente um IME pode ser ativado por vez.

Para adicionar um IME ao sistema Android, crie um app Android que contenha uma classe que estende InputMethodService. Além disso, você geralmente cria uma atividade de "configurações" que transmite opções para o serviço do IME (editor de método de entrada, na sigla em inglês). Você também pode definir uma interface de configurações que é mostrada como parte das configurações do sistema.

Esta página aborda os seguintes tópicos:

Se você não trabalhou com IMEs, leia o artigo introdutório Métodos de entrada na tela primeiro.

O ciclo de vida do IME

O diagrama a seguir descreve o ciclo de vida de um IME:

Imagem mostrando o ciclo de vida de um IME.
Figura 1. O ciclo de vida de um IME.

As seções abaixo descrevem como implementar a interface e o código associados a um IME que segue esse ciclo de vida.

Declarar os componentes do IME no manifesto

No sistema Android, um IME é um aplicativo para Android que contém um serviço de IME especial. O arquivo de manifesto do app precisa declarar o serviço, solicitar as permissões necessárias, fornecer um filtro de intent que corresponda à ação action.view.InputMethod e disponibilizar metadados que definam as características do IME. Além disso, para fornecer uma interface de configurações que permita ao usuário modificar o comportamento do IME, você pode definir uma atividade de "configurações" que pode ser iniciada nas configurações do sistema.

O snippet a seguir declara um serviço de IME. Ele solicita a permissão BIND_INPUT_METHOD para permitir que o serviço conecte o IME ao sistema, configura um filtro de intent que corresponda à ação android.view.InputMethod e define metadados para o IME:

<!-- Declares the input method service. -->
<service android:name="FastInputIME"
    android:label="@string/fast_input_label"
    android:permission="android.permission.BIND_INPUT_METHOD">
    <intent-filter>
        <action android:name="android.view.InputMethod" />
    </intent-filter>
    <meta-data android:name="android.view.im"
               android:resource="@xml/method" />
</service>

O próximo snippet declara a atividade de configurações do IME. Ele tem um filtro de intent para ACTION_MAIN que indica que essa atividade é o ponto de entrada principal para o aplicativo de IME:

<!-- Optional: an activity for controlling the IME settings. -->
<activity android:name="FastInputIMESettings"
    android:label="@string/fast_input_settings">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
</activity>

Você também pode fornecer acesso às configurações do IME diretamente na IU.

A API do método de entrada

Classes específicas para IMEs são encontradas nos pacotes android.inputmethodservice e android.view.inputmethod. A classe KeyEvent é importante para processar caracteres de teclado.

A parte central de um IME é um componente de serviço, uma classe que estende InputMethodService. Além de implementar o ciclo de vida normal do serviço, essa classe tem callbacks para fornecer a interface do IME (editor de método de entrada, na sigla em inglês), processar entradas do usuário e entregar texto ao campo em foco. Por padrão, a classe InputMethodService fornece a maior parte da implementação para gerenciar o estado e a visibilidade do IME e se comunicar com o campo de entrada atual.

As seguintes classes também são importantes:

BaseInputConnection
Define o canal de comunicação de um InputMethod para o aplicativo que está recebendo a entrada. Você pode usá-lo para ler o texto ao redor do cursor, confirmar o texto para a caixa de texto e enviar eventos de chave brutos para o aplicativo. Os aplicativos precisam estender essa classe em vez de implementar a interface base InputConnection.
KeyboardView
Uma extensão de View que renderiza um teclado e responde a eventos de entrada do usuário. O layout do teclado é especificado por uma instância de Keyboard, que você pode definir em um arquivo XML.

Projetar a IU do método de entrada

Há dois elementos visuais principais para um IME: a visualização de entrada e a visualização de candidatos. Você só precisa implementar os elementos relevantes para o método de entrada que está criando.

Visualização de entrada

A visualização de entrada é a interface em que o usuário insere texto na forma de cliques em teclas, escrita à mão ou gestos. Quando o IME é exibido pela primeira vez, o sistema chama o callback onCreateInputView(). Na implementação desse método, crie o layout que você quer exibir na janela do IME (editor de método de entrada, na sigla em inglês) e retorne-o para o sistema. O snippet a seguir mostra um exemplo de como implementar o método onCreateInputView():

Kotlin

override fun onCreateInputView(): View {
    return layoutInflater.inflate(R.layout.input, null).apply {
        if (this is MyKeyboardView) {
            setOnKeyboardActionListener(this@MyInputMethod)
            keyboard = latinKeyboard
        }
    }
}

Java

@Override
public View onCreateInputView() {
    MyKeyboardView inputView =
        (MyKeyboardView) getLayoutInflater().inflate(R.layout.input, null);

    inputView.setOnKeyboardActionListener(this);
    inputView.setKeyboard(latinKeyboard);

    return inputView;
}

Neste exemplo, MyKeyboardView é uma instância de uma implementação personalizada de KeyboardView que renderiza um Keyboard.

Visualização candidata

A visualização candidata é a interface em que o IME exibe possíveis correções de palavras ou sugestões para o usuário selecionar. No ciclo de vida do IME, o sistema chama onCreateCandidatesView() quando está pronto para exibir a visualização candidata. Na sua implementação desse método, retorne um layout que mostre sugestões de palavras ou retorne nulo se você não quiser mostrar nada. Uma resposta nulada é o comportamento padrão. Portanto, não será necessário implementá-la se você não fornecer sugestões.

Considerações sobre o design da IU

Esta seção descreve algumas considerações de design de interface para IMEs.

Processar vários tamanhos de tela

A interface do IME precisa ser capaz de escalonar para diferentes tamanhos de tela e processar as orientações de paisagem e retrato. No modo IME de tela não cheia, deixe espaço suficiente para que o aplicativo mostre o campo de texto e qualquer contexto associado para que não mais da metade da tela seja ocupada pelo IME. No modo IME de tela cheia, isso não é um problema.

Processar diferentes tipos de entrada

Os campos de texto do Android permitem definir um tipo de entrada específico, como texto em formato livre, números, URLs, endereços de e-mail e strings de pesquisa. Ao implementar um novo IME, detecte o tipo de entrada de cada campo e forneça a interface apropriada para ele. No entanto, você não precisa configurar seu IME para verificar se o usuário digita texto válido para o tipo de entrada. Essa responsabilidade é do aplicativo que tem o campo de texto.

Por exemplo, esta é a interface que o IME Latin fornece para a entrada de texto da Plataforma Android:

Imagem mostrando uma entrada de texto em um IME Latin
Figura 2. Entrada de texto do IME Latin.

E esta é a interface que o IME Latin fornece para a entrada numérica da Plataforma Android:

Imagem mostrando uma entrada numérica em um IME Latin
Figura 3. Entrada numérica do IME Latin.

Quando um campo de entrada recebe foco e seu IME é iniciado, o sistema chama onStartInputView(), transmitindo um objeto EditorInfo que contém detalhes sobre o tipo de entrada e outros atributos do campo de texto. Nesse objeto, o campo inputType contém o tipo de entrada do campo de texto.

O campo inputType é um int que contém padrões de bits para várias configurações de tipo de entrada. Para testar o tipo de entrada do campo de texto, mascare-o com a constante TYPE_MASK_CLASS da seguinte forma:

Kotlin

inputType and InputType.TYPE_MASK_CLASS

Java

inputType & InputType.TYPE_MASK_CLASS

O padrão de bits do tipo de entrada pode ter um de vários valores, incluindo:

TYPE_CLASS_NUMBER
Um campo de texto para inserir números. Como ilustrado na Figura 3, o IME Latin exibe um teclado numérico para campos desse tipo.
TYPE_CLASS_DATETIME
Um campo de texto para inserir uma data e hora.
TYPE_CLASS_PHONE
Um campo de texto para inserir números de telefone.
TYPE_CLASS_TEXT
Um campo de texto para inserir os caracteres compatíveis.

Essas constantes são descritas em mais detalhes na documentação de referência para InputType.

O campo inputType pode conter outros bits que indicam uma variante do tipo de campo de texto, como:

TYPE_TEXT_VARIATION_PASSWORD
Uma variante de TYPE_CLASS_TEXT para inserir senhas. O método de entrada exibe dingbats em vez do texto real.
TYPE_TEXT_VARIATION_URI
Uma variante de TYPE_CLASS_TEXT para inserir URLs da Web e outros identificadores uniformes de recursos (URIs).
TYPE_TEXT_FLAG_AUTO_COMPLETE
Uma variante de TYPE_CLASS_TEXT para inserir texto que o aplicativo preenche automaticamente a partir de um dicionário, pesquisa ou outra facilidade.

Mascarar inputType com a constante adequada ao testar essas variantes. As constantes de máscara disponíveis estão listadas na documentação de referência para InputType.

Enviar texto para o aplicativo

À medida que o usuário insere texto com seu IME, você pode enviar texto para o aplicativo enviando eventos de tecla individuais ou editando o texto ao redor do cursor no campo de texto do aplicativo. Em ambos os casos, use uma instância de InputConnection para entregar o texto. Para acessar essa instância, chame InputMethodService.getCurrentInputConnection().

Editar o texto ao redor do cursor

Ao editar o texto já existente, alguns métodos úteis em BaseInputConnection são:

getTextBeforeCursor()
Retorna um CharSequence contendo o número de caracteres solicitados antes da posição atual do cursor.
getTextAfterCursor()
Retorna um CharSequence contendo o número de caracteres solicitados após a posição atual do cursor.
deleteSurroundingText()
Exclui o número especificado de caracteres antes e depois da posição atual do cursor.
commitText()
Confirma um CharSequence para o campo de texto e define uma nova posição do cursor.

Por exemplo, o snippet a seguir mostra como substituir os quatro caracteres à esquerda do cursor pelo texto "Hello!":

Kotlin

currentInputConnection.also { ic: InputConnection ->
    ic.deleteSurroundingText(4, 0)
    ic.commitText("Hello", 1)
    ic.commitText("!", 1)
}

Java

InputConnection ic = getCurrentInputConnection();
ic.deleteSurroundingText(4, 0);
ic.commitText("Hello", 1);
ic.commitText("!", 1);

Agora é possível escrever textos antes de confirmar

Se o IME prevê texto ou exige várias etapas para compor um glifo ou uma palavra, você pode mostrar o progresso no campo de texto até que o usuário confirme a palavra. Em seguida, é possível substituir a composição parcial pelo texto completo. Você pode dar tratamento especial ao texto adicionando um span a ele ao transmitir para setComposingText().

O snippet a seguir demonstra como mostrar o progresso em um campo de texto:

Kotlin

currentInputConnection.also { ic: InputConnection ->
    ic.setComposingText("Composi", 1)
    ic.setComposingText("Composin", 1)
    ic.commitText("Composing ", 1)
}

Java

InputConnection ic = getCurrentInputConnection();
ic.setComposingText("Composi", 1);
ic.setComposingText("Composin", 1);
ic.commitText("Composing ", 1);

Interceptar eventos de chave de hardware

Mesmo que a janela do método de entrada não tenha foco explícito, ela recebe primeiro os eventos de tecla de hardware e pode consumi-los ou encaminhá-los ao aplicativo. Por exemplo, você pode consumir as teclas direcionais para navegar na interface para seleção de candidatos durante a composição. Você também pode interceptar a tecla "Voltar" para dispensar todas as caixas de diálogo da janela do método de entrada.

Para interceptar teclas de hardware, substitua onKeyDown() e onKeyUp().

Chame o método super() para as chaves que você não quer processar.

Criar um subtipo de IME

Os subtipos permitem que o IME exponha vários modos de entrada e idiomas compatíveis com um IME. Um subtipo pode representar o seguinte:

  • Uma localidade, como en_US ou fr_FR
  • Um modo de entrada, como voz, teclado ou escrita à mão
  • Outros estilos de entrada, formulários ou propriedades específicos do IME, como layouts de teclado QWERTY com 10 teclas ou

O modo pode ser qualquer texto, como "teclado" ou "voz". Um subtipo também pode expor uma combinação delas.

As informações de subtipo são usadas para uma caixa de diálogo do seletor de IME (editor de método de entrada, na sigla em inglês), disponível na barra de notificação e nas configurações do IME. Essas informações também permitem que o framework exiba diretamente um subtipo específico de IME (editor de método de entrada, na sigla em inglês). Ao criar um IME, use o recurso de subtipo, porque ele ajuda o usuário a identificar e alternar entre diferentes idiomas e modos do IME.

Defina subtipos em um dos arquivos de recurso XML do método de entrada usando o elemento <subtype>. O snippet de código abaixo define um IME com dois subtipos: um subtipo de teclado para a localidade em inglês dos EUA e outro para a localidade em francês da França:

<input-method xmlns:android="http://schemas.android.com/apk/res/android"
        android:settingsActivity="com.example.softkeyboard.Settings"
        android:icon="@drawable/ime_icon">
    <subtype android:name="@string/display_name_english_keyboard_ime"
            android:icon="@drawable/subtype_icon_english_keyboard_ime"
            android:languageTag="en-US"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="somePrivateOption=true" />
    <subtype android:name="@string/display_name_french_keyboard_ime"
            android:icon="@drawable/subtype_icon_french_keyboard_ime"
            android:languageTag="fr-FR"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="someVariable=30,someInternalOption=false" />
    <subtype android:name="@string/display_name_german_keyboard_ime" ... />
</input-method>

Para garantir que os subtipos sejam rotulados corretamente na interface, use "%s" para receber um rótulo de subtipo que seja igual ao marcador de localidade do subtipo. Isso é demonstrado nos próximos dois snippets de código. O primeiro snippet mostra parte do arquivo XML do método de entrada:

<subtype
    android:label="@string/label_subtype_generic"
    android:imeSubtypeLocale="en_US"
    android:icon="@drawable/icon_en_us"
    android:imeSubtypeMode="keyboard" />

O snippet seguinte faz parte do arquivo strings.xml do IME. O recurso de string label_subtype_generic, que é usado pela definição da interface do método de entrada para definir o rótulo do subtipo, é definido da seguinte maneira:

<string name="label_subtype_generic">%s</string>

Essa configuração faz com que o nome de exibição do subtipo corresponda à configuração de localidade. Por exemplo, em qualquer localidade em inglês, o nome de exibição é "English (United States)".

Escolher os subtipos de IME na barra de notificações

O sistema Android gerencia todos os subtipos expostos por todos os IMEs. Os subtipos de IME são tratados como modos do IME aos quais pertencem. O usuário pode navegar da barra de notificações ou do app Configurações até um menu de subtipos de IME disponíveis, conforme mostrado na figura abaixo:

Imagem mostrando o menu &quot;Idiomas e entrada&quot;
Figura 4. O menu do sistema Idiomas e entrada.

Escolher os subtipos de IME nas configurações do sistema

O usuário também pode controlar como os subtipos são usados no painel de configurações Idioma e entrada nas configurações do sistema:

Imagem mostrando o menu de seleção de idiomas
Figura 5. O menu do sistema Idiomas

Alternar entre os subtipos de IME

Você pode permitir que os usuários alternem facilmente entre os subtipos de IME fornecendo uma tecla de alternância, como o ícone de idioma em forma de globo no teclado. Isso melhora a usabilidade do teclado e é conveniente para o usuário. Para ativar essa troca, siga estas etapas:

  1. Declare supportsSwitchingToNextInputMethod = "true" nos arquivos de recursos XML do método de entrada. A declaração precisa ser semelhante a este snippet de código:
    <input-method xmlns:android="http://schemas.android.com/apk/res/android"
            android:settingsActivity="com.example.softkeyboard.Settings"
            android:icon="@drawable/ime_icon"
            android:supportsSwitchingToNextInputMethod="true">
    
  2. Chame o método shouldOfferSwitchingToNextInputMethod().
  3. Se o método retornar "verdadeiro", exiba um botão de alternância.
  4. Quando o usuário tocar no botão de alternância, chame switchToNextInputMethod(), transmitindo "falso". O valor "false" instrui o sistema a tratar todos os subtipos igualmente, independente do IME a que eles pertencem. Especificar "true" exige que o sistema percorra subtipos no IME atual.

Considerações gerais sobre o IME

Veja outros pontos a serem considerados ao implementar o IME:

  • Ofereça uma forma para que os usuários ativem opções diretamente da IU do IME.
  • Ofereça uma maneira para os usuários alternarem para um IME diferente diretamente da interface do método de entrada, porque vários IMEs podem estar instalados no dispositivo.
  • Chame a IU do IME rapidamente. Pré-carregue ou carregue sob demanda os recursos grandes para que os usuários vejam o IME assim que tocarem em um campo de texto. Armazene em cache recursos e visualizações para invocações subsequentes do método de entrada.
  • Libere grandes alocações de memória imediatamente após a janela do método de entrada ficar oculta para que os aplicativos tenham memória suficiente para serem executados. Use uma mensagem atrasada para liberar recursos se o IME ficar oculto por alguns segundos.
  • Verifique se os usuários podem inserir o máximo de caracteres possível para o idioma ou a localidade associada ao IME. Os usuários podem usar pontuação em senhas ou nomes de usuário, portanto, seu IME precisa fornecer muitos caracteres diferentes para permitir que os usuários insiram uma senha e acessem o dispositivo.