57

I have been looking for ways to implement a searchview in the activity toolbar (actionbar) as per the material design guidelines.

On clicking on the search icon, the entire toolbar animates to have only the search EditText with white background with suggestions appearing in the main view instead of a drop down.

Here is a screenshot from the guidelines: enter image description here

Here is a gif from the Gmail Inbox implementation: enter image description here

I have been looking for code examples and tutorials but so far I have been unsuccesful. How do I go about doing this?

Bolt UIX
  • 5,988
  • 6
  • 31
  • 58
GunnerFan
  • 3,576
  • 3
  • 25
  • 38
  • isn't this a duplicate of https://stackoverflow.com/questions/27556623/creating-a-searchview-that-looks-like-the-material-design-guidelines ? – Adam Burley Jan 26 '21 at 17:11

7 Answers7

141

I tried several material SearchView libraries, but none of them worked good as the one from the support library, so I decided to redesign it, after a lot of work, I am pleased with the result:

enter image description here

Here is how you can do it:

1) Add SearchView item to your menu

<item
    android:id="@+id/m_search"
    android:icon="@drawable/ic_action_search"
    android:title="@string/search_title"
    app:actionLayout="@layout/search_view_layout"
    app:showAsAction="ifRoom|collapseActionView" />

Notice that I'm declaring actionLayout instead of actionViewClass, I figured that this is the only way to set SearchView theme separately from Toolbar theme.

search_view_layout.xml:

<android.support.v7.widget.SearchView
    android:id="@+id/search_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/SearchViewTheme" />

2) Add the custom SearchView theme to your styles, declare SearchView theme in your Toolbar theme as well:

<style name="SearchViewTheme" parent="Widget.AppCompat.SearchView.ActionBar">
    <item name="layout">@layout/toolbar_search_view</item>
    <item name="commitIcon">@drawable/ic_search_commit</item>
    <item name="colorControlNormal">@color/material_light_active_icon</item>
    <item name="colorControlHighlight">@color/material_ripple_light</item>
    <item name="autoCompleteTextViewStyle">@style/AutoCompleteTextViewStyle</item>
    <item name="suggestionRowLayout">@layout/search_view_suggestion_row</item>
    <item name="android:maxWidth">9999dp</item>
</style>

<style name="AutoCompleteTextViewStyle" parent="Widget.AppCompat.Light.AutoCompleteTextView">
    <item name="android:popupBackground">@drawable/search_suggestions_bg</item>
    <item name="android:popupElevation">0dp</item>
</style>

<style name="ToolbarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
    <item name="searchViewStyle">@style/SearchViewTheme</item>
</style>

toolbar_search_view.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/search_bar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:paddingEnd="8dp">

<!-- This is actually used for the badge icon *or* the badge label (or neither) -->
<TextView
    android:id="@+id/search_badge"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_marginBottom="2dp"
    android:drawablePadding="0dp"
    android:gravity="center_vertical"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="?android:attr/textColorPrimary"
    android:visibility="gone" />

<ImageView
    android:id="@+id/search_button"
    style="?attr/actionButtonStyle"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="center_vertical"
    android:contentDescription="@string/abc_searchview_description_search"
    android:focusable="true" />

