ListView and in this post, I will guide how to filter it by a query string, adding search functionality to ListView will provides user an easy way to find the information they needs, to do that I am using SearchView widget located in Toolbar- instead of Action Bar.According to the aspirations of some reader, I will not use the default
ListView style with only text and default row layout (like android.R.layout.simple_list_item_1, android.R.layout.simple_list_item_2,...). In this post, I will filter a custom ListView with letter icon in the left side (like Gmail application style - see my previous post to know how to create it - I will reuse this project existing code). For the output, please watch this DEMO VIDEO first:Use AppCompat theme, use Toolbar instead of ActionBar
styles.xml
Use <resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
AppTheme.NoActionBar with your Activity:
AndroidManifest.xml
Designing the Activity layout, locating the Toolbar and including a ListView:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="devexchanges.info.letterlistview" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main">
<ListView
android:id="@+id/list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"/>
</RelativeLayout>
Locating SearchView on Toolbar
onCreateOptionsMenu() inprogrammatically code later:
menu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="always"
app:actionViewClass="android.support.v7.widget.SearchView"
android:title="Search"/>
</menu>
Creating the Filter for ListView
ArrayAdapter or BaseAdapter. In this class, declaring a filter method with a query string. Full code for this adapter:
ListViewAdapter.java
Like noted above, the package devexchanges.info.letterlistview;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class ListViewAdapter extends ArrayAdapter<String> {
private MainActivity activity;
private List<String> friendList;
private List<String> searchList;
public ListViewAdapter(MainActivity context, int resource, List<String> objects) {
super(context, resource, objects);
this.activity = context;
this.friendList = objects;
this.searchList = new ArrayList<>();
this.searchList.addAll(friendList);
}
@Override
public int getCount() {
return friendList.size();
}
@Override
public String getItem(int position) {
return friendList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
// If holder not exist then locate all view from UI file.
if (convertView == null) {
// inflate UI from XML file
convertView = inflater.inflate(R.layout.item_listview, parent, false);
// get all UI view
holder = new ViewHolder(convertView);
// set tag for holder
convertView.setTag(holder);
} else {
// if holder created, get tag from view
holder = (ViewHolder) convertView.getTag();
}
holder.friendName.setText(getItem(position));
//get first letter of each String item
String firstLetter = String.valueOf(getItem(position).charAt(0));
ColorGenerator generator = ColorGenerator.MATERIAL; // or use DEFAULT
// generate random color
int color = generator.getColor(getItem(position));
TextDrawable drawable = TextDrawable.builder()
.buildRound(firstLetter, color); // radius in px
holder.imageView.setImageDrawable(drawable);
return convertView;
}
// Filter method
public void filter(String charText) {
charText = charText.toLowerCase(Locale.getDefault());
friendList.clear();
if (charText.length() == 0) {
friendList.addAll(searchList);
} else {
for (String s : searchList) {
if (s.toLowerCase(Locale.getDefault()).contains(charText)) {
friendList.add(s);
}
}
}
notifyDataSetChanged();
}
private class ViewHolder {
private ImageView imageView;
private TextView friendName;
public ViewHolder(View v) {
imageView = (ImageView) v.findViewById(R.id.image_view);
friendName = (TextView) v.findViewById(R.id.text);
}
}
}
SearchView will be located in onCreateOptionsMenu() and we'll set "query listener" for it here. Full code for the Activity:
MainActivity.java
And the last, this is layout for each package devexchanges.info.letterlistview;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ListView listView;
private ArrayList<String> stringArrayList;
private ListViewAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.list_item);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setData();
adapter = new ListViewAdapter(this, R.layout.item_listview, stringArrayList);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this, (String)parent.getItemAtPosition(position), Toast.LENGTH_SHORT).show();
}
});
}
private void setData() {
stringArrayList = new ArrayList<>();
stringArrayList.add("Quynh Trang");
stringArrayList.add("Hoang Bien");
stringArrayList.add("Duc Tuan");
stringArrayList.add("Dang Thanh");
stringArrayList.add("Xuan Luu");
stringArrayList.add("Phan Thanh");
stringArrayList.add("Kim Kien");
stringArrayList.add("Ngo Trang");
stringArrayList.add("Thanh Ngan");
stringArrayList.add("Nguyen Duong");
stringArrayList.add("Quoc Cuong");
stringArrayList.add("Tran Ha");
stringArrayList.add("Vu Danh");
stringArrayList.add("Minh Meo");
}
@Override
public boolean onCreateOptionsMenu( Menu menu) {
getMenuInflater().inflate( R.menu.menu_main, menu);
MenuItem myActionMenuItem = menu.findItem( R.id.action_search);
final SearchView searchView = (SearchView) myActionMenuItem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
if (TextUtils.isEmpty(newText)) {
adapter.filter("");
listView.clearTextFilter();
} else {
adapter.filter(newText);
}
return true;
}
});
return true;
}
}
ListView item:
item_listview.xml
Running application, we'll have this result:<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:id="@+id/image_view"
android:layout_width="0dp"
android:layout_height="75dp"
android:layout_weight="30"
android:contentDescription="@string/app_name" />
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="70"
android:gravity="center"
android:text=""
android:textStyle="bold" />
</LinearLayout>
The filter will work immediately when you typing some texts:
Final thoughts
ListView with letter icon" was built by using an external library. So you must add this dependency to your app/build.gradle before coding:
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
(Please view carefully my previous). Through this tutorial, I hope that readers can be learned the way to use
SearchView with AppCompat theme and Toolbar. Basically, it's similar with Holo theme or other style. Finally, you can take the full project code on @Github.

