Wednesday, June 14, 2017

Android Material SearchView

Welcome to this post.
Android Material Search view tutorial. In this tutorial, we going to make a project and Implement A search view on the toolbar.
For search view, I use a 3rd party library Material SearchView

Note: If you don't want to use 3rd party library see this post,  Android Material SearchView with Recent Search Suggestions

Let's start-
Before starting, see a screenshot-

material_search_view
Material Search View


we have to do with this project-
1. we set search view on the toolbar.
2. Filter RecyclerView with text input
3. voice search

some bonus- In this project,
1. use SQL database for store data2. use the content provider to access data.
3. use Loaders for load data(Curser loaders).

Let's Start- First, add this line to your build.gradle file and sync gradle.
compile 'br.com.mauker.materialsearchview:materialsearchview:1.2.2'
Now go to on your style.xml file and below style-
<style name="MaterialSearchViewStyle">
  <item name="searchBackground">@color/white_ish</item>
  <item name="searchVoiceIcon">@drawable/ic_action_voice_search</item>
  <item name="searchCloseIcon">@drawable/ic_action_navigation_close</item>
  <item name="searchBackIcon">@drawable/ic_action_navigation_arrow_back</item>
  <item name="searchSuggestionBackground">@color/search_layover_bg</item>
  <item name="searchBarHeight">?attr/actionBarSize</item>
  <item name="voiceHintPrompt">@string/hint_prompt</item>
  <item name="android:textColor">@color/black</item>
  <item name="android:textColorHint">@color/gray_50</item>
  <item name="android:hint">@string/search_hint</item>
  <item name="android:inputType">textCapWords</item>
  </style>
OK, now time to add a toolbar to the layout. In this case, I add on activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  tools:context=".MainActivity"
  android:background="@color/colorPrimary"
  android:layout_marginBottom="2dp"
  android:elevation="6dp">
  <!-- don't forget to set elevation or it look like old action bar-->

  <android.support.v7.widget.Toolbar
  android:id="@+id/toolbar"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  android:background="?attr/colorPrimary"
  android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
  app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

  <br.com.mauker.materialsearchview.MaterialSearchView
  android:id="@+id/search_view"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  style="@style/MaterialSearchViewStyle"/>

  </RelativeLayout>
Note: I will provide GitHub link to this project later in this post

Now add a menu button on menu.xml-
<item
  android:id="@+id/action_search"
  android:icon="@drawable/ic_search"
  android:orderInCategory="100"
  android:title="@string/abc_search_hint"
  app:showAsAction="always" />

options menu looks like:

menu
Material Search View (options menu)


Now time to write java code- In this case MainActivity.java
MaterialSearchView searchView;
and again this view on onCreate-
searchView = (MaterialSearchView) findViewById(R.id.search_view);

now set all search operation I created a private method and call it on Oncreate-
In this method, we add three listeners to the search view-
1. setOnQueryTextListener
2. setSearchViewListener
3. setOnVoiceClickedListener In listener

1. setOnQueryTextListener is fired when we type text in the search view. In this listener, we have to override two methods name-
i. onQueryTextSubmit
ii. onQueryTextChange

i. onQueryTextSubmit is called when we put the query data with submit button.
after submit-
* build a URI
* call getContentResolver and query database and save it on cursor variable.
 * if the cursor is not null and cursor data is more than 0 we have data(Call cursor.getCount())
* so we go to the details activity with intent and set this URI with intent
*close search view

ii. onQueryTextChange is called every time when you write.
So we have to set-
* we are going to update recycler view with type text
* we check the text length is greater than zero
* then we create a selection string variable for cursor query. we use SQL LIKE statement not where statement.
String selection = MainWordDBContract.Entry.COLUMN_WORD +" like ? ";
* add also selection Argument-
String[] selectionArg = new String[]{newText+"%"};
* same way to get cursor and same condition for update UI as onQueryTextSubmit