<LinearLayout
    android:id="@+id/search_edit_frame"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:layoutDirection="locale"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/search_mag_icon"
        style="@style/RtlOverlay.Widget.AppCompat.SearchView.MagIcon"
        android:layout_width="@dimen/abc_dropdownitem_icon_width"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:scaleType="centerInside"
        android:visibility="gone" />

    <!-- Inner layout contains the app icon, button(s) and EditText -->
    <LinearLayout
        android:id="@+id/search_plate"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:orientation="horizontal">

        <view
            android:id="@+id/search_src_text"
            class="android.support.v7.widget.SearchView$SearchAutoComplete"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:layout_marginEnd="@dimen/item_list_horizontal_margin"
            android:layout_marginStart="@dimen/item_list_horizontal_margin"
            android:layout_weight="1"
            android:background="@null"
            android:dropDownAnchor="@id/anchor_dropdown"
            android:dropDownHeight="wrap_content"
            android:dropDownHorizontalOffset="0dp"
            android:dropDownVerticalOffset="0dp"
            android:ellipsize="end"
            android:imeOptions="actionSearch"
            android:inputType="text|textAutoComplete|textNoSuggestions"
            android:maxLines="1"
            android:paddingEnd="8dp"
            android:textColor="@android:color/black"
            android:textColorHint="@color/material_light_hint_text"
            android:textSize="20sp" />

        <ImageView
            android:id="@+id/search_close_btn"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:contentDescription="@string/abc_searchview_description_clear"
            android:focusable="true"
            android:paddingEnd="8dp"
            android:paddingStart="8dp" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/submit_area"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/search_go_btn"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:contentDescription="@string/abc_searchview_description_submit"
            android:focusable="true"
            android:paddingEnd="8dp"
            android:paddingStart="8dp"
            android:visibility="gone" />

        <ImageView
            android:id="@+id/search_voice_btn"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:contentDescription="@string/abc_searchview_description_voice"
            android:focusable="true"
            android:paddingEnd="8dp"
            android:paddingStart="8dp"
            android:visibility="gone" />
    </LinearLayout>
</LinearLayout>

Notice that I added anchor dropdown view under the Toolbar view, so suggestions will get full screen width.

<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/appBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

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

<View
    android:id="@+id/anchor_dropdown"
    android:layout_width="match_parent"
    android:layout_height="0dp" />

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

search_view_suggestion_row.xml:

(change suggestion_divider visibility if you want divider between suggestions):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="58dp"
    android:theme="@style/Theme.AppCompat.DayNight">

<!-- Icons come first in the layout, since their placement doesn't depend on
     the placement of the text views. -->
<ImageView
    android:id="@android:id/icon1"
    style="@style/RtlOverlay.Widget.AppCompat.Search.DropDown.Icon1"
    android:layout_width="56dp"
    android:layout_height="56dp"
    android:layout_alignParentBottom="true"
    android:layout_alignParentTop="true"
    android:scaleType="centerInside"
    android:visibility="invisible" />

<ImageView
    android:id="@+id/edit_query"
    style="@style/RtlOverlay.Widget.AppCompat.Search.DropDown.Query"
    android:layout_width="56dp"
    android:layout_height="56dp"
    android:layout_alignParentBottom="true"
    android:layout_alignParentTop="true"
    android:background="?attr/selectableItemBackground"
    android:scaleType="centerInside"
    android:visibility="gone" />

<ImageView
    android:id="@id/android:icon2"
    style="@style/RtlOverlay.Widget.AppCompat.Search.DropDown.Icon2"
    android:layout_width="56dp"
    android:layout_height="56dp"
    android:layout_alignParentBottom="true"
    android:layout_alignParentTop="true"
    android:layout_alignWithParentIfMissing="true"
    android:scaleType="centerInside"
    android:visibility="gone" />

<!-- The subtitle comes before the title, since the height of the title depends on whether the
     subtitle is visible or gone. -->
<TextView
    android:id="@android:id/text2"
    style="?android:attr/dropDownItemStyle"
    android:layout_width="match_parent"
    android:layout_height="29dp"
    android:layout_alignParentBottom="true"
    android:layout_alignWithParentIfMissing="true"
    android:gravity="top"
    android:maxLines="1"
    android:paddingBottom="4dp"
    android:textColor="?android:textColorSecondary"
    android:textSize="12sp"
    android:visibility="gone" />

<!-- The title is placed above the subtitle, if there is one. If there is no
     subtitle, it fills the parent. -->
<TextView
    android:id="@android:id/text1"
    style="?android:attr/dropDownItemStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@android:id/text2"
    android:layout_centerVertical="true"
    android:ellipsize="end"
    android:maxLines="1"
    android:scrollHorizontally="false"
    android:textColor="?android:textColorPrimary"
    android:textSize="16sp" />

<View
    android:id="@+id/suggestion_divider"
    android:layout_width="match_parent"
    android:layout_height="0.5dp"
    android:layout_alignParentBottom="true"
    android:layout_alignStart="@android:id/text1"
    android:layout_marginStart="8dp"
    android:background="@color/divider_color"
    android:visibility="gone" />

