21 March 2014

Android type ahead filter

Any time I find a self filtering search box that works properly, my life is improved! Good UI and all that :) I find auto complete a great way to search and thought I'd share. There is no rocket science here, I just pieced this together from a few various examples.

First a simple activity main:

    android:orientation="vertical" >

    <!-- Pretty hint text, and maxLines -->

    <!-- 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="@+id/listview1"
        android:layout_weight="1" /> 

Next in my ActivityMain I create a helper method getNames() to load in the data. Obviously you'll do this in your own unique way. Also note the TextWatcher, this is pretty simple.

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


    private ArrayList<String> getNames(){
        String[] names = getResources().getStringArray(R.array.planets_array);
        ArrayList<String> listNames = new ArrayList<String>();
        for(String name: names){
        return listNames;

Then in onCreate we put it all together:

        mFriendsList = (ListView) findViewById(R.id.listview1);
        //Apply Filter
        filterText = (EditText) findViewById(R.id.searchbox1);
        arrayAdapter = new newNameAdapter(this, getNames());

Finally the complicated (ish) bit is the Adapter.

package com.example.testleaguefinder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;

public class newNameAdapter extends BaseAdapter implements Filterable{

    Context context;
    ArrayList<String> names;
    private LayoutInflater mInflater;
    private GameFilter filter;
    ArrayList<String> orig;
    public newNameAdapter(Context _context, ArrayList<String> _names) {
        this.context = _context;
        this.names = _names;
        filter = new GameFilter();
        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    public int getCount() {
        return names.size();

    public String getItem(int position) {
        return names.get(position);

    public long getItemId(int position) {
        return 0;

    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.listview_names, null);
        TextView txtName = (TextView) convertView.findViewById(R.id.listview_names_name);
        return convertView;
    public void sort(){
        Collections.sort(names, new Comparator<String>() {
            public int compare(String fruite1, String fruite2)
                return  fruite1.compareTo(fruite2);
    public Filter getFilter() {
        return filter;
    private class GameFilter extends Filter {
        public GameFilter() {
        protected FilterResults performFiltering(CharSequence constraint) {
            String lowerConstraint = constraint.toString();
            FilterResults oReturn = new FilterResults();
            ArrayList<String> results = new ArrayList<String>();
            String league;
            if (orig == null){
                orig = names;
            if (lowerConstraint != null){
                lowerConstraint = lowerConstraint.toLowerCase(Locale.UK);
                if (orig != null && orig.size() > 0) {
                    for (String g : orig) {
                        league = g.toLowerCase(Locale.UK);
                        if (league.contains(lowerConstraint)){
                oReturn.values = results;
            return oReturn;
        protected void publishResults(CharSequence constraint, FilterResults results) {
            names = (ArrayList<String>)results.values;

Most of it should be fairly standard, the only interesting bit should be the Filter. All we're doing really is taking the entered text and lower casing it. Then we loop through the list of entries and find any that contain the filter string.