EditableTextable dentro do ListView

121

Eu gastei cerca de 6 horas nisso até agora e atingi nada além de obstáculos. A premissa geral é que há alguma linha em ListView(seja gerada pelo adaptador ou adicionada como uma visualização de cabeçalho) que contém um EditTextwidget e a Button. Tudo o que eu quero fazer é poder usar o jogball / setas, para navegar no seletor para itens individuais como o normal, mas quando chego a uma linha específica - mesmo que eu precise identificar explicitamente a linha - que tem foco criança, quero que essa criança se concentre em vez de indicar a posição com o seletor.

Eu tentei muitas possibilidades e até agora não tive sorte.

layout:

<ListView
    android:id="@android:id/list" 
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent"
    />

Visualização do cabeçalho:

EditText view = new EditText(this);
listView.addHeaderView(view, null, true);

Supondo que haja outros itens no adaptador, o uso das teclas de seta move a seleção para cima / baixo na lista, conforme o esperado; mas, ao chegar à linha do cabeçalho, ela também é exibida com o seletor e não há como focar noEditText uso do jogball. Nota: tocando no EditText vai centrar-se nesse ponto, no entanto, que se baseia em uma tela sensível ao toque, o que não deve ser um requisito.

ListViewaparentemente possui dois modos nesse sentido:
1 setItemsCanFocus(true).: o seletor nunca é exibido, mas oEditText foco pode ser obtido ao usar as setas. O algoritmo de busca por foco é difícil de prever e nenhum feedback visual (em qualquer linha: ter filhos focáveis ​​ou não) sobre o item selecionado, os quais podem proporcionar ao usuário uma experiência inesperada.
2 setItemsCanFocus(false).: o seletor é sempre desenhado no modo não sensível ao toque e EditTextnunca consegue obter foco - mesmo se você tocar nele.

Para piorar a situação, a chamada editTextView.requestFocus()retorna verdadeira, mas na verdade não dá o foco EditText.

O que estou imaginando é basicamente um híbrido de 1 e 2, onde, em vez da configuração da lista, se todos os itens forem focáveis ​​ou não, eu quero definir a focagem para um único item da lista, para que o seletor faça a transição sem problemas da seleção de linha inteira para itens não focáveis ​​e percorrer a árvore de foco para itens que contêm filhos focáveis.

Algum comprador?

Joe
fonte

Respostas:

101

Desculpe, respondeu minha própria pergunta. Pode não ser a solução mais correta ou mais elegante, mas funciona para mim e oferece uma experiência ao usuário bastante sólida. Examinei o código do ListView para ver por que os dois comportamentos são tão diferentes e me deparei com isso no ListView.java:

    public void setItemsCanFocus(boolean itemsCanFocus) {
        mItemsCanFocus = itemsCanFocus;
        if (!itemsCanFocus) {
            setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        }
    }

Portanto, ao ligar setItemsCanFocus(false), também define a focagem descendente, de modo que nenhuma criança possa se concentrar. Isso explica por que eu não podia simplesmente alternar mItemsCanFocusno OnItemSelectedListener do ListView - porque o ListView estava bloqueando o foco de todos os filhos.

O que eu tenho agora:

<ListView
    android:id="@android:id/list" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent"
    android:descendantFocusability="beforeDescendants"
    />

Uso beforeDescendantsporque o seletor só será desenhado quando o ListView em si (não um filho) tiver foco; portanto, o comportamento padrão precisa ser que o ListView focalize primeiro e desenhe os seletores.

Em seguida, no OnItemSelectedListener, já que sei qual visualização do cabeçalho quero substituir o seletor (seria necessário mais trabalho para determinar dinamicamente se uma determinada posição contém uma visualização focalizável), posso alterar a focalização descendente e definir o foco no EditText. E quando eu sair desse cabeçalho, altere-o novamente.

public void onItemSelected(AdapterView<?> listView, View view, int position, long id)
{
    if (position == 1)
    {
        // listView.setItemsCanFocus(true);

        // Use afterDescendants, because I don't want the ListView to steal focus
        listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        myEditText.requestFocus();
    }
    else
    {
        if (!listView.isFocused())
        {
            // listView.setItemsCanFocus(false);

            // Use beforeDescendants so that the EditText doesn't re-take focus
            listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
            listView.requestFocus();
        }
    }
}