The suggestions background and the commit icon are custom made, the rest of the icons I used can be found at: https://material.io/icons/

ic_search_commit.xml:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:autoMirrored="true"
    android:viewportHeight="24.0"
    android:viewportWidth="24.0">
    <path
        android:fillColor="@color/active_icon_color"
        android:pathData="m18.364,16.95l-8.605,-8.605l7.905,-0l-0.007,-2.001l-11.314,0l0,11.314l1.994,-0l0.007,-7.898l8.605,8.605l1.414,-1.414z" />

search_suggestions_bg.xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <shape android:shape="rectangle">
        <padding android:top="0.5dp" />
        <stroke
            android:width="0.5dp"
            android:color="@color/divider_color" />
    </shape>
</item>
<item>
    <shape android:shape="rectangle">
        <solid android:color="@color/cards_and_dialogs_color" />
    </shape>
</item>
</layer-list>

Add following values to your colors.xml (add values-night only if you are using DayNight theme):

values/colors.xml

<color name="material_light_primary_text">#DE000000</color>
<color name="material_light_hint_text">#61000000</color>
<color name="material_light_active_icon">#8A000000</color>
<color name="material_ripple_light">#1F000000</color>
<color name="divider_color">#1F000000</color>
<color name="active_icon_color">#8A000000</color>
<color name="cards_and_dialogs_color">@android:color/white</color>
<color name="quantum_grey_600">#757575</color>

values-night/colors.xml:

<color name="divider_color">#1FFFFFFF</color>
<color name="active_icon_color">@android:color/white</color>
<color name="cards_and_dialogs_color">#424242</color>

3) Last part, make the magic happen in code:

Setup and initialize SearchView in your desired activity

private MenuItem mSearchItem;
private Toolbar mToolbar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);

    mSearchItem = menu.findItem(R.id.m_search);

    MenuItemCompat.setOnActionExpandListener(mSearchItem, new MenuItemCompat.OnActionExpandListener() {
        @Override
        public boolean onMenuItemActionCollapse(MenuItem item) {
            // Called when SearchView is collapsing
            if (mSearchItem.isActionViewExpanded()) {
                animateSearchToolbar(1, false, false);
            }
            return true;
        }

        @Override
        public boolean onMenuItemActionExpand(MenuItem item) {
            // Called when SearchView is expanding
            animateSearchToolbar(1, true, true);
            return true;
        }
    });

    return true;
}

public void animateSearchToolbar(int numberOfMenuIcon, boolean containsOverflow, boolean show) {

    mToolbar.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white));
    mDrawerLayout.setStatusBarBackgroundColor(ContextCompat.getColor(this, R.color.quantum_grey_600));

    if (show) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            int width = mToolbar.getWidth() -
                    (containsOverflow ? getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material) : 0) -
                    ((getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_material) * numberOfMenuIcon) / 2);
            Animator createCircularReveal = ViewAnimationUtils.createCircularReveal(mToolbar,
                    isRtl(getResources()) ? mToolbar.getWidth() - width : width, mToolbar.getHeight() / 2, 0.0f, (float) width);
            createCircularReveal.setDuration(250);
            createCircularReveal.start();
        } else {
            TranslateAnimation translateAnimation = new TranslateAnimation(0.0f, 0.0f, (float) (-mToolbar.getHeight()), 0.0f);
            translateAnimation.setDuration(220);
            mToolbar.clearAnimation();
            mToolbar.startAnimation(translateAnimation);
        }
    } else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            int width = mToolbar.getWidth() -
                    (containsOverflow ? getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material) : 0) -
                    ((getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_material) * numberOfMenuIcon) / 2);
            Animator createCircularReveal = ViewAnimationUtils.createCircularReveal(mToolbar,
                    isRtl(getResources()) ? mToolbar.getWidth() - width : width, mToolbar.getHeight() / 2, (float) width, 0.0f);
            createCircularReveal.setDuration(250);
            createCircularReveal.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    mToolbar.setBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimary));
                    mDrawerLayout.setStatusBarBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimaryDark));
                }
            });
            createCircularReveal.start();
        } else {
            AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
            Animation translateAnimation = new TranslateAnimation(0.0f, 0.0f, 0.0f, (float) (-mToolbar.getHeight()));
            AnimationSet animationSet = new AnimationSet(true);
            animationSet.addAnimation(alphaAnimation);
            animationSet.addAnimation(translateAnimation);
            animationSet.setDuration(220);
            animationSet.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    mToolbar.setBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimary));
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            mToolbar.startAnimation(animationSet);
        }
        mDrawerLayout.setStatusBarBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimaryDark));
    }
}

