2009-11-15 2 views
157

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

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

------------------------- 
| Text View    | 
------------------------- 
| List item    | 
| List item    | 
| List item    | 
| List item    | 
|      | 
|      | 
|      | 
|      | 
------------------------- 
+7

Я назначаю для повторного открытия. Я обновил текст, чтобы сделать этот вопрос более актуальным, и это, безусловно, ценный ресурс сообщества, поскольку ответы по-прежнему поступают в – Hamy

+0

. Пожалуйста, закройте вопрос. Это явно полезно. – 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, с хорошей коробкой выше. Чтобы сделать этот блок что-то, нам нужно взять его от него и сделать этот входным фильтром список. В то время как многие люди пытались сделать это вручную, Большинство классы имеют объект Filter, который может быть использован для автоматической фильтрации. Нам просто нужно подключить вход от EditText к Filter. Оказывается, это довольно легко. Для того, чтобы запустить быстрый тест, добавьте эту строку в onCreate() вызова

adapter.getFilter().filter(s); 

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

Следующий шаг - получить данные от EditText. Это на самом деле задумывается. Вы можете добавить OnKeyListener() к вашему EditText. Однако этот слушатель получает только некоторые ключевые события. Например, если пользователь вводит «wyw», предсказательный текст, скорее всего, порекомендует «глаз». Пока пользователь не выберет «wyw» или «eye», ваш OnKeyListener не получит ключевое событие. Некоторые могут предпочесть это решение, но я счел это расстраивающим. Я хотел каждое ключевое событие, поэтому у меня был выбор фильтрации или не фильтрации. Решение представляет собой TextWatcher. Просто создайте и добавьте TextWatcher в EditText и передайте ListAdapterFilter запрос на фильтр при каждом изменении текста. Не забудьте удалить 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); 
} 
+7

Есть ли простой способ фильтрации ListView в «содержит» вместо «начинается с», как это делает это решение? –

+12

Viktor - Если слова, которые вас интересуют, разделены пробелами, тогда он сделает это автоматически. В противном случае, не совсем. Вероятно, самым простым способом было бы подклассировать адаптер, расширив его и переопределив метод getFilter, чтобы вернуть объект Filter, который вы определяете. См. Http://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ArrayAdapter.java#L449, чтобы понять, как работает ArrayFilter по умолчанию - было бы просто скопировать 95% этого кода и изменить строки 479 и 486 – Hamy

+0

Что такое getStringArrayList(), я запускаю этот код, но не могу получить эффект фильтра, вы можете мне помочь – pengwang

10

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

Я swaped строку:

android:id="@+building_list/search_box"

с

android:id="@+id/search_box"

могло быть проблема? Что такое «@ + building_list»?

+0

Johe, когда вы говорите R.id.something, что-то существует в id, потому что вы сказали android: id = "@ + id/search_box". Если вы скажете андроид: id = "@ + building_list/search_box", то в коде вы можете вызвать findViewById (R.building_list.search_box); Какое исключение верхнего уровня вы получаете? Этот код скопирован w/oa test compile, поэтому я, вероятно, оставил хотя бы одну ошибку там где-то – Hamy

+0

Привет, Хами, вы ссылаетесь на «@ + building_list/search_box» в коде с помощью «filterText = (EditText) findViewById (R.id. поисковая строка);" Вот почему мне было интересно. – j7nn7k

+1

Принудительное замедление при вводе. Часть ошибки: ОШИБКА/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: ERROR/AndroidRuntime (188): at android.widget.TextView.sendOnTextChanged (TextView.java:6102) 02-11 07: 30: 29.828: ОШИБКА/AndroidRuntime (188): at android.widget.TextView.handleTextChanged (TextView.java:6143) 02-11 07: 30: 29.828: ERROR/AndroidRuntime (188): at android.widget.TextView $ ChangeWatcher.onTextChanged (TextView.java:6286) – j7nn7k

4

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

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

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

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

это обеспечивается в моем случае, что список был обновлен сразу и даже при удалении поиска ПОСУТОЧНЫХ-символов список получает успешно восстановлен в любом случае :)

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