public void onNothingSelected(AdapterView<?> listView)
{
    // This happens when you start scrolling, so we need to prevent it from staying
    // in the afterDescendants mode if the EditText was focused 
    listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}

Observe as setItemsCanFocuschamadas comentadas . Com essas chamadas, obtive o comportamento correto, mas fiz com que o setItemsCanFocus(false)foco pulasse do EditText, para outro widget fora do ListView, retorne ao ListView e exibi o seletor no próximo item selecionado, e esse foco saltador foi perturbador. A remoção da alteração ItemsCanFocus e a alternância da focagem descendente me deram o comportamento desejado. Todos os itens desenham o seletor normalmente, mas ao chegar à linha com o EditText, ele se concentra no campo de texto. Então, ao continuar fora desse EditText, ele começou a desenhar o seletor novamente.

Joe
fonte
muito legal, ainda não testei. Você já testou nas versões 1.5, 1.6 e 3.0?
Rafael Sanches
Rafael Sanches: Não toquei no projeto desde a versão 2.1, mas, na época, foi confirmado o trabalho nas versões 1.5, 1.6 e 2.1. Não garanto que ele ainda funcione na versão 2.2 ou posterior.
Joe
13
só precisava de android: descendantFocusability = "afterDescendants" - de qualquer maneira +1
Kellogs
5
@ kellogs: sim, descendantFocusability="afterDescendants"permitirá que o seu EditText tenha foco dentro do ListView, mas você não terá um seletor de itens da lista enquanto estiver navegando com um dpad. Minha tarefa era ter o seletor de itens da lista em todas as linhas, exceto aquela com o EditText. Ainda bem que ajudou. FWIW, acabamos reavaliando essa implementação e decidimos que um foco dentro de um ListView não é apenas um design de interface do usuário idiomático para Android, então descartamos a ideia em favor de uma abordagem mais amigável para o Android.
26511 Joe
5
Isso foi testado no Ice Cream Sandwich? Não consigo fazer funcionar. Obrigado.
Rajat Anantharam
99

Isso me ajudou.
No seu manifesto:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
logan
fonte
3
Não tenho certeza de ver a relevância. A configuração de windowSoftInputMode apenas altera a maneira como o IME ajusta o restante do conteúdo da janela quando está aberto. Não permite alterar seletivamente o tipo de foco do ListView. Você pode explicar um pouco mais, como isso se relaciona com o caso de uso inicial?
21711 Joe
1
@ Joe Quando o IME é aberto, o cursor - e provavelmente o foco também - apenas salta na minha tela, tornando impossível a inserção de texto. O seu OnItemSelectedListenernão muda isso. No entanto, a solução simples de Iogan funciona como um encanto, obrigado!
Gubbel #
2
@Gubbel: De fato, isso não mudaria nada, porque a pergunta original era sobre algo completamente diferente :) A correção de Logan feliz por trabalhar com o que você estava procurando, mas simplesmente não está nem remotamente relacionada à pergunta.
315 Joe
8
O uso dessa mais a android:descendantFocusabilitypropriedade de Joe colocou meus EditTexts dentro de uma ListViewresolução adequada do teclado, com os votos positivos de ambos. android:descendantFocusabilitypor si só, não funcionou e eu não estava remotamente entusiasmado @Overriding onItemSelectedpor todos os 14 EditTexts com os quais tenho que lidar. :) Obrigado!
Thomson Comer
Isso funcionou para mim, mas observe que se você estiver usando TabHost ou TabActivity, é necessário definir android: windowSoftInputMode = ”AdjustPan" para a definição de TabActivity no manifesto.
kiduxa
18

Minha tarefa era implementar o ListViewque se expande quando clicado. O espaço adicional mostra EditTextonde você pode inserir algum texto. O aplicativo deve funcionar em 2.2+ (até 4.2.2 no momento em que este foi escrito)

Tentei várias soluções neste post e em outras que encontrei; testou-os em dispositivos 2.2 até 4.2.2. Nenhuma das soluções foi satisfatória em todos os dispositivos 2.2+, cada solução apresentou problemas diferentes.