private boolean isRtl(Resources resources) {
    return resources.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}

private static int getThemeColor(Context context, int id) {
    Resources.Theme theme = context.getTheme();
    TypedArray a = theme.obtainStyledAttributes(new int[]{id});
    int result = a.getColor(0, 0);
    a.recycle();
    return result;
}

Few things to notice about the code:

1) The animation will adjust it's start point based on your set of number of menu items and if the toolbar has overflow icon, it will detect if layout is LTR or RTL automatically.

2) I'm using navigation drawer activity, so I set StatusBar color to mDrawerLayout, if you are using regular activity, you can set StatusBar color this way:

getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.quantum_grey_600));

3) The circular reveal animation will only work on KitKat and above.

Vikas Patidar
  • 42,865
  • 22
  • 93
  • 106
shnizlon
  • 1,486
  • 1
  • 11
  • 8
  • 10
    This looks awesome, could you possibly put it onto github? Where you create toolbar_search_view.xml, is that appbarlayout xml in the same file? – zen_1991 Jan 19 '17 at 07:10
  • I have add [circleReveal animation](https://github.com/ozodrukh/CircularRevea) with third party library at below Lollipop Just put toolbar inside io.codetail.widget.RevealFrameLayout frame layout and import io.codetail.animation.ViewAnimationUtils rather then android class and see the magic – Zohaib Akram Feb 01 '17 at 13:06
  • 2
    Note that toolbar_search_view.xml is a changed copy of abc_search_view.xml from the support library, that is Apache 2.0 licensed. So when using this, you should ensure you comply with that license. – Simon Warta May 21 '17 at 16:08
  • "android:popupElevation" work only Android 5.0+. But I need this for Android 4.3. – Alexei Oct 17 '17 at 10:30
  • 1
    you are using a dark actionBar theme, so the title and icons are white (on a light blue background in your example), but when the search expand the colors are now gray, how do you achieve this? mine is still white so it's white on white... – ndori Aug 15 '18 at 07:16
  • when the search is not the rightmost, the voice/x button is aligned to that spot and not to the end, is it possible to fix this? – ndori Aug 15 '18 at 09:19
  • @ndori add this line to your styles.xml @drawable/back_arrow and don't forget to create the arrow and color it a darker shade. – A P Aug 28 '18 at 07:21
  • This worked great for me. However, the commit icon is not showing up and I still get the very ugly recents icon. Anyone know how to change the recents icon in suggestions? – Nathan Schwermann Nov 19 '18 at 17:02
  • I am implementing this code but translating to Xamarin Android. I have found different disadvantages but I think I'm close to getting it. Maybe someone has previously done this work in Xamarin that you can share with us? – JotaPardo Jun 04 '19 at 20:08
  • anyone have a full implementation of this in android, can share the git repo? I have implemented this but there are few customizations required. – Sagar Nayak Jun 24 '19 at 13:01
  • How can i get the entered text? – Vivek Makwana Feb 21 '20 at 14:23
  • looks great, however technically first the "Courses" text is removed, then the animation starts. that leads to some kind of optical flickering (one cna split the gif in frames to confirm this). How to keep the courses text and just animate over it? – stefan.at.kotlin Jul 25 '20 at 08:24
  • mDrawerLayout is not declared – Mohit Yadav Sep 05 '20 at 06:48
  • @VivekMakwana how did u set search view queryListener in this ? – xaif May 07 '21 at 12:59
11

It is actually quite easy to do this, if you are using android.support.v7 library.

Step - 1

Declare a menu item

<item android:id="@+id/action_search"
android:title="Search"
android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />

Step - 2

Extend AppCompatActivity and in the onCreateOptionsMenu setup the SearchView.

import android.support.v7.widget.SearchView;

public class YourActivity extends AppCompatActivity {

    ...

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_home, menu);
        // Retrieve the SearchView and plug it into SearchManager
        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
        SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        return true;
    }

    ... 
}
Sam
  • 4,475
  • 2
  • 28
  • 31
