Como remover o foco sem definir o foco para outro controle?

95

Gosto que minhas IUs sejam intuitivas; cada tela deve orientar o usuário de maneira natural e discreta para a próxima etapa do aplicativo. Exceto isso, eu me esforço para tornar as coisas o mais confusas e confusas possíveis.

Só brincando :-)

Eu tenho três TableRows, cada um contendo um controle EditText somente leitura e não focalizável e um botão à sua direita. Cada botão inicia a mesma atividade, mas com um argumento diferente. O usuário faz uma seleção lá e a subatividade termina, preenchendo o apropriado EditTextcom a seleção do usuário.

É o mecanismo clássico de valores em cascata; cada seleção restringe as opções disponíveis para a próxima seleção, etc. Portanto, estou desabilitando os dois controles em cada uma das próximas linhas até que EditText na linha atual contenha um valor.

Preciso fazer uma de duas coisas, nesta ordem de preferência:

  1. Quando um botão é clicado, imediatamente remove o foco sem definir o foco para um botão diferente
  2. Defina o foco para o primeiro botão quando a atividade começar

O problema se manifesta após o retorno da subatividade; o botão clicado mantém o foco.

Re: nº 1 acima - não parece ser um removeFocus()método ou algo semelhante

Re: nº 2 acima - posso usar requestFocus()para definir o foco para o botão na próxima linha, e isso funciona depois que a subatividade retorna, mas por algum motivo não funciona na atividade pai onCreate().

Preciso da consistência da IU em qualquer direção - nenhum botão tem foco depois que a subatividade termina ou cada botão recebe o foco dependendo de seu lugar no fluxo lógico, incluindo o primeiro (e único) botão ativo antes de qualquer seleção.

InteXX
fonte

Respostas:

173

Usar clearFocus () não parecia funcionar para mim, como você descobriu (viu nos comentários de outra resposta), mas o que funcionou para mim no final foi adicionar:

<LinearLayout 
    android:id="@+id/my_layout" 
    android:focusable="true" 
    android:focusableInTouchMode="true" ...>

ao meu Layout View de nível superior (um layout linear). Para remover o foco de todos os botões / textos de edição, etc, você pode simplesmente fazer

LinearLayout myLayout = (LinearLayout) activity.findViewById(R.id.my_layout);
myLayout.requestFocus();

Solicitar o foco não adianta nada, a menos que eu defina a visualização como focalizável.

camarão
fonte
Por um breve momento, pensei que isso poderia funcionar. "Quão bela em sua simplicidade!" Eu disse a mim mesmo. Infelizmente, não era para ser. Mesmo quando eu removo requestFocus (), o botão da próxima linha obtém o foco. Isso seria bom como uma segunda preferência, mas apenas se eu pudesse de alguma forma definir o foco no primeiro botão em onCreate ().
InteXX
@InteXX: Eu me deparei com esse problema novamente hoje e encontrei uma solução. Sei que você escolheu um caminho diferente agora, mas experimente isso se algum dia se encontrar no mesmo barco novamente!
actionshrimp
"Sei que você escolheu um caminho diferente agora" Ainda não é tarde para voltar :-) Vou tentar e avisar, obrigado.
InteXX
Droga. Quase, mas não exatamente. Sim, tira o foco do último botão clicado, mas também impede que QUALQUER botão ganhe foco. Isso significaria que meus usuários sem toque não conseguiriam clicar em um botão. Está fazendo o mesmo por você?
InteXX
6
Isso não funcionará em ScrollView. Se sua vista superior for ScrollView, use-a em seu filho.
Uroš Podkrižnik
79

Pergunta antiga, mas me deparei com ela quando tive um problema semelhante e pensei em compartilhar o que acabei fazendo.

A visão que ganhou foco era diferente a cada vez, então usei o muito genérico:

View current = getCurrentFocus();
if (current != null) current.clearFocus();
willlma
fonte
12
Maneira elegante de fazer isso. Um pode querer verificar para nulo um valor de getCurrentFocus () antes de chamar clearFocus ()
Vering
4
+1 Sim, você terá que jogar esse cheque nulo! - Por que o Java não pode simplesmente implementar um operador seguro-traversal? Seria tão simples quanto getCurrentFocus()?.clearFocus();, tão bonito e elegante: - /
Levita
1
@willlma: Sim, foi exatamente isso que eu quis dizer.
Vering de
5
@Levit Very Swift-like. :-) Mas mesmo que o Java implementasse tal operador, o Android ainda levaria 4 anos para alcançá-lo.
Greg Brown
2
@Levit, você provavelmente já encontrou, mas Kotlin.
prodaea
9
  1. Você pode usar View.clearFocus().

  2. Use View.requestFocus()chamadas de onResume().