Eu queria compartilhar minha solução final:

  1. defina listview como android:descendantFocusability="afterDescendants"
  2. defina listview como setItemsCanFocus(true);
  3. defina sua atividade como android:windowSoftInputMode="adjustResize" Muitas pessoas sugerem, adjustPanmas adjustResizeoferece ux imho muito melhor, apenas teste isso no seu caso. Com adjustPanvocê, os itens de lista inferiores serão obscurecidos, por exemplo. Os documentos sugerem que ("Isso geralmente é menos desejável do que redimensionar"). Também na 4.0.4, após o usuário começar a digitar no teclado virtual, a tela se move para o topo.
  4. no 4.2.2, adjustResizeexistem alguns problemas com o foco do EditText. A solução é aplicar a solução rjrjr a partir deste encadeamento. Parece assustador, mas não é. E isso funciona. Apenas tente.

Adicional 5. Devido à atualização do adaptador (devido ao redimensionamento da exibição) quando o EditTextfoco se concentra nas versões anteriores do HoneyComb, encontrei um problema com as exibições invertidas: obtendo o item View for ListView / ordem inversa no 2.2; trabalha em 4.0.3

Se você estiver fazendo algumas animações, poderá alterar o comportamento adjustPandas versões anteriores ao favo de mel para que o redimensionamento não seja acionado e o adaptador não atualize as visualizações. Você só precisa adicionar algo assim

if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

Tudo isso fornece ux aceitável em dispositivos 2.2 - 4.2.2. Espero que isso poupe tempo às pessoas, pois levei pelo menos várias horas para chegar a essa conclusão.

AndroidGecko
fonte
1
apenas primeira e segunda suficiente passo no meu caso
Kalpesh Lakhani
Funciona perfeitamente com meu xElement.setOnClickListener (..) em ArrayAdapter e um ListView.setOnItemClickListener (...) - finalmente !! Big thx.
Javatar 27/08/15
10

Isso salvou minha vida --->

  1. defina esta linha

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Em seu manifesto na tag de atividade, digite isto ->

    <activity android:windowSoftInputMode="adjustPan">

Sua intenção habitual

Ravi Ranjan
fonte
7

Estamos tentando isso em uma lista curta que não faz nenhuma reciclagem de exibição. Por enquanto, tudo bem.

XML:

<RitalinLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
  <ListView
      android:id="@+id/cart_list"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:scrollbarStyle="outsideOverlay"
      />
</RitalinLayout>

Java:

/**
 * It helps you keep focused.
 *
 * For use as a parent of {@link android.widget.ListView}s that need to use EditText
 * children for inline editing.
 */
public class RitalinLayout extends FrameLayout {
  View sticky;

  public RitalinLayout(Context context, AttributeSet attrs) {
    super(context, attrs);

    ViewTreeObserver vto = getViewTreeObserver();

    vto.addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
      @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) {
        if (newFocus == null) return;

        View baby = getChildAt(0);

        if (newFocus != baby) {
          ViewParent parent = newFocus.getParent();
          while (parent != null && parent != parent.getParent()) {
            if (parent == baby) {
              sticky = newFocus;
              break;
            }
            parent = parent.getParent();
          }
        }
      }
    });

    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override public void onGlobalLayout() {
        if (sticky != null) {
          sticky.requestFocus();
        }
      }
    });
  }
}
rjrjr
fonte
Ligeiramente modificada, esta solução funciona para mim depois de 2 dias de @ # ( : if (sticky = null) {sticky.RequestFocus (); sticky.RequestFocusFromTouch (); pegajosa = null;}!
Chris van de Steeg
4

esta postagem correspondia exatamente às minhas palavras-chave. Eu tenho um cabeçalho ListView com uma pesquisa EditText e um botão de pesquisa.

Para dar foco ao EditText depois de perder o foco inicial, o único HACK que encontrei é:

    searchText.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {
            // LOTS OF HACKS TO MAKE THIS WORK.. UFF...
            searchButton.requestFocusFromTouch();
            searchText.requestFocus();
        }
    });

