Android AutoCompleteTextView: Suggestions from Web Service Call - Truiton
Skip to content

Android AutoCompleteTextView: Suggestions from Web Service Call

Android AutoCompleteTextView - Featured

When building a mobile app, one of the most basic things to have in an app is an auto suggest box. It is such a common widget that anyone who’s using an app expects it to be there. But recently I realized that not many Android autocomplete examples exist on the internet where a web service or an API call is used to fetch data dynamically. Instead many tutorials are present showing the wrong practices of implementing an Android AutoCompleteTextView with web API call. Hence here I would show how I built an autosuggest feature on my Android app.

To start with lets, understand the new additions in the AutoCompleteTextView widget. Recently with the release of Android support appcompat library 25.1.0 a background tint support was added and post which the new widget class was named as AppCompatAutoCompleteTextView. This new widget class is not much different from the original one, but since its an enhanced version of AutoCompleteTextView we will using this class for this Android AutoCompleteTextView with and API call tutorial.

Android AutoCompleteTextView with data from Web API Call example

To start with, lets define the objectives of this tutorial. Here we will create and Android AutoCompleteTextView example, where data is fetched dynamically from a web API call. Once the data is fetched dynamically based on the user’s input, we would display it accordingly in a drop down scrolling list. The Android AutoCompleteTextView implementation is so flexible that even its dropdown can also be customised. But to keep things simple and focused, we would keep this out of the scope of this example.

1. Prerequisites

To make any API call in an android app, we must have a REST framework implemented. So that we can have some basic set of features relating to web API call. Since Android Volley is a very simple framework, we would be using volley in this example to make API calls. But just in case, if you are comfortable with any other REST framework, please feel free to use that in your app.

First lets add internet permission in the manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>

    <uses-permission android:name="android.permission.INTERNET" />

    <application .... >
    </application>
</manifest>

Secondly lets define a basic class to make an API call through the app.

package com.truiton.autosuggesttextviewapicall;

import android.content.Context;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

/**
 * Created by MG on 04-03-2018.
 */

public class ApiCall {
    private static ApiCall mInstance;
    private RequestQueue mRequestQueue;
    private static Context mCtx;

    public ApiCall(Context ctx) {
        mCtx = ctx;
        mRequestQueue = getRequestQueue();
    }

    public static synchronized ApiCall getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new ApiCall(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }

    public static void make(Context ctx, String query, Response.Listener<String>
            listener, Response.ErrorListener errorListener) {
        String url = "https://itunes.apple.com/search?term=" + query
                + "&country=US";
        StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                listener, errorListener);
        ApiCall.getInstance(ctx).addToRequestQueue(stringRequest);
    }
}

Method to take note of in the class above is make, as this is the only static method in this class. This method would be used to make an API call for the Android AutoCompleteTextView implementation ahead. Internally this method would create a request queue for volley and handle all the network I/O itself. Also in the above class, I just defined a basic example of Android volley request queue, through which we will be making an API call for the implementation of Android AutoCompleteTextView showing dynamic data from the same. To learn more in detail on Android volley please refer to my tutorial on it here.

2. Define an Adapter for Android AutoCompleteTextView with dynamic data from API call

As you may know, the drop down list shown on top of an AutoCompleteTextView is just a ListView inside of the overflow. Therefore as per principle, we have to make an adapter for the underlying ListView. Now since we are going to use an adapter, where only data needs to be shown in a form of list and also we don’t need any sort of processing on the list therefore we would use an ArrayAdapter. But keeping the future improvements in mind we will also implement a Filterable interface. This can help us in processing the list as well without making an API call.

package com.truiton.autosuggesttextviewapicall;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.Filterable;

import java.util.ArrayList;
import java.util.List;

public class AutoSuggestAdapter extends ArrayAdapter<String> implements Filterable {
    private List<String> mlistData;

    public AutoSuggestAdapter(@NonNull Context context, int resource) {
        super(context, resource);
        mlistData = new ArrayList<>();
    }

    public void setData(List<String> list) {
        mlistData.clear();
        mlistData.addAll(list);
    }

    @Override
    public int getCount() {
        return mlistData.size();
    }

    @Nullable
    @Override
    public String getItem(int position) {
        return mlistData.get(position);
    }

    /**
     * Used to Return the full object directly from adapter.
     *
     * @param position
     * @return
     */
    public String getObject(int position) {
        return mlistData.get(position);
    }

    @NonNull
    @Override
    public Filter getFilter() {
        Filter dataFilter = new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults filterResults = new FilterResults();
                if (constraint != null) {
                    filterResults.values = mlistData;
                    filterResults.count = mlistData.size();
                }
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results != null && (results.count > 0)) {
                    notifyDataSetChanged();
                } else {
                    notifyDataSetInvalidated();
                }
            }
        };
        return dataFilter;
    }
}

The above piece of code would turn out to be a fully functional ArrayAdapter, with the capability of setting the data without instantiating the adapter object once again. Also this would have a Filterable implementation, therefore it would be perfect example for an Android AutoCompleteTextView where suggestions from an API call are shown.

3. Using Android AppCompatAutoCompleteTextView with web API call

As mentioned earlier for this tutorial we will be using Android AppCompatAutoCompleteTextView widget for building an auto suggest experience, as this widget gives an extra advantage over standard implementation. That is with this new one, we can set background tint color and modify it dynamically with ViewCompat methods. Lets start by defining a layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.truiton.autosuggesttextviewapicall.MainActivity">

    <android.support.v7.widget.AppCompatAutoCompleteTextView
        android:id="@+id/auto_complete_edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_margin="10dp"
        android:inputType="text"
        android:maxLines="1"/>

    <TextView
        android:id="@+id/selected_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/auto_complete_edit_text"
        android:layout_marginTop="50dp"
        android:padding="20dp"/>