siliconeagle
fonte
@siliconeagle: Isso é muito, MUITO estranho. (Obrigado pelo ponteiro para clearFocus () aliás - eu não sabia disso.) Quando clico no primeiro botão, faço uma seleção e depois retorno, o primeiro botão não pode mais receber o foco. Não estou definindo o foco para este botão em nenhum lugar do meu código. Gostaria de mostrar meu código, mas não há caracteres suficientes disponíveis nesta janela de edição e, sendo novo no SO, não tenho certeza se devo inserir outra resposta apenas para poder postar o código.
InteXX
@siliconeagle: Infelizmente, clearFocus () não funciona - os botões mantêm o foco quando a subatividade retorna.
InteXX
@siliconeagle: OK, não se preocupe com o primeiro comentário. Eu estava usando setFocusable (false) no evento de clique do botão e esqueci de ligá-lo novamente depois que a subatividade retornou. clearFocus () ainda não tem efeito - eu me pergunto por quê? Estou chamando-o em todos os botões de onActivityResult (), mas o botão clicado ainda mantém o foco. Ímpar.
InteXX
@siliconeagle: @actionshrimp: Decidi eliminar todos os comandos e configurações relacionados ao foco. Minha intenção original era evitar o foco em botões desabilitados, mas se é isso que custa, não vale a pena. Sem requestFocus (), clearFocus () ou setFocusable (), nenhum botão retém o foco após o retorno da subatividade. É o menor de dois gorgulhos - acho que terei que conviver com o usuário sendo capaz de definir o foco para um botão desabilitado.
InteXX
elementos desativados não podem ser focalizados AFAIK ... use setEnabled (false)
siliconeagle
8

android:descendantFocusability="beforeDescendants"

usar o seguinte na atividade com algumas opções de layout abaixo pareceu funcionar conforme desejado.

 getWindow().getDecorView().findViewById(android.R.id.content).clearFocus();

em conexão com os seguintes parâmetros na visualização raiz.

<?xml
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:descendantFocusability="beforeDescendants" />

https://developer.android.com/reference/android/view/ViewGroup#attr_android:descendantFocusability

Responda com agradecimento a: https://forums.xamarin.com/discussion/1856/how-to-disable-auto-focus-on-edit-text

Sobre windowSoftInputMode

Há ainda outro ponto de discórdia a ser considerado. Por padrão, o Android atribuirá automaticamente o foco inicial ao primeiro EditText ou controle focalizável em sua atividade. Segue-se naturalmente que o InputMethod (geralmente o teclado virtual) responderá ao evento de foco mostrando-se. O atributo windowSoftInputMode em AndroidManifest.xml, quando definido como stateAlwaysHidden, instrui o teclado a ignorar este foco inicial atribuído automaticamente.

<activity
    android:name=".MyActivity"
    android:windowSoftInputMode="stateAlwaysHidden"/>

ótima referência

CrandellWS
fonte
6
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:id="@+id/ll_root_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">

LinearLayout llRootView = findViewBindId(R.id.ll_root_view);
llRootView.clearFocus();

Eu uso isso quando já terminei de atualizar as informações do perfil e remover todo o foco de EditText no meu layout

====> Atualizar: No conteúdo do layout pai, minha linha de adicionar EditText:

android:focusableInTouchMode="true"
Ho Luong
fonte
3

Que tal apenas adicionar android:windowSoftInputMode="stateHidden"sua atividade no manifesto.

Retirado de um homem inteligente que comentou sobre isso: https://stackoverflow.com/a/2059394/956975

Marienke
fonte
2

Em primeiro lugar, funcionará 100% ........

  1. onResume()Método de criação .
  2. Dentro dele, onResume()encontre a visão que está focalizando continuamente findViewById().
  3. Dentro deste onResume()conjunto requestFocus()para esta vista.
  4. Dentro deste onResume()conjunto clearFocuspara esta vista.
  5. Vá no xml do mesmo layout e encontre a vista superior que você deseja que seja focada e defina focusableverdadeira e focusableInTuchverdadeira.
  6. Dentro dele, onResume()encontre a vista superior acima porfindViewById
  7. Dentro deste onResume()conjunto requestFocus()para esta vista no passado.
  8. E agora aproveite ...