Let's see the code-
searchView.setOnQueryTextListener(new MaterialSearchView.OnQueryTextListener() {
  @Override
  public boolean onQueryTextSubmit(String query) {

  Uri uri = MainWordDBContract.Entry.buildUriWithWord(query.toUpperCase());
  Cursor cursor = getContentResolver().query(uri,
  MainActivity.projection,null,null,null);

  if (cursor != null && cursor.getCount() > 0){
  Intent intent = new Intent(MainActivity.this,
  DetailsActivity.class);
  intent.setData(uri);
  startActivity(intent);
  searchView.closeSearch();
  searchView.setCloseOnTintClick(false);
  }

  if (cursor != null){
  cursor.close();
  }
  return true;
  }

  @Override
  public boolean onQueryTextChange(String newText) {

  if (newText.length() > 0){
  String selection = MainWordDBContract.Entry.COLUMN_WORD +" like ? ";
  //if you are try to search from any position of word
  //then use
  //String[] selectionArg = new String[]{"%"+newText+"%"};
  //if you try to search from start of word the use this line
  String[] selectionArg = new String[]{newText+"%"};

  Cursor cursor = getContentResolver().query(MainWordDBContract.Entry.CONTENT_URI,
  MainActivity.projection,selection,selectionArg,null);

  if (cursor != null && cursor.getCount() > 0){
  mAdapter.swapCursor(cursor);
  }

  return true;
  } else {
  return false;
  }
  }
  });
Now add the setSearchViewListener listener and there are also two methods.
* if search view is open then hide the fab
* if searchView close the show the fab again Code-
searchView.setSearchViewListener(new MaterialSearchView.SearchViewListener() {
  @Override
  public void onSearchViewOpened() {
  fab.hide();
  }

  @Override
  public void onSearchViewClosed() {
  fab.show();
  }
  });
On the last, we have added setOnVoiceClickedListener if you are using this option. for this, I write another method name askSpeechInput so let's see the code one by one- the listener-
searchView.setOnVoiceClickedListener(new MaterialSearchView.OnVoiceClickedListener() {
  @Override
  public void onVoiceClicked() {
  askSpeechInput();
  }
  });
method-
private void askSpeechInput() {
  Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

  intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
  RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);

  intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
  intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
  "Speak your desire word");
  try {
  startActivityForResult(intent, MaterialSearchView.REQUEST_VOICE);
  } catch (ActivityNotFoundException a) {
  a.printStackTrace();
  slet("Activity not found", a);
  Toast.makeText(this, "Sorry Speech To Text is not " +
  "supported in your device", Toast.LENGTH_SHORT).show();
  }
  }
one more method to go. we are now overriding onActivityResult for getting voice input-
@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == MaterialSearchView.REQUEST_VOICE && resultCode == RESULT_OK) {
            ArrayList<String> matches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
            if (matches != null && matches.size() > 0) {
                String searchWrd = matches.get(0);
                if (!TextUtils.isEmpty(searchWrd)) {

                    //Todo more accure on settings
                    searchView.setQuery(searchWrd, false);
                    Uri uri = MainWordDBContract.Entry.buildUriWithWord(searchWrd.toUpperCase());

                    Cursor cursor = getContentResolver().query(uri,
                            MainActivity.projection,null,null,null);

                    if (cursor != null && cursor.getCount() > 0){
                        Intent intent = new Intent(MainActivity.this, DetailsActivity.class);
                        intent.setData(uri);
                        startActivity(intent);
                        searchView.closeSearch();
                        searchView.setCloseOnTintClick(false);
                    }

                    if (cursor != null){
                        cursor.close();
                    }
                }
            }

            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

voice search screen short:
voice
Material Search View (voice search)


add this line onResume method
searchView.activityResumed();
and last one-
@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        switch (item.getItemId()) {
            case R.id.action_search:
                // Open the search view on the menu item click.
                searchView.openSearch();
                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }
Now run your app and see the material search view

It will work fine.

Note: If you don't want to use 3rd party library to add search functionality, see this post.Android Material SearchView with Recent Search Suggestions
Thank you for reading

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 :