Filtro de visualização de lista Android

93

Eu criei uma visualização de lista no Android e quero adicionar texto de edição acima da lista e quando o usuário inserir o texto, a lista será filtrada de acordo com a entrada do usuário

Alguém pode me dizer por favor se existe uma maneira de filtrar o adaptador de lista no Android?

Amira Elsayed Ismail
fonte
1
Olá, tente este exemplo, Exemplo um, e o segundo, Exemplo 2 Eu implementei o mesmo com base nestes tutoriais ... Espero que isso ajude você
Pragnani
5
A melhor resposta simplesmente não forneceu informações suficientes para mim. Esta resposta a uma pergunta semelhante tem mais contexto e foi exatamente informação suficiente para me resolver.
James Skemp
Estou usando o modo de exibição do reciclador. Quero filtrar os registros, mas estou usando botões personalizados para inserir texto de edição, como teclado personalizado, mas estou recebendo atraso no clique rápido do botão, você pode me ajudar a sair disso. Tenho registros em milhares
Sagar

Respostas:

139

Adicione um EditText no topo de sua exibição de lista em seu arquivo de layout .xml. E na sua atividade / fragmento ..

lv = (ListView) findViewById(R.id.list_view);
    inputSearch = (EditText) findViewById(R.id.inputSearch);

// Adding items to listview
adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.product_name,    products);
lv.setAdapter(adapter);       
inputSearch.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
        // When user changed the Text
        MainActivity.this.adapter.getFilter().filter(cs);
    }

    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { }

    @Override
    public void afterTextChanged(Editable arg0) {}
});

O básico aqui é adicionar um OnTextChangeListener ao texto de edição e, dentro de seu método de retorno de chamada, aplicar filtro ao adaptador do listview.

EDITAR

Para obter o filtro para seu BaseAdapter personalizado, você precisará implementar uma interface filtrável .

class CustomAdapter extends BaseAdapter implements Filterable {

    public View getView(){
    ...
    }
    public Integer getCount()
    {
    ...
    }

    @Override
    public Filter getFilter() {

        Filter filter = new Filter() {

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {

                arrayListNames = (List<String>) results.values;
                notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {

                FilterResults results = new FilterResults();
                ArrayList<String> FilteredArrayNames = new ArrayList<String>();

                // perform your search here using the searchConstraint String.

                constraint = constraint.toString().toLowerCase();
                for (int i = 0; i < mDatabaseOfNames.size(); i++) {
                    String dataNames = mDatabaseOfNames.get(i);
                    if (dataNames.toLowerCase().startsWith(constraint.toString()))  {
                        FilteredArrayNames.add(dataNames);
                    }
                }

                results.count = FilteredArrayNames.size();
                results.values = FilteredArrayNames;
                Log.e("VALUES", results.values.toString());

                return results;
            }
        };

        return filter;
    }
}

Dentro de performFiltering (), você precisa fazer uma comparação real da consulta de pesquisa com os valores em seu banco de dados. Ele passará seu resultado para o método publishResults () .

Puru Pawar
fonte
Eu criei um adaptador personalizado que estende BaseAdapter e dentro dele eu defini um Vector do meu objeto que será mostrado na lista, quando tento usar o código acima não consegui encontrar o método getFilter em meu adaptador, então você poderia diga-me se tenho que implementar alguma interface ??
Amira Elsayed Ismail
Filtrar os dados no caso de BaseAdapter é um pouco complicado. Você terá que implementar uma interface filtrável para sua implementação do BaseAdapter. Você terá então o método getFilter () e dentro dele você terá que implementar dois métodos de retorno de chamada para trabalhar; void publishResults () e FilterResults performFiltering (restrição CharSequence).
Puru Pawar
você pode apoiar com um exemplo simples, por favor?
Amira Elsayed Ismail
Sim. Verifique a seção EDIT da minha resposta.
Puru Pawar
1
Eu sugiro que você poste outra pergunta no SO sobre isso. Porque não é uma maneira adequada de continuar fazendo perguntas diferentes na mesma postagem. Bem, como um aviso, apenas copie a lista de arraylist inteira em outra lista de arraylist temporária primeiro e dentro do método onPerformFiltering () use esta lista temporária para pesquisa. Isto irá resolver o seu problema. E, por favor, vote positivamente e / ou aceite a resposta se isso ajudou você.
Puru Pawar
9

Implemente seu adaptador filtrável:

public class vJournalAdapter extends ArrayAdapter<JournalModel> implements Filterable{
private ArrayList<JournalModel> items;
private Context mContext;
....

em seguida, crie sua classe Filter:

private class JournalFilter extends Filter{

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        FilterResults result = new FilterResults();
        List<JournalModel> allJournals = getAllJournals();
        if(constraint == null || constraint.length() == 0){

            result.values = allJournals;
            result.count = allJournals.size();
        }else{
            ArrayList<JournalModel> filteredList = new ArrayList<JournalModel>();
            for(JournalModel j: allJournals){
                if(j.source.title.contains(constraint))
                    filteredList.add(j);
            }
            result.values = filteredList;
            result.count = filteredList.size();
        }

