How to dynamically update a ListView on Android
android, android-widget, filter, listview
Solution
First, you need to create an XML layout that has both an EditText, and a 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>
This will lay everything out properly, with a nice EditText above the ListView. Next, create a ListActivity as you would normally, but add a `setContentView()` call in the `onCreate()` method so we use our recently declared layout. Remember that we ID'ed the `ListView` specially, with `android:id="@android:id/list"`. This allows the `ListActivity` to know which `ListView` we want to use in our declared layout.
@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());
}
Running the app now should show your previous `ListView`, with a nice box above. In order to make that box do something, we need to take the input from it, and make that input filter the list. While a lot of people have tried to do this manually, most `ListView` `Adapter` classes come with a `Filter` object that can be used to perform the filtering automagically. We just need to pipe the input from the `EditText` into the `Filter`. Turns out that is pretty easy. To run a quick test, add this line to your `onCreate()` call
adapter.getFilter().filter(s);
Notice that you will need to save your `ListAdapter` to a variable to make this work - I have saved my `ArrayAdapter<String>` from earlier into a variable called 'adapter'.
Next step is to get the input from the `EditText`. This actually takes a bit of thought. You could add an `OnKeyListener()` to your `EditText`. However, this listener only receives some key events. For example, if a user enters 'wyw', the predictive text will likely recommend 'eye'. Until the user chooses either 'wyw' or 'eye', your `OnKeyListener` will not receive a key event. Some may prefer this solution, but I found it frustrating. I wanted every key event, so I had the choice of filtering or not filtering. The solution is a `TextWatcher`. Simply create and add a `TextWatcher` to the `EditText`, and pass the `ListAdapter` `Filter` a filter request every time the text changes. Remember to remove the `TextWatcher` in `OnDestroy()`! Here is the final solution:
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);
}
Problem
On Android, how can I a `ListView` that filters based on user input, where the items shown are updated dynamically based on the `TextView` value? I'm looking for something like this: ``` ------------------------- | Text View | ------------------------- | List item | | List item | | List item | | List item | | | | | | | | | ------------------------- ```