Filter the data loaded from Server(PHP MySQL or JSON file) in responding to user input and show them on autocomplete view as search suggestions.
This tutorial only covers, how to implement autocomplete search suggestions for search bar/SearchView in android and if you want to display the result with RecyclerView after the user enters a query and go, then please refer tutorial Android Search View with PHP MySQL.
The AsyncTask will make a request to PHP MySQL server and get the response data in JSON format. To fetch data from the server if you want to use any library like retrofit or volley, then you have to remove AsyncTask and replace appropriate code.
Search Suggestions Video Demo
Some approaches to Search Suggestions when loading the data from a server
Here are some approaches that I think of, which may help you based on your circumstances.
- Load data from server everytime when the user enters a character into the search bar(Make a call to PHP file and apply query with LIKE statement in MySQL).
- Retrieve all data from the server before launching search activity, filter data based on user input and display search suggestions.
- Fetch data from the server before launching search activity and store those data into SQLite database and when fetching next time fetch only new records and update your SQLite database.
The first approach if you consider, the response time and delay are high and a user may or may not get search suggestions for entered query.
Here, I considered the second approach for autocomplete search suggestions as my data is some few records.
Implementing Autocomplete Search Suggestions
MySQL
The structure of table ‘tbl_fish’ defined as below.
CREATE TABLE IF NOT EXISTS `tbl_fish` (
`fish_id` int(11) NOT NULL AUTO_INCREMENT,
`fish_name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Table ‘tbl_fish’ contains data like this.
INSERT INTO `tbl_fish` (`fish_id`, `fish_name`) VALUES
(1, 'Indian Mackerel'),
(2, 'Manthal Repti'),
(3, 'Baby Sole Fish'),
(4, 'Clam Meat'),
(5, 'Indian Prawn'),
(6, 'Lamp'),
(7, 'Kandike');
PHP and JSON
config.inc.php
The configuration file for PHP.
<?php
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "test";
try {
$connection = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
die("OOPs something went wrong");
}
?>
fetch-all-fish.php
This file is responsible for fetching data from database return data back to android in JSON format.
<?php
require_once('config.inc.php');
$sql = 'SELECT * from tbl_fish';
$statement = $connection->prepare($sql);
$statement->execute();
if($statement->rowCount())
{
$row_all = $statement->fetchall(PDO::FETCH_ASSOC);
header('Content-type: application/json');
echo json_encode($row_all);
}
elseif(!$statement->rowCount())
{
echo "no rows";
}
?>
The above PHP file output’s data in JSON format however if you don’t want to use PHP MySQL with search suggestions then entering path of your JSON file say http://192.168.1.7/test/fetch-all-fish.JSON
in your MainActivity.java
also works. Below is the example JSON file.
fetch-all-fish.JSON
Create this file if you don’t want to use PHP MySQL.
[ {"fish_id":"1","fish_name":"Indian Mackerel"}, {"fish_id":"2","fish_name":"Manthal Repti"}, {"fish_id":"3","fish_name":"Baby Sole Fish"}, {"fish_id":"4","fish_name":"Clam Meat"}, {"fish_id":"5","fish_name":"Indian Prawn"}, {"fish_id":"6","fish_name":"Lamp"}, {"fish_id":"7","fish_name":"Kandike"} ]
Android
Files which are directly involved in android autocomplete search suggestions are.
MainActivity.java
MainActivity.java
is responsible for following activities.
- Define SearchView/Search bar on action bar/toolbar
- We defined AsyncTask class to fetch data from PHP or JSON file.
- To bind fetched data from the server, we use
SimpleCursorAdapter
- Define
OnSuggestionListener
andOnQueryTextListener
for SearchView to handle events. - Use
changeCursor
method ofSimpleCursorAdapter
to filter data.
File Location: App→java→Your package
package com.androidcss.searchexample;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.database.MatrixCursor;
import android.os.AsyncTask;
import android.provider.BaseColumns;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import android.database.Cursor;
public class MainActivity extends AppCompatActivity {
// CONNECTION_TIMEOUT and READ_TIMEOUT are in milliseconds
public static final int CONNECTION_TIMEOUT = 10000;
public static final int READ_TIMEOUT = 15000;
private SimpleCursorAdapter myAdapter;
SearchView searchView = null;
private String[] strArrData = {"No Suggestions"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final String[] from = new String[] {"fishName"};
final int[] to = new int[] {android.R.id.text1};
// setup SimpleCursorAdapter
myAdapter = new SimpleCursorAdapter(MainActivity.this, android.R.layout.simple_spinner_dropdown_item, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
// Fetch data from mysql table using AsyncTask
new AsyncFetch().execute();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// adds item to action bar
getMenuInflater().inflate(R.menu.search_main, menu);
// Get Search item from action bar and Get Search service
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchManager searchManager = (SearchManager) MainActivity.this.getSystemService(Context.SEARCH_SERVICE);
if (searchItem != null) {
searchView = (SearchView) searchItem.getActionView();
}
if (searchView != null) {
searchView.setSearchableInfo(searchManager.getSearchableInfo(MainActivity.this.getComponentName()));
searchView.setIconified(false);
searchView.setSuggestionsAdapter(myAdapter);
// Getting selected (clicked) item suggestion
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
@Override
public boolean onSuggestionClick(int position) {
// Add clicked text to search box
CursorAdapter ca = searchView.getSuggestionsAdapter();
Cursor cursor = ca.getCursor();
cursor.moveToPosition(position);
searchView.setQuery(cursor.getString(cursor.getColumnIndex("fishName")),false);
return true;
}
@Override
public boolean onSuggestionSelect(int position) {
return true;
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
return false;
}
@Override
public boolean onQueryTextChange(String s) {
// Filter data
final MatrixCursor mc = new MatrixCursor(new String[]{ BaseColumns._ID, "fishName" });
for (int i=0; i<strArrData.length; i++) {
if (strArrData[i].toLowerCase().startsWith(s.toLowerCase()))
mc.addRow(new Object[] {i, strArrData[i]});
}
myAdapter.changeCursor(mc);
return false;
}
});
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
// Every time when you press search button on keypad an Activity is recreated which in turn calls this function
@Override
protected void onNewIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
if (searchView != null) {
searchView.clearFocus();
}
// User entered text and pressed search button. Perform task ex: fetching data from database and display
}
}
// Create class AsyncFetch
private class AsyncFetch extends AsyncTask<String, String, String> {
ProgressDialog pdLoading = new ProgressDialog(MainActivity.this);
HttpURLConnection conn;
URL url = null;
@Override
protected void onPreExecute() {
super.onPreExecute();
//this method will be running on UI thread
pdLoading.setMessage("\tLoading...");
pdLoading.setCancelable(false);
pdLoading.show();
}
@Override
protected String doInBackground(String... params) {
try {
// Enter URL address where your php file resides or your JSON file address
url = new URL("http://192.168.1.7/test/fetch-all-fish1.php");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return e.toString();
}
try {
// Setup HttpURLConnection class to send and receive data from php and mysql
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(READ_TIMEOUT);
conn.setConnectTimeout(CONNECTION_TIMEOUT);
conn.setRequestMethod("GET");
// setDoOutput to true as we receive data
conn.setDoOutput(true);
conn.connect();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return e1.toString();
}
try {
int response_code = conn.getResponseCode();
// Check if successful connection made
if (response_code == HttpURLConnection.HTTP_OK) {
// Read data sent from server
InputStream input = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
// Pass data to onPostExecute method
return (result.toString());
} else {
return("Connection error");
}
} catch (IOException e) {
e.printStackTrace();
return e.toString();
} finally {
conn.disconnect();
}
}
@Override
protected void onPostExecute(String result) {
//this method will be running on UI thread
ArrayList<String> dataList = new ArrayList<String>();
pdLoading.dismiss();
if(result.equals("no rows")) {
// Do some action if no data from database
}else{
try {
JSONArray jArray = new JSONArray(result);
// Extract data from json and store into ArrayList
for (int i = 0; i < jArray.length(); i++) {
JSONObject json_data = jArray.getJSONObject(i);
dataList.add(json_data.getString("fish_name"));
}
strArrData = dataList.toArray(new String[dataList.size()]);
} catch (JSONException e) {
// You to understand what actually error is and handle it appropriately
Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
Toast.makeText(MainActivity.this, result.toString(), Toast.LENGTH_LONG).show();
}
}
}
}
}
search_main.xml
location: res→menu resource directory.
If you don’t find menu directory inside res folder, then add one by Right clicking on res directory(Right click on res→New→Android Resource Directory).
<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=".SearchResultsActivity">
<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>
searchable.xml
This file has to go under res→xml resource directory. If you don’t find any XML folder inside res directory, then add one by Right clicking on res directory(Right click on res→New→Android Resource Directory).
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="Search..."
android:label="@string/app_name" />
styles.xml
In my theme by default, the color of AutoComplete box and text is in black and white color respectively so I changed it to light grey and black color by defining my own AutoCompleteTextView style as you can see it in below image. The code responsible for AutoCompleteTextView is highlighted below.
File Location: App→res→Values
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="autoCompleteTextViewStyle">@style/myAutoCompleteTextViewStyle</item>
<item name="textAppearanceSearchResultTitle">@style/mySearchResult</item>
</style>
<style name="myAutoCompleteTextViewStyle" parent="Widget.AppCompat.Light.AutoCompleteTextView">
<item name="android:popupBackground">#EEE</item>
<item name="android:colorFocusedHighlight">#000000</item>
</style>
<style name="mySearchResult" parent="TextAppearance.AppCompat.SearchResult.Title">
<item name="android:textColor">#FFF</item>
</style>
</resources>
AndroidManifest.xml
Don’t forget to add below-highlighted code to your AndroidManifest.xml file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidcss.searchexample" >
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<meta-data
android:name="android.app.default_searchable"
android:value=".MainActivity" />
<activity android:name=".MainActivity" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.SEARCH" />
<action android:name="android.intent.action.VIEW" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
</application>
</manifest>
December 15, 2019 at 1:06 pm
Hello what you do is great ,your blog should be known by more people ! Too bad you don’t keep it updated.
I have one question i’d like to know if it’s possible to do this with AutoCompleteTextView and if it’s possible how to do it.
I’m a beginer to android but what you do really helps me !
I hope you’ll do more tutorial like this in the futur.
Thank you so much.
November 11, 2018 at 4:18 pm
hi, thanks for your tutorial, but i get this error
android.support.v7.widget.SearchView cannot be cast to android.widget.SearchView
need help
thanks
November 12, 2018 at 3:27 am
For search view always use android.support.v7.widget.SearchView. Replace import statement.
October 24, 2018 at 9:21 am
What is BaseColumns._ID? from this I think I’m not getting another value while I’m sending data to another activity.
October 25, 2018 at 4:24 am
Actually we are creating a cursor for simple cursor adapter. We are defining column name using BaseColumns.
October 27, 2018 at 5:58 am
sir can you give me a sample code to send data to next activity please
October 23, 2018 at 12:33 pm
fetching data in search bar its all going well no doubt but i want to send data to next activity for eg in name search xyz and xyz have phone details email id etc.. but i m sending only one data that’s it name i m not getting the number mail id etc its show
java.lang.IllegalArgumentException: columnNames.length = 3, columnValues.length = 2
MatrixCursor.addRow
it’s a request to solve my problem give a reply
thanks
October 24, 2018 at 4:19 am
Rather than passing string variable, you can pass object to next activity. If you dont understand, please post your code to analyze further.
October 12, 2018 at 11:31 am
where is activity_main.xml file?
October 12, 2018 at 2:52 pm
We are not going to add any widgets/components to our activity so just add any root element to your layout file.
June 2, 2018 at 12:06 pm
searchView.setSuggestionsAdapter(myAdapter);
Error in myAdapter.
June 4, 2018 at 3:50 am
I think you are not defined/initialised a myAdapter variable in onCreate method. Please look into the code.
December 28, 2017 at 7:09 am
Thank you. This tutorial is helpful for me to do my project
April 26, 2017 at 6:25 am
nice work and clean codes
what if i have this type of json data
[
{"fish_id":"1","fish_name":"Indian Mackerel","fish_weight":"2kg","price":"$10"},
{"fish_id":"2","fish_name":"Manthal Repti","fish_weight":"0.5kg","price":"$3"},
{"fish_id":"3","fish_name":"Baby Sole Fish","fish_weight":"5kg","price":"$12"},
{"fish_id":"4","fish_name":"Clam Meat","fish_weight":"1kg","price":"$7"},
{"fish_id":"5","fish_name":"Indian Prawn","fish_weight":"0.2kg","price":"$5"}
]
when auto suggest item clicked i want to display other related data i mean price and fish_weight how can i achieve that?
March 22, 2017 at 5:02 am
This is implemented without any bugs, Once the autocomplete search fills up how to load the data from the server regarding that search field value?
December 20, 2016 at 9:38 am
Nice tutorial.
But I am facing a problem when retrieving the data from MySQL. The app can fetch data without any problem if I use the mobile data to connect to the internet. But if I use Wifi to connect to the internet, the data cannot be fetched and it shows error “Connection error” and an exception.
March 22, 2017 at 12:00 pm
check your URL in browser to make sure it is working.
October 20, 2016 at 7:47 am
Hi, I’m using BaseAdapter how do I filter the result using ArrayAdapter?
Currently, all the data are populated in the ListView.
i’m using getFilter() but it could not work as I posted in http://stackoverflow.com/questions/40147767/getfilter-in-adapter-not-working-android
Hope you can give some advice.
Thanks
September 11, 2016 at 4:36 am
thank you for this advanced programming
i have a quetion
how i parse or display JSON when i press search button ??
September 25, 2016 at 6:31 am
Refer tutorial Android Search View with PHP MySQL.
October 6, 2016 at 9:19 am
Thank You… I changed my PHP file and its working..
August 3, 2016 at 6:28 pm
Programming is combination of intelligent and creative work. Programmers can do anything with code. The entire Programming tutorials that you mention here on this blog are awesome. also provides latest tutorials of Programming from beginning to advance level.
Be with us to learn programming in new and creative way.