9

The idea is very simple - you have to write your own AutoCompleteTextView using EditText, TextWatcher and RecyclerView with Filterable adapter.

  • EditText gives you a text field with ability to input characters
  • TextWatcher allows you to watch for text changes
  • RecyclerView can be placed anywhere, so you can show the search results just like on your screenshot
  • Filterable adapter helps to present data filtered with the entered text

So:

  • make a layout with EditText on the top, with RecyclerView filling the remaining space. Add the icon, shadow, etc.
  • add a TextWatcher and update the adapter on each text change

If you'd like to see my solution in action, check out my project on github: https://github.com/ZieIony/Carbon

The Auto complete demo can be sound in the sample app in 'Demos' section.

Screenshot

Zielony
  • 16,239
  • 6
  • 34
  • 39
  • 2
    I think you misunderstood my question or maybe I did not phrase it right. My question was about implementing the UI part and not the text watching & filtering logic - whether android has any widgets (like ActionBar, Menu items etc ) which help in implementing this view as per material design guidelines? Turns out there are no such widgets and you have to build the toolbar yourself. Once you figure this out, we can use your answer to build the filtering and text watching logic. Although I have upvoted your answer and found it useful, I did not accept it as it answers only part of the question. – GunnerFan Aug 07 '15 at 11:38
7

Taking a hint from @Zielony's answer I did the following:

1) Instead if using an ActionBar or ToolBar I built my own layout (basically a RelativeLayout with burger menu, search and other menu buttons and a EditText for search)

2) Used a theme without an ActionBar, placed my custom layout at the top of the activity so that it appeared like an ActionBar.

3) In the search button's OnClickListener I do 2 things:

  • Hide the menu buttons and show the 'search' EditText.
  • Add a fragment to display search suggestions and search
  • Show the soft keyboard input

3) Added OnClickListeners for the other menu buttons.

4) Added a TextWatcher on the 'search' EditText to display search hints and results from the server.

This is how it appears now: enter image description here

GunnerFan
  • 3,576
  • 3
  • 25
  • 38
2

I think I've figured it out. I'm now using just an EditText inside of the Toolbar.

I now have this:

enter image description here

First inside onCreate() of my activity I added the EditText with an image view on the right hand side to the Toolbar like this:

// Setup search container view
searchContainer = new LinearLayout(this);
Toolbar.LayoutParams containerParams = new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
containerParams.gravity = Gravity.CENTER_VERTICAL;
searchContainer.setLayoutParams(containerParams);

// Setup search view
toolbarSearchView = new EditText(this);
// Set width / height / gravity
int[] textSizeAttr = new int[]{android.R.attr.actionBarSize};
int indexOfAttrTextSize = 0;
TypedArray a = obtainStyledAttributes(new TypedValue().data, textSizeAttr);
int actionBarHeight = a.getDimensionPixelSize(indexOfAttrTextSize, -1);
a.recycle();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, actionBarHeight);
params.gravity = Gravity.CENTER_VERTICAL;
params.weight = 1;
toolbarSearchView.setLayoutParams(params);

// Setup display
toolbarSearchView.setBackgroundColor(Color.TRANSPARENT);
toolbarSearchView.setPadding(2, 0, 0, 0);
toolbarSearchView.setTextColor(Color.WHITE);
toolbarSearchView.setGravity(Gravity.CENTER_VERTICAL);
toolbarSearchView.setSingleLine(true);
toolbarSearchView.setImeActionLabel("Search", EditorInfo.IME_ACTION_UNSPECIFIED);
toolbarSearchView.setHint("Search");
toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff"));
try {
    // Set cursor colour to white
    // http://stackoverflow.com/a/26544231/1692770
    // https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
    Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
    f.setAccessible(true);
    f.set(toolbarSearchView, R.drawable.edittext_whitecursor);
} catch (Exception ignored) {
}

