Android Search Widget Example

0

In this article, we will add search functionality to the database list application we developed in our previous article.

You can add the search interface either using the search dialog or the search widget.

A search dialog shows up at the top of the activity window whereas a search widget can be inserted anywhere in the layout.

The search widget is an instance of SearchView and is is available only in Android 3.0 (API Level 11) and higher.

In this article, we will be using search widget as the search interface.

Search Components

The search components and the flow are mostly same for both search dialog and search widget.

When the user executes a search from the search dialog or a search widget, the system creates an Intent and stores the user query in it. The system then starts the search activity we have declared in androidManifest.xml. In the search activity, we can retrieve the query from the Intent object, perform the search and return the search results.

To implement search framework, we need the following components:

  1. A searchable configuration – An XML file that configures some settings for the search dialog or widget.
  2. A searchable activity – This can be a ListActivity that receives the search query from the Intent, searches your data, and then sets the search results to the list adapter.
  3. A Search dialog or Search View Widget – Search dialog is hidden initially but appears at the top of the screen when you call onSearchRequested(). If you are using search widget, you may want to include it as an action bar item.
Android Search  Components

Android Search Components

Add SearchView Widget to ActionBar

Add SearchView widget to the menu. actionViewClass attribute must be set to android.widget.SearchView. We have set showAsAction to ifRoom|collapseActionView which means the search view widget will be added to the action bar if there is enough room and it will be collapsed into a button The collapsed search button may appear in the overflow or in the action bar itself if there is enough space. You will the search action bar moment the user selects the action button.

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="com.javarticles.android.SearchableActivity" >
    <item
        android:id="@+id/search"
        android:actionViewClass="android.widget.SearchView"
        android:icon="@drawable/ic_search"
        android:showAsAction="ifRoom|collapseActionView"
        android:title="@string/search_label"/>
</menu>

Configuring the search widget

Create searchable configuration XML file called searchable.xml in dir res/xml/. Android uses this XML file to create SearchableInfo which will later use to configure the search view.

Below is a simple search configuration XML, <searchable> is the root element. android:label is a mandatory element and must contain the application’s label. android:hint will be used to add a hint label to the search view, which remains visible till users enters a query.

searchable.xml:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android" 
    android:label="@string/app_name"
    android:hint="@string/search_hint">   
</searchable>

We can configure the search widget in onCreateOptionsMenu() callback.

  1. First inflate the menu as search item is added to menu.
    getMenuInflater().inflate(R.menu.main, menu);
  2. You can get a reference to the SearchableInfo by calling getSearchableInfo() on SearchManager.
                         SearchManager searchManager =
    		      (SearchManager) getSystemService(Context.SEARCH_SERVICE);
                         SearchableInfo searchableInfo = searchManager.getSearchableInfo(getComponentName());
  3. SearchableInfo object represents your searchable configuration. This needs to be set to the SearchView widget.
    searchView.setSearchableInfo(searchableInfo);

LanguageListActivity:

package com.javarticles.android;

import android.app.ListActivity;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.SearchManager;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.view.Menu;
import android.widget.CursorAdapter;
import android.widget.SearchView;
import android.widget.SimpleCursorAdapter;

import com.javarticles.android.database.DatabaseConstants;

public class LanguageListActivity extends ListActivity implements
		LoaderCallbacks {
	private static final int LOADER_ID = 42;
	private CursorAdapter _adapter;
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.container_list);
		_adapter = new SimpleCursorAdapter(this,
				R.layout.container_list_item_view, null,
				new String[] { DatabaseConstants.COL_LANG_NAME },
				new int[] { R.id.list_item });
		setListAdapter(_adapter);
		getLoaderManager().initLoader(LOADER_ID, null, this);
	}

	@Override
	public Loader onCreateLoader(int id, Bundle args) {
		if (id != LOADER_ID) {
			return null;
		}
		return new CursorLoader(LanguageListActivity.this,
				LanguageContentProvider.CONTENT_URI,
				new String[] { DatabaseConstants.COL_LANG_ID, DatabaseConstants.COL_LANG_NAME }, null, null,
				null);
	}

	@Override
	public void onLoadFinished(Loader loader, Cursor data) {
		_adapter.swapCursor(data);
	}

	@Override
	public void onLoaderReset(Loader loader) {
		_adapter.swapCursor(null);
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		SearchManager searchManager =
		           (SearchManager) getSystemService(Context.SEARCH_SERVICE);
	        SearchView searchView =
		            (SearchView) menu.findItem(R.id.search).getActionView();
		searchView.setSearchableInfo(
		            searchManager.getSearchableInfo(getComponentName()));
		return true;
	}
}

