Creating a SearchView that looks like the material design guidelines Creating a SearchView that looks like the material design guidelines android android

Creating a SearchView that looks like the material design guidelines


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;    }    ... }

Result

enter image description here

enter image description here


After a week of puzzling over this. I think I've figured it out.
I'm now using just an EditText inside of the Toolbar. This was suggested to me by oj88 on reddit.

I now have this:
New SearchView

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        // https://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) {            // https://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}/** * Modified version of this, https://stackoverflow.com/a/26836272/1692770<br> * I flipped the start offset around for the animations because it seemed like it was the wrong way around to me.<br> * I also added a listener to the animation so I can find out when the home button has finished rotating. */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></android.support.v7.widget.Toolbar>

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        // https://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);


I know its a old thread but still posting the library I just made. Hope this might help someone.

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

Meterial Search View