How to dynamically update a ListView on Android

AndroidListviewFilterAndroid Widget

Android Problem Overview


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

Android Solutions


Solution 1 - Android

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

Solution 2 - Android

running the programm will cause a force close.

I swaped the line:

> android:id="@+building_list/search_box"

with

> android:id="@+id/search_box"

could that be the problem? What is the '@+building_list' for?

Solution 3 - Android

i had a problem with filtering, that results have been filtered, but not restored!

so before filtering (activity start) i created a list backup.. (just another list, containing the same data)

on filtering, the filter and listadapter is connected to the primary list.

but the filter itself used the data from the backuped list.

this ensured in my case, that the list was updated immediately and even on deleting search-term-characters the list gets restored successfully in every case :)

thanks for this solution anyways.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHamyView Question on Stackoverflow
Solution 1 - AndroidHamyView Answer on Stackoverflow
Solution 2 - Androidj7nn7kView Answer on Stackoverflow
Solution 3 - AndroidcV2View Answer on Stackoverflow