Фильтр просмотра списка Android


94

Я создал представление списка в android, и я хочу добавить текст редактирования над списком, и когда пользователь вводит текст, список будет отфильтрован в соответствии с вводом пользователя

может кто-нибудь сказать мне, пожалуйста, есть ли способ отфильтровать адаптер списка в android?


1
Привет, попробуйте этот пример Первый пример и второй Пример 2 Я реализовал то же самое на основе этих руководств .. Надеюсь, это поможет вам
Прагнани

5
Главный ответ просто не предоставил мне достаточно информации. Этот ответ на аналогичный вопрос имеет больше контекста и был достаточно информацией, чтобы разобраться со мной.
Джеймс Скемп

Я использую режим ресайклера. Я хочу фильтровать записи, но я использую настраиваемые кнопки для ввода текста редактирования, например, настраиваемой клавиатуры, но я получаю задержку при быстром нажатии кнопки, вы можете помочь мне выбраться из этого. У меня есть записи в тысячах
Sagar

Ответы:


141

Добавьте EditText поверх списка в его файле макета .xml. И в вашей активности / фрагменте ..

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) {}
});

Основное здесь - добавить OnTextChangeListener к вашему тексту редактирования и внутри его метода обратного вызова применить фильтр к адаптеру вашего списка.

РЕДАКТИРОВАТЬ

Для того, чтобы получить фильтр к пользовательскому BaseAdapter вы "должны будет реализовывать Фильтруемый интерфейс.

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;
    }
}

Внутри performFiltering () вам необходимо провести фактическое сравнение поискового запроса со значениями в вашей базе данных. Он передаст свой результат методу publishResults () .


Я создал собственный адаптер, который расширяет BaseAdapter, и внутри него я определил вектор моего объекта, который будет отображаться в списке, когда я пытаюсь использовать приведенный выше код, я не могу найти метод getFilter в моем адаптере, так что не могли бы вы скажите, пожалуйста, нужно ли мне реализовать какой-нибудь интерфейс ??
Амира Эльсайед Исмаил

Фильтрация данных в случае BaseAdapter немного сложна. Вам нужно будет реализовать интерфейс Filterable для вашей реализации BaseAdapter. Затем у вас будет метод getFilter (), и внутри него вы должны реализовать два метода обратного вызова для работы; void publishResults () и FilterResults performFiltering (ограничение CharSequence).
Purush Pawar

не могли бы вы поддержать простым примером?
Амира Эльсайед Исмаил

Да. Проверьте раздел EDIT моего ответа.
Purush Pawar

1
Я предлагаю вам задать еще один вопрос по этому поводу. Потому что это неправильный способ задавать разные вопросы в одном и том же посте. Что ж, в качестве напоминания, просто скопируйте сначала весь Arraylist в другой временный Arraylist, а внутри метода onPerformFiltering () используйте этот временный список для поиска. Это решит вашу проблему. И, пожалуйста, проголосуйте за и / или примите ответ, если он вам помог.
Purush Pawar

9

Реализуйте свой адаптер Filterable:

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

затем создайте свой класс фильтра:

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();
        }
    }

}

Таким образом, ваш адаптер является фильтруемым, вы можете передать элемент фильтра в фильтр адаптера и выполнить работу. Надеюсь, это будет полезно.


1

Если кто-то все еще интересуется этой темой, я считаю, что лучший подход для фильтрации списков - это создать общий класс Filter и использовать его с некоторыми базовыми методами отражения / обобщения, содержащимися в пакете SDK старой школы Java. Вот что я сделал:

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;
}

И после этого единственное, что вам нужно сделать, это создать фильтр как класс-член (возможно, внутри View "onCreate"), передав ссылку на ваш адаптер, ваш список и метод, который будет вызываться для фильтрации:

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

Единственное, чего сейчас не хватает, это переопределить метод getFilter в классе адаптера:

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

Все сделано! Вы должны успешно отфильтровать свой список - конечно, вы также должны реализовать свой алгоритм фильтрации, наилучшим образом описывающий ваши потребности, приведенный ниже код является лишь примером. . Надеюсь, это помогло, береги себя.


Я не знаю об Android, но я помню, как мне сказали, чтобы я старался избегать отражения, если это возможно в C #, потому что это довольно ресурсоемко (я обычно работаю с мобильными приложениями Windows, поэтому это может быть проблемой), применимо ли это к Android? или отражение имеет тот же эффект, что и создание реального класса без дженериков? Я думал создать шаблон для фильтруемого и просто добавить класс и метод, используемые в качестве параметров
Cruces

Да, ты прав. То же самое применимо и здесь, отражение дает вес обработке программы, но в данном случае это очень простое использование, и поскольку мы используем его с обобщенной нотацией / шаблоном, это также помогает компилятору. Удачи!
jbrios777 03

NB. У вас могут возникнуть проблемы с обфускацией (dexguard / proguard), если вы используете отражение.
Алексей
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.