SearchableActivity our activity that performs searches based on a query string and presents the search results. When the user enters a query and executes the search, the system starts your searchable activity and delivers the search query in an Intent with android.intent.action.SEARCH as the action key. You can access this action using constant Intent.ACTION_SEARCH.

	private void handleIntent(Intent intent) {
	    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
	      String query = intent.getStringExtra(SearchManager.QUERY);
	      doMySearch(query);
	    }
	}

The searchable activity needs to extract the query from the intent’s QUERY extra, then searches your data and presents the results. Since our data is in database, we will use a database query to create Cursor and then create a SimpleCursorAdapter adapter to set the list model.

	private void doMySearch(String query) {
		SQLiteHelper sqLiteHelper = ((MyApplication)getApplication()).getDbHelper();
		Cursor cursor = sqLiteHelper.getReadableDatabase().rawQuery("SELECT " + DatabaseConstants.COL_LANG_ID + ", " + 
		                                                     DatabaseConstants.COL_LANG_NAME + " FROM " + DatabaseConstants.TABLE_LANG + 
				" WHERE upper(" + DatabaseConstants.COL_LANG_NAME + ") like '%" + query.toUpperCase() + "%'", null);
		setListAdapter(new SimpleCursorAdapter(this, R.layout.container_list_item_view, cursor, 
				new String[] {DatabaseConstants.COL_LANG_NAME }, new int[]{R.id.list_item}));
	}

SearchableActivity:

package com.javarticles.android;

import android.app.ListActivity;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.Menu;
import android.widget.SearchView;
import android.widget.SimpleCursorAdapter;

import com.javarticles.android.database.DatabaseConstants;

public class SearchableActivity extends ListActivity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.container_list);
	    handleIntent(getIntent());
	}

	@Override
	protected void onNewIntent(Intent intent) {
	    setIntent(intent);
	    handleIntent(intent);
	}

	private void handleIntent(Intent intent) {
	    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
	      String query = intent.getStringExtra(SearchManager.QUERY);
	      doMySearch(query);
	    }
	}

	private void doMySearch(String query) {
		SQLiteHelper sqLiteHelper = ((MyApplication)getApplication()).getDbHelper();
		Cursor cursor = sqLiteHelper.getReadableDatabase().rawQuery("SELECT " + DatabaseConstants.COL_LANG_ID + ", " + 
		                                                     DatabaseConstants.COL_LANG_NAME + " FROM " + DatabaseConstants.TABLE_LANG + 
				" WHERE upper(" + DatabaseConstants.COL_LANG_NAME + ") like '%" + query.toUpperCase() + "%'", null);
		setListAdapter(new SimpleCursorAdapter(this, R.layout.container_list_item_view, cursor, 
				new String[] {DatabaseConstants.COL_LANG_NAME }, new int[]{R.id.list_item}));
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);		
		SearchManager searchManager =
		           (SearchManager) getSystemService(Context.SEARCH_SERVICE);
		SearchView searchView =
		            (SearchView) menu.findItem(R.id.search).getActionView();
	        searchView.setSearchableInfo(
		            searchManager.getSearchableInfo(getComponentName()));
		return true;
	}
}

Declare search activity and intent in android Manifest

  1. You need to declare the search activity.
  2. You also need to declare the intent to accept the ACTION_SEARCH action using <intent-filter> element.
  3. You will also have to specify the searchable configuration XML resource used in a <meta-data> element.
  4. Finally, we need to specify <meta-data> element with android:name as android.app.default_searchable and value as SearchableActivity

Specify the searchable configuration to use, in a element.

androidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.javarticles.android"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:maxSdkVersion="22"
        android:minSdkVersion="14"
        android:targetSdkVersion="22" />

    <application
        android:name="com.javarticles.android.MyApplication"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher_sandbox"
        android:label="@string/app_name" >
        <activity android:name="com.javarticles.android.LanguageListActivity" >
            <meta-data android:name="android.app.default_searchable"
                   android:value=".SearchableActivity" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SearchableActivity"
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>
        <provider
            android:name=".LanguageContentProvider"
            android:authorities="com.javarticles.android.provider.LanguageContentProvider"
            android:exported="false" />
    </application>

</manifest>

Run the Android Search Example

searchable_list

Search Dialog

Search Dialog

Search Results

Search Results

Search Again on Search Results

Search Again on Search Results

Final Search Results

Final Search Results

Download the source code

This was an example about adding search functionality to the database list application.

You can download the source code: searchWidgetExample.zip
Share.

Comments are closed.