// Search text changed listener
toolbarSearchView.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container);
        if (mainFragment != null && mainFragment instanceof MainListFragment) {
            ((MainListFragment) mainFragment).search(s.toString());
        }
    }

    @Override
    public void afterTextChanged(Editable s) {
        // http://stackoverflow.com/a/6438918/1692770
        if (s.toString().length() <= 0) {
            toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff"));
        }
    }
});
((LinearLayout) searchContainer).addView(toolbarSearchView);

// Setup the clear button
searchClearButton = new ImageView(this);
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics());
LinearLayout.LayoutParams clearParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
clearParams.gravity = Gravity.CENTER;
searchClearButton.setLayoutParams(clearParams);
searchClearButton.setImageResource(R.drawable.ic_close_white_24dp); // TODO: Get this image from here: https://github.com/google/material-design-icons
searchClearButton.setPadding(px, 0, px, 0);
searchClearButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        toolbarSearchView.setText("");
    }
});
((LinearLayout) searchContainer).addView(searchClearButton);

// Add search view to toolbar and hide it
searchContainer.setVisibility(View.GONE);
toolbar.addView(searchContainer);

This worked, but then I came across an issue where onOptionsItemSelected() wasn't being called when I tapped on the home button. So I wasn't able to cancel the search by pressing the home button. I tried a few different ways of registering the click listener on the home button but they didn't work.

Eventually I found out that the ActionBarDrawerToggle I had was interfering with things, so I removed it. This listener then started working:

 toolbar.setNavigationOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // toolbarHomeButtonAnimating is a boolean that is initialized as false. It's used to stop the user pressing the home button while it is animating and breaking things.
        if (!toolbarHomeButtonAnimating) {
            // Here you'll want to check if you have a search query set, if you don't then hide the search box.
            // My main fragment handles this stuff, so I call its methods.
            FragmentManager fragmentManager = getFragmentManager();
            final Fragment fragment = fragmentManager.findFragmentById(R.id.container);
            if (fragment != null && fragment instanceof MainListFragment) {
                if (((MainListFragment) fragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) {
                    displaySearchView(false);
                    return;
                }
            }
        }

        if (mDrawerLayout.isDrawerOpen(findViewById(R.id.navigation_drawer)))
            mDrawerLayout.closeDrawer(findViewById(R.id.navigation_drawer));
        else
            mDrawerLayout.openDrawer(findViewById(R.id.navigation_drawer));
    }
});

So I can now cancel the search with the home button, but I can't press the back button to cancel it yet. So I added this to onBackPressed():

FragmentManager fragmentManager = getFragmentManager();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.container);
if (mainFragment != null && mainFragment instanceof MainListFragment) {
    if (((MainListFragment) mainFragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) {
        displaySearchView(false);
        return;
    }
}

I created this method to toggle visibility of the EditText and menu item:

public void displaySearchView(boolean visible) {
    if (visible) {
        // Stops user from being able to open drawer while searching
        mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);

        // Hide search button, display EditText
        menu.findItem(R.id.action_search).setVisible(false);
        searchContainer.setVisibility(View.VISIBLE);

        // Animate the home icon to the back arrow
        toggleActionBarIcon(ActionDrawableState.ARROW, mDrawerToggle, true);

        // Shift focus to the search EditText
        toolbarSearchView.requestFocus();

        // Pop up the soft keyboard
        new Handler().postDelayed(new Runnable() {
            public void run() {
                toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0));
                toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0));
            }
        }, 200);
    } else {
        // Allows user to open drawer again
        mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);

        // Hide the EditText and put the search button back on the Toolbar.
        // This sometimes fails when it isn't postDelayed(), don't know why.
        toolbarSearchView.postDelayed(new Runnable() {
            @Override
            public void run() {
                toolbarSearchView.setText("");
                searchContainer.setVisibility(View.GONE);
                menu.findItem(R.id.action_search).setVisible(true);
            }
        }, 200);

        // Turn the home button back into a drawer icon
        toggleActionBarIcon(ActionDrawableState.BURGER, mDrawerToggle, true);

        // Hide the keyboard because the search box has been hidden
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(toolbarSearchView.getWindowToken(), 0);
    }
}

