Como atualizar dinamicamente um ListView no Android [fechado]

159

No Android, como ListViewfiltrar com base na entrada do usuário, onde os itens mostrados são atualizados dinamicamente com base no TextViewvalor?

Estou procurando algo parecido com isto:

-------------------------
| Text View             |
-------------------------
| List item             |
| List item             |
| List item             |
| List item             |
|                       |
|                       |
|                       |
|                       |
-------------------------
Hamy
fonte
7
Eu indico para reabertura. Eu atualizei o texto para tornar isso mais de uma pergunta, e é claramente um recurso da comunidade valioso como respostas ainda estão chegando
Hamy
Reabra a pergunta. É claramente útil.
criar o seu alerta

Respostas:

286

Primeiro, você precisa criar um layout XML que tenha um EditText e um ListView.

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <!-- Pretty hint text, and maxLines -->
    <EditText android:id="@+building_list/search_box" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="type to filter"
        android:inputType="text"
        android:maxLines="1"/>

    <!-- Set height to 0, and let the weight param expand it -->
    <!-- Note the use of the default ID! This lets us use a 
         ListActivity still! -->
    <ListView android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1" 
         /> 

</LinearLayout>

Isso organizará tudo corretamente, com um bom EditText acima do ListView. Em seguida, crie uma ListActivity como faria normalmente, mas adicione uma setContentView()chamada no onCreate()método para usar nosso layout declarado recentemente. Lembre-se de que identificamos o ListViewespecialmente com android:id="@android:id/list". Isso permite que oListActivity saber o que ListViewqueremos usar em nosso layout declarado.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.filterable_listview);

        setListAdapter(new ArrayAdapter<String>(this,
                       android.R.layout.simple_list_item_1, 
                       getStringArrayList());
    }

A execução do aplicativo agora deve mostrar o anterior ListView, com uma boa caixa acima. Para fazer com que essa caixa faça alguma coisa, precisamos extrair a entrada dela e fazer com que essa entrada filtre a lista. Embora muitas pessoas tenham tentado fazer isso manualmente, a maioria das ListView Adapter classes vem com um Filterobjeto que pode ser usado para executar a filtragem automaticamente. Nós só precisamos canalizar a entrada do EditTextpara o Filter. Acontece que é bem fácil. Para executar um teste rápido, adicione esta linha à sua onCreate()chamada

adapter.getFilter().filter(s);

Observe que você precisará salvar sua ListAdapterem uma variável para fazer esse trabalho - eu salvei meuArrayAdapter<String> anterior em uma variável chamada 'adaptador'.

O próximo passo é obter a entrada do EditText. Isso realmente requer um pouco de reflexão. Você pode adicionar um OnKeyListener()ao seu EditText. No entanto, esse ouvinte recebe apenas alguns eventos importantes . Por exemplo, se um usuário digitar 'wyw', o texto preditivo provavelmente recomendará 'eye'. Até que o usuário escolha 'wyw' ou 'eye', você OnKeyListenernão receberá um evento importante. Alguns podem preferir essa solução, mas achei frustrante. Eu queria todos os eventos principais, então tive a opção de filtrar ou não. A solução é a TextWatcher. Basta criar e adicionar TextWatchera ao EditTexte passar a ListAdapter Filtersolicitação de filtro sempre que o texto for alterado. Lembre-se de remover o TextWatcherin OnDestroy()! Aqui está a solução final:

private EditText filterText = null;
ArrayAdapter<String> adapter = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.filterable_listview);

    filterText = (EditText) findViewById(R.id.search_box);
    filterText.addTextChangedListener(filterTextWatcher);

    setListAdapter(new ArrayAdapter<String>(this,
                   android.R.layout.simple_list_item_1, 
                   getStringArrayList());
}

private TextWatcher filterTextWatcher = new TextWatcher() {

    public void afterTextChanged(Editable s) {
    }

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
    }

    public void onTextChanged(CharSequence s, int start, int before,
            int count) {
        adapter.getFilter().filter(s);
    }

};

@Override
protected void onDestroy() {
    super.onDestroy();
    filterText.removeTextChangedListener(filterTextWatcher);
}
Hamy
fonte
7
Existe alguma maneira simples de filtrar o ListView em "contém" em vez de "começar com" a moda como esta solução faz?
Viktor Brešan
12
Viktor - Se as palavras em que você está interessado são separadas por espaços, isso será feito automaticamente. Caso contrário, não realmente. Provavelmente, a maneira mais fácil seria subclassificar o adaptador estendendo-o e substituir o método getFilter para retornar um objeto de filtro definido por você. Consulte github.com/android/platform_frameworks_base/blob/master/core/… para entender como o ArrayFilter padrão funciona - seria simples copiar 95% desse código e alterar as linhas 479 e 486
Hamy
2
Hamy, excelente redação! Mas eu tenho uma pergunta: eu implementei isso e, após cada letra que digito, o ListView desaparece por alguns segundos e depois volta, filtrado. Você já experimentou isso? Minha intuição é que é porque eu tenho mais de 600 itens na lista, com funções toString () não triviais.
lowellk
2
No modo paisagem em uma tela menor, o teclado EditText + ocupa a tela inteira e o ListView não é visível! Qualquer solução?
Martin Konicek
4
Isso é realmente necessário? Lembre-se de remover o TextWatcher em OnDestroy ()
Jojo
10

executar o programa causará um fechamento forçado.

Troquei a linha:

android: id = "@ + lista de construção / caixa de pesquisa"

com

android: id = "@ + id / caixa de pesquisa"

poderia ser esse o problema? Para que serve o '@ + building_list'?

j7nn7k
fonte
Johe, quando você diz R.id.something, o algo existe no id porque você disse android: id = "@ + id / search_box". Se você diz android: id = "@ + building_list / search_box", no código você pode chamar findViewById (R.building_list.search_box); Qual é a exceção de nível superior que você está recebendo? Este código é copiado w compilação de teste / oa, então eu provavelmente deixou pelo menos um erro em algum lugar
Hamy
Olá Hamy, você referenciou "@ + building_list / search_box" no código com "filterText = (EditText) findViewById (R.id.search_box);" Por isso eu estava pensando.
J7nn7k
1
O forceclose occorrs ao iniciar a digitação. Parte do erro: ERROR / AndroidRuntime (188): java.lang.NullPointerException 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): em xxx.com.ListFilter $ 1. onTextChanged (ListFilter.java:46) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): at android.widget.TextView.sendOnTextChanged (TextView.java:6102) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): em android.widget.TextView.handleTextChanged (TextView.java:6143) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): em android.widget.TextView $ ChangeWatcher.onTextChanged (TextView.java : 6286)
j7nn7k
Johe, opa - parece que eu estava tentando modificar o código para me livrar da lista + building_list (porque esse é um ponto de confusão), mas não o peguei em todos os lugares. Obrigado pela dica! Apenas mudá-lo para @ + id deve corrigi-lo
Hamy
4

tive um problema com a filtragem, que os resultados foram filtrados, mas não restaurados !

antes de filtrar (início da atividade), criei um backup da lista. (apenas outra lista, contendo os mesmos dados)

na filtragem, o filtro e o listadapter são conectados à lista principal.

mas o próprio filtro usou os dados da lista de backups.

isso garantiu no meu caso, que a lista fosse atualizada imediatamente e, mesmo ao excluir os caracteres de termo de pesquisa, a lista é restaurada com êxito em todos os casos :)

obrigado por esta solução de qualquer maneira.

cV2
fonte