Vishambar Pandey
fonte
2

Tentei desativar e ativar o foco para visualização e funcionou para mim (o foco foi redefinido):

focusedView.setFocusable(false);
focusedView.setFocusableInTouchMode(false);
focusedView.setFocusable(true);
focusedView.setFocusableInTouchMode(true);
Serhiy
fonte
1

Você pode tentar desligar a capacidade da Activity principal de salvar seu estado (fazendo com que ela esqueça qual controle tinha texto e qual tinha foco). Você precisará ter alguma outra maneira de lembrar o que seu EditText tem e preenchê-lo novamente emResume (). Inicie suas sub-Activities com startActivityForResult () e crie um manipulador onActivityResult () em sua Activity principal que atualizará o EditText corretamente. Desta forma, você pode definir o botão adequado que deseja colocar em foco emResume () ao mesmo tempo em que preenche novamente o EditText usando um myButton.post (new Runnable () {run () {myButton.requestFocus ();}});

O método View.post () é útil para definir o foco inicialmente porque esse executável será executado depois que a janela for criada e as coisas se acalmarem, permitindo que o mecanismo de foco funcione corretamente naquele momento. Tentar definir o foco durante onCreate / Start / Resume () geralmente apresenta problemas, descobri.

Observe que este é um pseudocódigo e não foi testado, mas é uma direção possível que você pode tentar.

Uncle Code Monkey
fonte
Pensando bem ... tente usar apenas as coisas View.post () primeiro em vez de desligar o estado salvo da atividade. Isso pode resolver o problema por si só.
Uncle Code Monkey
Infelizmente, não tenho mais uma maneira fácil de testar isso para nós. Desde então, decidi abandonar o modelo de código nativo e ir com HTML5 / CSS3 para aplicativos móveis e, portanto, reorganizei meu sistema de acordo. Mas obrigado pelo que parece ser uma resposta muito detalhada. Eu votei.
InteXX
1
android:focusableInTouchMode="true"
android:focusable="true"
android:clickable="true"

Adicione-os ao seu ViewGroup que inclui seu EditTextView. Funciona corretamente com o meu Layout de restrição. Espero que esta ajuda

Tánh Lee
fonte
0

Experimente o seguinte (ligando clearAllEditTextFocuses();)

private final boolean clearAllEditTextFocuses() {
    View v = getCurrentFocus();
    if(v instanceof EditText) {
        final FocusedEditTextItems list = new FocusedEditTextItems();
        list.addAndClearFocus((EditText) v);

        //Focus von allen EditTexten entfernen
        boolean repeat = true;
        do {
            v = getCurrentFocus();
            if(v instanceof EditText) {
                if(list.containsView(v))
                    repeat = false;
                else list.addAndClearFocus((EditText) v);
            } else repeat = false;
        } while(repeat);

        final boolean result = !(v instanceof EditText);
        //Focus wieder setzen
        list.reset();
        return result;
    } else return false;
}

private final static class FocusedEditTextItem {

    private final boolean focusable;

    private final boolean focusableInTouchMode;

    @NonNull
    private final EditText editText;

    private FocusedEditTextItem(final @NonNull EditText v) {
        editText = v;
        focusable = v.isFocusable();
        focusableInTouchMode = v.isFocusableInTouchMode();
    }

    private final void clearFocus() {
        if(focusable)
            editText.setFocusable(false);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(false);
        editText.clearFocus();
    }

    private final void reset() {
        if(focusable)
            editText.setFocusable(true);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(true);
    }

}

private final static class FocusedEditTextItems extends ArrayList<FocusedEditTextItem> {

    private final void addAndClearFocus(final @NonNull EditText v) {
        final FocusedEditTextItem item = new FocusedEditTextItem(v);
        add(item);
        item.clearFocus();
    }

    private final boolean containsView(final @NonNull View v) {
        boolean result = false;
        for(FocusedEditTextItem item: this) {
            if(item.editText == v) {
                result = true;
                break;
            }
        }
        return result;
    }

    private final void reset() {
        for(FocusedEditTextItem item: this)
            item.reset();
    }

}
5chw4hn
fonte