Perdi muitas horas e não é uma solução real. Espero que ajude alguém difícil.

Rafael Sanches
fonte
2

Se a lista for dinâmica e contiver widgets focalizáveis, a opção certa é usar o RecyclerView em vez do ListView IMO.

As soluções alternativas que definem adjustPan, FOCUS_AFTER_DESCENDANTSou lembram-se manualmente da posição focada, são de fato apenas soluções alternativas. Eles têm estojos de canto (rolagem + problemas no teclado virtual, mudança de posição de cursor no EditText). Eles não mudam o fato de que o ListView cria / destrói visualizações em massa durante notifyDataSetChanged.

Com o RecyclerView, você notifica sobre inserções, atualizações e exclusões individuais. A exibição focada não está sendo recriada, portanto, não há problemas com os controles de formulário que perdem o foco. Como um bônus adicional, o RecyclerView anima as inserções e remoções de itens da lista.

Aqui está um exemplo de documentação oficial sobre como começar RecyclerView: Guia do desenvolvedor - Crie uma lista com o RecyclerView

Pēteris Caune
fonte
1

algumas vezes, quando você usa android:windowSoftInputMode="stateAlwaysHidden"atividades manifestas ou xml, esse tempo perde o foco do teclado. Portanto, primeiro verifique essa propriedade no seu xml e manifesto, se houver, remova-a. Depois de adicionar essas opções para manifestar o arquivo na atividade paralela android:windowSoftInputMode="adjustPan"e adicionar essa propriedade à listview em xmlandroid:descendantFocusability="beforeDescendants"


fonte
0

Outra solução simples é definir seu onClickListener, no método getView (..), do seu ListAdapter.

public View getView(final int position, View convertView, ViewGroup parent){
    //initialise your view
    ...
    View row = context.getLayoutInflater().inflate(R.layout.list_item, null);
    ...

    //define your listener on inner items

    //define your global listener
    row.setOnClickListener(new OnClickListener(){
        public void onClick(View v) {
            doSomethingWithViewAndPosition(v,position);
        }
    });

    return row;

Dessa forma, sua linha é clicável e sua visão interna também :)

Weber Antoine
fonte
1
A questão está relacionada ao foco, não à clicabilidade.
11111 Joe
0

A parte mais importante é obter o foco trabalhando para a célula da lista. Especialmente para lista no Google TV, isso é essencial:

O método setItemsCanFocus da exibição em lista faz o truque:

...
mPuzzleList = (ListView) mGameprogressView.findViewById(R.id.gameprogress_puzzlelist);
mPuzzleList.setItemsCanFocus(true);
mPuzzleList.setAdapter(new PuzzleListAdapter(ctx,PuzzleGenerator.getPuzzles(ctx, getResources(), version_lite)));
...

Minha lista de células xml começa da seguinte maneira:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/puzzleDetailFrame"
             android:focusable="true"
             android:nextFocusLeft="@+id/gameprogress_lessDetails"
             android:nextFocusRight="@+id/gameprogress_reset"
...

nextFocusLeft / Right também são importantes para a navegação no D-Pad.

Para mais detalhes, confira as ótimas outras respostas.

Michael Biermann
fonte
0

Acabei de encontrar outra solução. Eu acredito que é mais um hack do que uma solução, mas funciona no Android 2.3.7 e Android 4.3 (eu até testei o bom e velho D-pad)

inicie sua webview como de costume e adicione: (obrigado Michael Bierman)

listView.setItemsCanFocus(true);

Durante a chamada getView:

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });
alaeri
fonte
o que é visualizar aqui em view.requestFocus?
Narendra Singh
esta é uma velha resposta, mas referiu-se à vista no âmbito dado como um argumento para o OnFocusChangeListener Eu acredito
alaeri
1
Isso causa um loop na mudança de foco.
Morteza Rastgoo 14/02
0

Apenas tente isso

android:windowSoftInputMode="adjustNothing"

no

atividade

seção do seu manifesto. Sim, ele ajusta nada, o que significa que o editText permanecerá onde está quando o IME for aberto. Mas isso é apenas um pequeno inconveniente que ainda resolve completamente o problema de perder o foco.

Tor_Gash
fonte