        return result;
    }
    @SuppressWarnings("unchecked")
    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        if (results.count == 0) {
            notifyDataSetInvalidated();
        } else {
            items = (ArrayList<JournalModel>) results.values;
            notifyDataSetChanged();
        }
    }

}

desta forma, seu adaptador é filtrável, você pode passar o item do filtro para o filtro do adaptador e fazer o trabalho. Espero que isso seja útil.

M.Kouchi
fonte
1

Caso alguém ainda esteja interessado neste assunto, acho que a melhor abordagem para filtrar listas é criar uma classe Filter genérica e usá-la com algumas técnicas básicas de reflexão / genéricas contidas no pacote Java da velha escola SDK. Aqui está o que eu fiz:

public class GenericListFilter<T> extends Filter {

    /**
     * Copycat constructor
     * @param list  the original list to be used
     */
    public GenericListFilter (List<T> list, String reflectMethodName, ArrayAdapter<T> adapter) {
        super ();

        mInternalList = new ArrayList<>(list);
        mAdapterUsed  = adapter;

        try {
            ParameterizedType stringListType = (ParameterizedType)
                    getClass().getField("mInternalList").getGenericType();
            mCompairMethod =
                    stringListType.getActualTypeArguments()[0].getClass().getMethod(reflectMethodName);
        }
        catch (Exception ex) {
            Log.w("GenericListFilter", ex.getMessage(), ex);

            try {
                if (mInternalList.size() > 0) {
                    T type = mInternalList.get(0);
                    mCompairMethod = type.getClass().getMethod(reflectMethodName);
                }
            }
            catch (Exception e) {
                Log.e("GenericListFilter", e.getMessage(), e);
            }

        }
    }

    /**
     * Let's filter the data with the given constraint
     * @param constraint
     * @return
     */
    @Override protected FilterResults performFiltering(CharSequence constraint) {
        FilterResults results = new FilterResults();
        List<T> filteredContents = new ArrayList<>();

        if ( constraint.length() > 0 ) {
            try {
                for (T obj : mInternalList) {
                    String result = (String) mCompairMethod.invoke(obj);
                    if (result.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
                        filteredContents.add(obj);
                    }
                }
            }
            catch (Exception ex) {
                Log.e("GenericListFilter", ex.getMessage(), ex);
            }
        }
        else {
            filteredContents.addAll(mInternalList);
        }

        results.values = filteredContents;
        results.count  = filteredContents.size();
        return results;
    }

    /**
     * Publish the filtering adapter list
     * @param constraint
     * @param results
     */
    @Override protected void publishResults(CharSequence constraint, FilterResults results) {
        mAdapterUsed.clear();
        mAdapterUsed.addAll((List<T>) results.values);

        if ( results.count == 0 ) {
            mAdapterUsed.notifyDataSetInvalidated();
        }
        else {
            mAdapterUsed.notifyDataSetChanged();
        }
    }

    // class properties
    private ArrayAdapter<T> mAdapterUsed;
    private List<T> mInternalList;
    private Method  mCompairMethod;
}

E depois disso, a única coisa que você precisa fazer é criar o filtro como uma classe membro (possivelmente dentro de "onCreate" do View) passando sua referência de adaptador, sua lista e o método a ser chamado para filtrar:

this.mFilter = new GenericFilter<MyObjectBean> (list, "getName", adapter);

A única coisa que falta agora é substituir o método "getFilter" na classe do adaptador:

@Override public Filter getFilter () {
     return MyViewClass.this.mFilter;
}

Tudo feito! Você deve filtrar sua lista com sucesso - Claro, você também deve implementar seu algoritmo de filtro da melhor maneira que descreva sua necessidade, o código abaixo é apenas um exemplo. . Espero que tenha ajudado, tome cuidado.

jbrios777
fonte
Não sei sobre o Android, mas lembro-me de ser instruído a tentar evitar a reflexão, se possível, em c # porque ele consome muitos recursos (geralmente trabalho em aplicativos do Windows Mobile, então isso pode ser um problema). Isso se aplica ao Android? ou a reflexão tem o mesmo efeito que construir uma aula real sem genéricos? Eu estava pensando em criar um modelo para filtrável e apenas adicionar a classe e o método usados ​​como parâmetros
Cruces
Sim, você está correto. O mesmo se aplica aqui, a reflexão dá um peso ao processamento do programa, mas neste caso é um uso muito simples dela, e como a estamos usando com uma notação genérica / modelo, ela também ajuda o compilador. Boa sorte!
jbrios777
NB Você pode ter problemas com ofuscação (dexguard / proguard) se você usar reflexão.
Alexey