</RelativeLayout>

Next to implement an Android AppCompatAutoCompleteTextView where suggestions come from a WebService call, we need to define an activity where all of this is done.

package com.truiton.autosuggesttextviewapicall;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatAutoCompleteTextView;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.TextView;

import com.android.volley.Response;
import com.android.volley.VolleyError;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final int TRIGGER_AUTO_COMPLETE = 100;
    private static final long AUTO_COMPLETE_DELAY = 300;
    private Handler handler;
    private AutoSuggestAdapter autoSuggestAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final AppCompatAutoCompleteTextView autoCompleteTextView =
                findViewById(R.id.auto_complete_edit_text);
        final TextView selectedText = findViewById(R.id.selected_item);

        //Setting up the adapter for AutoSuggest
        autoSuggestAdapter = new AutoSuggestAdapter(this,
                android.R.layout.simple_dropdown_item_1line);
        autoCompleteTextView.setThreshold(2);
        autoCompleteTextView.setAdapter(autoSuggestAdapter);
        autoCompleteTextView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> parent, View view,
                                            int position, long id) {
                        selectedText.setText(autoSuggestAdapter.getObject(position));
                    }
                });

        autoCompleteTextView.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int
                    count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before,
                                      int count) {
                handler.removeMessages(TRIGGER_AUTO_COMPLETE);
                handler.sendEmptyMessageDelayed(TRIGGER_AUTO_COMPLETE,
                        AUTO_COMPLETE_DELAY);
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if (msg.what == TRIGGER_AUTO_COMPLETE) {
                    if (!TextUtils.isEmpty(autoCompleteTextView.getText())) {
                        makeApiCall(autoCompleteTextView.getText().toString());
                    }
                }
                return false;
            }
        });
    }

    private void makeApiCall(String text) {
        ApiCall.make(this, text, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                //parsing logic, please change it as per your requirement
                List<String> stringList = new ArrayList<>();
                try {
                    JSONObject responseObject = new JSONObject(response);
                    JSONArray array = responseObject.getJSONArray("results");
                    for (int i = 0; i < array.length(); i++) {
                        JSONObject row = array.getJSONObject(i);
                        stringList.add(row.getString("trackName"));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //IMPORTANT: set data here and notify
                autoSuggestAdapter.setData(stringList);
                autoSuggestAdapter.notifyDataSetChanged();
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {

            }
        });
    }
}

The above implementation of an Android AppCompatAutoCompleteTextView is a bit different from the regular implementation. Here firstly an adapter is instantiated, which we defined in the previous section of tutorial. Secondly an instance of AdapterView.OnItemClickListener() is set, to capture the clicks on the dropdown items. Next one of the most important part, a TextWatcher is attached to the AutoCompleteTextView. This TextWatcher captures all the all the user inputs and sends it to a handler. Here in this Android AutoCompleteTextView example, we are processing the changes of EditText via a delayed handler. In a way, this approach saves us some unnecessary web API calls, as a delay in consecutive requests is introduced with the help of Handler class. Once a successful call is reached to the handler, we make an API call which returns the results in expected form and they are shown with the help of an Adapter. This way, data is shown on the AutoCompleteTextView drop down. To view the full source code, please checkout the link below:

Full Source Code

This is how the output of the above example would look:

Android AutoCompleteTextView

Some Important properties of Android AutoCompleteTextView

Now that we have a fair understanding of how an Android AutoCompleteTextView works, lets have a look at some of its frequently used properties:

  • setThreshold(int threshold) – This sets a character input threshold, for triggering the dropdown.
  • setCompletionHint(CharSequence hint) – Once the threshold is reached and a dropdown is shown, over the bottom this completion hint is also shown.
  • setDropDownBackgroundResource(int id) – With the help of this method, you can set a background resource for the dropdown list.
  • setDropDownBackgroundDrawable(Drawable d) – With this method, we can set a background drawable for auto suggest list. But on a separate note, if you wish to style the Android AutoCompleteTextView dropdown background you can also consider styling the list item of dropdown by using a custom xml for inflating the item(explained in next section).

Android AutoCompleteTextView dropdown positioning

At times when making an Android autosuggest, we wish to move the dropdown and attach it to some other view on screen. This may sound complex, but its very simple to implement with Android AutoCompleteTextView, we just need to define a few properties in xml. To get started, lets discuss some properties for dropdown positioning of Andorid AutoCompleteTextView:

When using the above property, if the dropdown length goes beyond the keyboard height, in some scenarios it will be automatically detached and positioned on top of the screen. To avoid this behaviour of Android AutoCompleteTextView, we can use the following properties, these will help in curbing the mentioned behaviour:

Now addressing the issue of custom views in Android AutoCompleteTextView, to do so in the above piece of code firstly you need to make a new layout for the custom list item. Next, that xml resource needs to be specified in the adapter initiation like this: new AutoSuggestAdapter(this,R.layout.custom_dropdown_item);. Once this is done, in the adapter you need to implement the getView() method and use the custom layout xml. This way as a result you would see a list dropdown in an Android AutoCompleteTextView with a custom layout. That said, lets conclude this tutorial and please connect with us on Twitter, Facebook and Google+ for more updates.

5 thoughts on “Android AutoCompleteTextView: Suggestions from Web Service Call”

  1. hi sir, this tutorial is very useful but if I want to make another package and make make class in it and put makeApiCall() method in it, so how it is done thanks in advance

  2. Very useful tutorial.
    I just have a suggestion: If you want to adopt Singleton design pattern you should set “AutoSuggestAdapter” constructor as private and not as public. Otherwise there is the risk to create another instance of AutoSuggestAdapter.

Leave a Reply

Your email address will not be published. Required fields are marked *