Как динамически обновить ListView на Android [закрыто]

159

На Android, как я могу ListViewфильтровать на основе пользовательского ввода, где показанные элементы обновляются динамически в зависимости от TextViewзначения?

Я ищу что-то вроде этого:

-------------------------
| Text View             |
-------------------------
| List item             |
| List item             |
| List item             |
| List item             |
|                       |
|                       |
|                       |
|                       |
-------------------------
Hamy
источник
7
Я номинирую на повторное открытие. Я обновил текст, чтобы сделать его более интересным, и он, несомненно, является ценным ресурсом сообщества, поскольку ответы все еще поступают
Хэми
Пожалуйста, снова откройте вопрос. Это явно полезно.
SilentNot

Ответы:

286

Во-первых, вам нужно создать макет XML, который имеет и EditText, и 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>

Это выложит все правильно, с хорошим EditText над ListView. Затем создайте ListActivity, как обычно, но добавьте setContentView()вызов в onCreate()метод, чтобы мы использовали наш недавно объявленный макет. Помните, что мы определили ListViewспециально, с android:id="@android:id/list". Это позволяет ListActivityузнать, что ListViewмы хотим использовать в нашем объявленном макете.

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

Запущенное приложение теперь должно показать ваше предыдущее ListView, с красивой рамкой выше. Чтобы заставить этот блок что-то делать, нам нужно взять входные данные из него и сделать этот фильтр фильтрующим список. Хотя многие пытались сделать это вручную, большинство ListView Adapter классов Filterсодержат объект, который можно использовать для автоматической фильтрации. Нам просто нужно передать данные из EditTextв Filter. Оказывается, это довольно легко. Чтобы выполнить быстрый тест, добавьте эту строку в свой onCreate()звонок

adapter.getFilter().filter(s);

Обратите внимание, что вам нужно сохранить вашу ListAdapterпеременную, чтобы сделать эту работу - я сохранил свой ArrayAdapter<String>ранее в переменную под названием «адаптер».

Следующий шаг - получить информацию от EditText. Это на самом деле занимает немного мысли. Вы можете добавить OnKeyListener()к своему EditText. Однако этот слушатель получает только некоторые ключевые события . Например, если пользователь вводит «wyw», предиктивный текст, скорее всего, порекомендует «eye». Пока пользователь не выберет «wyw» или «eye», вы OnKeyListenerне получите ключевое событие. Некоторые могут предпочесть это решение, но я нахожу это разочаровывающим. Я хотел каждое ключевое событие, поэтому у меня был выбор фильтрации или не фильтрации. Решение есть TextWatcher. Просто создайте и добавьте TextWatcherк EditText, и передайте ListAdapter Filterзапрос фильтра каждый раз, когда текст изменяется. Не забудьте удалить TextWatcherв OnDestroy()! Вот окончательное решение:

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
источник
7
Есть ли простой способ фильтрации ListView в «содержит» вместо «начинается с» мода, как это решение?
Виктор Брешан
12
Виктор - Если слова, которые вас интересуют, разделены пробелами, то это будет сделано автоматически. Иначе не совсем. Вероятно, самым простым способом было бы создать подкласс Adapter, расширив его, и переопределить метод getFilter, чтобы он возвращал объект Filter, который вы определили. См. Github.com/android/platform_frameworks_base/blob/master/core/… чтобы понять, как работает ArrayFilter по умолчанию - было бы просто скопировать 95% этого кода и изменить строки 479 и 486
Hamy
2
Хэми, отличная рецензия! У меня есть вопрос, хотя: я реализовал это, и после каждой набираемой буквы ListView исчезает на пару секунд, а затем возвращается, фильтруется. Вы испытали это? Моя интуиция заключается в том, что у меня в списке более 600 элементов с нетривиальными функциями toString ().
lowellk
2
В ландшафтном режиме на меньшем экране клавиатура EditText + занимает весь экран, а ListView не отображается! Любое решение?
Мартин Коничек
4
Это действительно необходимо? > Не забудьте удалить TextWatcher из OnDestroy ()
Jojo
10

запуск программы вызовет принудительное закрытие.

Я поменял линию:

андроид: идентификатор = "@ + building_list / search_box"

с участием

андроид: идентификатор = "@ + идентификатор / search_box"

Может ли это быть проблема? Для чего нужен «@ + building_list»?

j7nn7k
источник
Джохе, когда ты говоришь R.id.something, что-то существует в id, потому что ты сказал android: id = "@ + id / search_box". Если вы говорите android: id = "@ + building_list / search_box", то в коде вы можете вызвать findViewById (R.building_list.search_box); Какое исключение верхнего уровня вы получаете? Этот код копируется без тестовой компиляции, поэтому я, вероятно, оставил там хотя бы одну ошибку
Hamy
Привет, Хэми, ты ссылался на "@ + building_list / search_box" в коде с помощью "filterText = (EditText) findViewById (R.id.search_box);" Вот почему мне было интересно.
j7nn7k
1
При принудительном вводе происходит принудительное закрытие. Часть ошибки: ERROR / AndroidRuntime (188): java.lang.NullPointerException 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): на xxx.com.ListFilter $ 1. onTextChanged (ListFilter.java:46) 02-11 07: 30: 29.828: ОШИБКА / AndroidRuntime (188): в android.widget.TextView.sendOnTextChanged (TextView.java:6102) 02-11 07: 30: 29.828: ОШИБКА / AndroidRuntime (188): в android.widget.TextView.handleTextChanged (TextView.java:6143) 02-11 07: 30: 29.828: ОШИБКА / AndroidRuntime (188): в android.widget.TextView $ ChangeWatcher.onTextChanged (TextView.java) : 6286)
j7nn7k
Джо, упс - похоже, я пытался изменить код, чтобы избавиться от @ + building_list (потому что это путаница), но я не везде его ловил. Спасибо за чаевые! Просто изменив это на @ + id, это должно исправить
Hamy
4

У меня была проблема с фильтрацией, результаты были отфильтрованы, но не восстановлены !

поэтому перед фильтрацией (началом активности) я создал резервную копию списка .. (просто еще один список, содержащий те же данные)

при фильтрации фильтр и listadapter подключаются к основному списку.

но сам фильтр использовал данные из резервного архива.

в моем случае это гарантировалось, что список обновлялся немедленно, и даже при удалении символов-терминов поиска список успешно восстанавливался в каждом случае :)

спасибо за это решение в любом случае.

CV2
источник