Sunday, December 24, 2017

Android Material SearchView with Recent Search Suggestions

Welcome to this post.

In this post, I am going to cover Android Search View with Recent Search Suggestion.

In a previous post, I already cover Android Material SearchView. In that post, I use a 3rd party library. If you want to use 3rd party library to add search functionality, see this post. Android Material SearchView

But Now I don't want to use any 3rd party library.
In this post, I use:
  • Search Widget
Let's start:
create a new activity:(In my case, I named it: SearchActivity)
First jump into XML code-
activity_search.xml: Now it looks like
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.blogspot.shudiptotrafder.soilscience.SearchActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/search_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay">
        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>
Now add a search Widget to the bottom of toolbar in AppBarLayout
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.blogspot.shudiptotrafder.soilscience.SearchActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/search_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay">

            <android.support.v7.widget.SearchView
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:id="@+id/searchViewN"
                app:iconifiedByDefault="false">
            </android.support.v7.widget.SearchView>

        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>

Note: If you want to filter your search data with user input then the best idea is adding a RecyclerView to this layout. But I am not providing you the code of recycler view. But Definitely I tell you where you insert the filter option and update the RecyclerView adapter and all the other item is same for RecyclerView, it's adapter.

Modify the Manifest.XML file
what to do-
  • set launch mode to single top
  • add an intent filter to identify this activity as searchable
  • add metadata with an XML file
<activity android:name=".SearchActivity"
    android:label="@string/search"
    android:launchMode="singleTop"
    android:theme="@style/AppTheme.NoActionBar">

    <!-- to identify this activity as "searchable" -->
    <intent-filter>
        <action android:name="android.intent.action.SEARCH" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>

    <meta-data
        android:name="android.app.searchable"
        android:resource="@xml/searchable" />
</activity>
Now the 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"
    android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
    android:voicePromptText="@string/speak_voice"
    android:searchSuggestAuthority="com.example.MySuggestionProvider"
    android:searchSuggestSelection=" ?" />
Note: you are not allowed here to write hardcode string. If you do that string is not showing.
In this searchView, I also use voice search. If you don't want to use this just simply remove this line
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:voicePromptText="@string/speak_voice"

Now move on Java code:
Create an instance of SearchView and SearchRecentSuggestion
private SearchView searchView;
private SearchRecentSuggestions suggestions;

We are using SearchRecentSuggestions, so we need a provider. Basically, this class needs a Content Provider that provide recent search data and those data is saved in the SQL Database. But the plus point is we don't need to write the SQL boilerplate code. Just need a class and extend this by SearchRecentSuggestions Provider.

MySuggestionProvider.java:
public class MySuggestionProvider extends SearchRecentSuggestionsProvider {

    public final static String AUTHORITY = "com.example.MySuggestionProvider";
    public final static int MODE = DATABASE_MODE_QUERIES;

    public MySuggestionProvider() {
        setupSuggestions(AUTHORITY, MODE);
    }
}
Note: Another Mode is also available. You can use any of them with your requirement. If you need 2 lines of search query then use below mode:
public final static int MODE = DATABASE_MODE_2LINES;
add this class to the manifest.
<!--Search Suggestion database provider -->
<provider android:name=".MySuggestionProvider"
       android:authorities="com.example.MySuggestionProvider"
       android:exported="false"
       android:enabled="true"/>

Now come back to SearchActivity.java

Create a SearchManager and also initialize Search View and SearchRecentSuggestion
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = findViewById(R.id.searchViewN);
suggestions = new SearchRecentSuggestions(this,
                MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE);
set searchable information to searchView
if (searchManager != null) {
     searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
}
add below line: to remove iconify and request focus on onCreate().
// Do not iconify the widget; expand it by default
searchView.setIconifiedByDefault(false);
searchView.setQueryRefinementEnabled(true);
searchView.requestFocus(1);

Now we add two listeners in search view.

  • onQueryTextListener
  • onSuggestionListener
OnQueryTextListener: In this listener, we need to implement two methods.
  • onQueryTextSubmit -> this method is called when the query is submitted on the searchView. So in this method, you need to implement the logic when you get the user's submitted a query. For example, you are creating a dictionary app. So user search for a word. When they submit the word you show the details of that word in details activity.
  • onQueryTextChange -> this method is called when the user changes the query. For example- the user input 'a' this method is called, and after user input again, now user input is 'ab', this method is called again. So here you can use your filter logic of list view or RecyclerView. 
See the code:
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                // user submit a query 
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                if (newText.length() > 0) {
            //so the text length is grater that 0
            // user input some thing
            // implements the filter logic
                    return true;
                } else {
            // user don't input anything
                    return false;
                }
            }
        });

OnSuggestionListener: 
In this listener, we need to implement two methods.

  • onSuggestionSelect -> when you select a suggestion
  • onSuggestionClick -> when you click a suggestion. When the user clicks the suggestion then we submit the query to searchView.
See the code-
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
            @Override
            public boolean onSuggestionSelect(int position) {
                return true;
            }

            @Override
            public boolean onSuggestionClick(int position) {
                CursorAdapter selectedView = searchView.getSuggestionsAdapter();
                Cursor cursor = (Cursor) selectedView.getItem(position);
                int index = cursor.getColumnIndexOrThrow(SearchManager.SUGGEST_COLUMN_TEXT_1);
                searchView.setQuery(cursor.getString(index), true);
                return true;
            }
        });

we need to override the onNewIntent() method-
@Override
    protected void onNewIntent(Intent intent) {
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            //query from the voice search.
           // use this query for your further action 
        }
    }

That's it:

By this way, you can enable search functionality without using any 3rd party library.

If you want to use 3rd party library to add search functionality, see this post. Android Material SearchView

Thank you for reading this post.
Happy coding.

About Author:

I am Shudipto Trafder,
Since 2015, I write android code. I love to implement new ideas. I use java and also kotlin. Now I am learning Flutter. I love to learn new things and also love to share. And that is the main reason to run this blog.


Let's Get Connected: Facebook Twitter Linkedin Github

No comments :