Pages

Collapsible Search in Action Bar + Full implementation (android)

search view in collapsed mode

search view expanded. 

This is such a common feature in an app to have but getting this feature to work was, hmm, tricky. If you don't implement it as mentioned below it might not work. Even debugging the code didn't help.

Before jumping to the implementation details let's talk about how this widget works.
  1. When the app is loaded you will this widget as a search icon. 
  2. Clicking on the search icon will expand into an input field. 
  3. After entering text and pressing go the search term is passed (using intent) to the search activity where you will do the searching. 
  4. If you press back button in search activity you will be taken back again to search input field so you can search again.

So how to implement this? follow these steps.

Step 1: In the app/bulid.gradle file make sure that minSdkVersion is 15 and targetSdkVersion is greater than or equal to 15. If you are looking for backward compatibility refer to official google docs.

Step 2: Declare search_title and search_hint string resource. Don't skip this step. Everything is explained in the next step.

res/values/strings.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<resources>
    <string name="app_name">ActionSearchDemo</string>

    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>

    <string name="search_title">Search</string>
    <string name="search_hint">Search Hint</string>
    <string name="title_activity_search">SearchActivity</string>
</resources>

Step 3: Declare searchable.xml file. If you don't have xml folder create one. Imp Note: Use references in android:label and android:hint. If you use hardcode strings here it might not work.

res/xml/searchable.xml
1
2
3
4
5
<?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>

Step 4: Add search menu item to the action bar. Imp Note: Make sure android:actionViewClass and app:actionViewClass are declared as shown below or else it will not work.

res/menu/menu_main.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<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/search"
        android:title="@string/search_title"
        android:icon="@android:drawable/ic_menu_search"
        app:showAsAction="collapseActionView|ifRoom"
        android:actionViewClass="android.support.v7.widget.SearchView"
        app:actionViewClass="android.support.v7.widget.SearchView" />

    <item android:id="@+id/action_settings" android:title="@string/action_settings"
        android:orderInCategory="100" app:showAsAction="never" />
</menu>

Step 5: Load SearchView onto action bar. Imp Note: Make sure you import this search view, android.support.v7.widget.SearchView.

MainActivity.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.example.actionsearchdemo;

import android.app.SearchManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);

        SearchManager searchManager =
                (SearchManager) getSystemService(SEARCH_SERVICE);
        SearchView searchView =
                (SearchView) menu.findItem(R.id.search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        ...
    }
}

Step 6: The search term is passed to your search activity class through intent. Below you can see how to access it from intent.

SearchActivity.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.actionsearchdemo;

import android.app.SearchManager;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class SearchActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);

        handleIntent(getIntent());
    }

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

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

            Log.d("testapp", "your search term: "+query);
        }
    }
}

Step 7: You link your search bar with your search activity class in AndroidManifest.xml file.

AndroidManifest.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.actionsearchdemo" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <!-- Declare which activity should handle search -->
            <meta-data
                android:name="android.app.default_searchable"
                android:value=".SearchActivity" />
        </activity>
        <activity
            android:name=".SearchActivity"
            android:label="@string/title_activity_search" >
            <!-- Declaring this activity handles search using intent filter-->
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
            <!-- where is search bar info? -->
            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>
    </application>

</manifest>

that's it. happy coding. cheers.