I needed a way to toggle the home button on the toolbar between the drawer icon and the back button. I eventually found the method below in this SO answer. Though I modified it slightly to made more sense to me:

private enum ActionDrawableState
{
  BURGER, ARROW 
}

private void toggleActionBarIcon(final ActionDrawableState state, final ActionBarDrawerToggle toggle, boolean animate) {
    if (animate) {
        float start = state == ActionDrawableState.BURGER ? 1.0f : 0f;
        float end = Math.abs(start - 1);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            ValueAnimator offsetAnimator = ValueAnimator.ofFloat(start, end);
            offsetAnimator.setDuration(300);
            offsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            offsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float offset = (Float) animation.getAnimatedValue();
                    toggle.onDrawerSlide(null, offset);
                }
            });
            offsetAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {

                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    toolbarHomeButtonAnimating = false;
                }

                @Override
                public void onAnimationCancel(Animator animation) {

                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });
            toolbarHomeButtonAnimating = true;
            offsetAnimator.start();
        }
    } else {
        if (state == ActionDrawableState.BURGER) {
            toggle.onDrawerClosed(null);
        } else {
            toggle.onDrawerOpened(null);
        }
    }
}

This works, I've managed to work out a few bugs that I found along the way. I don't think it's 100% but it works well enough for me. EDIT: If you want to add the search view in XML instead of Java do this:

toolbar.xml:

<android.support.v7.widget.Toolbar 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
contentInsetLeft="72dp"
contentInsetStart="72dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:minHeight="?attr/actionBarSize"
app:contentInsetLeft="72dp"
app:contentInsetStart="72dp"
app:popupTheme="@style/ActionBarPopupThemeOverlay"
app:theme="@style/ActionBarThemeOverlay">

<LinearLayout
    android:id="@+id/search_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <EditText
        android:id="@+id/search_view"
        android:layout_width="0dp"
        android:layout_height="?attr/actionBarSize"
        android:layout_weight="1"
        android:background="@android:color/transparent"
        android:gravity="center_vertical"
        android:hint="Search"
        android:imeOptions="actionSearch"
        android:inputType="text"
        android:maxLines="1"
        android:paddingLeft="2dp"
        android:singleLine="true"
        android:textColor="#ffffff"
        android:textColorHint="#b3ffffff" />

    <ImageView
        android:id="@+id/search_clear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:src="@drawable/ic_close_white_24dp" />
</LinearLayout>

onCreate() of your Activity:

searchContainer = findViewById(R.id.search_container);
toolbarSearchView = (EditText) findViewById(R.id.search_view);
searchClearButton = (ImageView) findViewById(R.id.search_clear);

// Setup search container view
try {
    // Set cursor colour to white
    // http://stackoverflow.com/a/26544231/1692770
    // https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
    Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
    f.setAccessible(true);
    f.set(toolbarSearchView, R.drawable.edittext_whitecursor);
} catch (Exception ignored) {
}

// Search text changed listener
toolbarSearchView.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container);
        if (mainFragment != null && mainFragment instanceof MainListFragment) {
            ((MainListFragment) mainFragment).search(s.toString());
        }
    }

    @Override
    public void afterTextChanged(Editable s) {
    }
});

// Clear search text when clear button is tapped
searchClearButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        toolbarSearchView.setText("");
    }
});

// Hide the search view
searchContainer.setVisibility(View.GONE);
2

Here is how I tried to implement it, please check this out.

https://github.com/Shahroz16/material-searchview

Meterial Search View

ShahrozKhan91
  • 710
  • 10
  • 19
  • I really loved this library, but i'm having a problem with detected view in Fragment, can you add a tutorial in the github page for Fragments ? really want this! thanks. – Jaeger Jul 15 '16 at 03:39
0

You can use AutoCompleteTextView to achieve this, Follow the link below

How to build Gmail like search box in the action bar?

Community
  • 1
  • 1
Vinay Jayaram
  • 1,030
  • 9
  • 29
  • I already have this. I want to implement it as per material design guidelines. Notice how sugesstions appear in the main view instead of dropdown. – GunnerFan May 21 '15 at 09:28