59

I'm having a bit of trouble customizing the search icon in the SearchView. On my point of view, the icon can be changed in the Item attributes, right? Just check the code bellow..

Can someone tell me what I'm doing wrong?

This is the menu I'm using, with my custom search icon icn_lupa. But when I run the app, I always get the default search icon...

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_search"
      android:title="@string/menu_search"
      android:icon="@drawable/icn_lupa"
      android:showAsAction="always"
      android:actionViewClass="android.widget.SearchView" />
</menu>
starball
  • 20,030
  • 7
  • 43
  • 238
Valdemar
  • 2,560
  • 3
  • 18
  • 14
  • 1
    I think @just_user 's answer should be marked the correct. Please review. :) – Zen Dec 14 '14 at 23:59
  • 1
    Recent support library `android.support.v7.widget.SearchView` can be styled with `app:searchIcon="@drawable/ic_your_custom_icon"` – Afilu Nov 27 '17 at 09:17

19 Answers19

100

I've found another way to change the search icon which goes in the same line as Diego Pino's answer but straight in onPrepareOptionsMenu.

In your menu.xml (same as before)

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_search"
      android:icon="@drawable/ic_action_fav"
      android:title="@string/action_websearch"
      android:showAsAction="always|never"
      android:actionViewClass="android.widget.SearchView" />
</menu>

In your activity:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    MenuItem searchViewMenuItem = menu.findItem(R.id.action_search);    
    mSearchView = (SearchView) searchViewMenuItem.getActionView();
    int searchImgId = getResources().getIdentifier("android:id/search_button", null, null);
    ImageView v = (ImageView) mSearchView.findViewById(searchImgId);
    v.setImageResource(R.drawable.your_new_icon); 
    mSearchView.setOnQueryTextListener(this);
    return super.onPrepareOptionsMenu(menu);
}

I followed the example for changing the edittext in this example.

You should be able to do this for all icons/backgrounds in your SearchView, to find the right ID you can check here.

UPDATE November 2017:

Since this answer android has been updated with the possibility of changing the search icon through the XML.

If you target anything below android v21 you can use:

<android.support.v7.widget.SearchView
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    app:searchIcon="@drawable/ic_search_white_24dp"
    app:closeIcon="@drawable/ic_clear_white_24dp" />

Or v21 and later:

<SearchView
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:searchIcon="@drawable/ic_search_white_24dp"
    android:closeIcon="@drawable/ic_clear_white_24dp" />

And there are even more options:

closeIcon
commitIcon
goIcon
searchHintIcon
searchIcon
voiceIcon
starball
  • 20,030
  • 7
  • 43
  • 238
just_user
  • 11,769
  • 19
  • 90
  • 135
  • 1
    Using identifier "android:id/search_button" is a great idea! I couldn't find it anywhere else. Thank your very much for the answer!!! – DenisMath Jul 16 '14 at 09:18
  • 1
    Brilliant - searched high and low and tried many things before finding your solution - thanks! – daveywc Sep 16 '14 at 10:45
  • 1
    thanks but when my searchview is expanded still uses the system icon. any idea? – mmlooloo Oct 26 '14 at 19:39
  • 3
    Update Hint with SpannableStringBuilder. Copied following from android source, SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon ssb.append(hintText); Drawable searchIcon = getResources().getDrawable(R.drawable.search_or); int textSize = (int) (searchTextView.getTextSize() * 1.25); searchIcon.setBounds(0, 0, textSize, textSize); ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); searchTextView.setHint(ssb); – Pravin Patil Nov 03 '14 at 18:58
  • updated answer shows preview in android studio properly, but when I run app it doesn't show effect at runtime. Strange.... – Krunal Panchal Aug 15 '18 at 13:16
  • 3
    I don't get how to use your updated version, could you also share with us your `menu.xml` too? – azerafati Aug 20 '18 at 14:18
  • using getIdentifier helped me to solve the problem thanks :) – other Tall guy Sep 03 '20 at 15:00
37

Nice answer from @just_user

For my case, since I am using the appcompat v7 library for the SearchView + ActionBar, i modified his solution a bit to make it compatible to my project, it should work so as long as you did not modify anything when you added appcompat v7 as library

XML:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:metrodeal="http://schemas.android.com/apk/res-auto" >


    <item
        android:id="@+id/main_menu_action_search"
        android:orderInCategory="100"
        android:title="@string/search"
        metrodeal:showAsAction="always"
        metrodeal:actionViewClass="android.support.v7.widget.SearchView" 
        android:icon="@drawable/search_btn"/>

</menu>

Java code:

@Override
public void onPrepareOptionsMenu(Menu menu) {
    MenuItem searchViewMenuItem = menu.findItem(R.id.main_menu_action_search);
    SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(searchViewMenuItem);
    int searchImgId = android.support.v7.appcompat.R.id.search_button; // I used the explicit layout ID of searchview's ImageView
    ImageView v = (ImageView) mSearchView.findViewById(searchImgId);
    v.setImageResource(R.drawable.search_btn);
    super.onPrepareOptionsMenu(menu);
}

Excuse for the very big icon (I have not resized the icon just yet), but it should work as it is.

enter image description here

patrickjason91
  • 865
  • 11
  • 20
  • 1
    I did the the same for sherlock library and it worked. Thanks. `MenuItem searchViewMenuItem = menu.findItem(R.id.search); SearchView mSearchView = (SearchView) searchViewMenuItem.getActionView(); ImageView v = (ImageView) mSearchView.findViewById(com.actionbarsherlock.R.id.abs__search_button);` v.setImageResource(R.drawable.ic_action_search); – nmvictor Aug 05 '14 at 16:36
  • For androidx: `androidx.appcompat.R.id.search_button`. And fix the ImageView scaling with `v.setScaleType(ImageView.ScaleType.CENTER_INSIDE);` – Bad Loser Oct 16 '20 at 08:18
21

I was struggling with this too but then I accidentaly used 'collapseActionView' and that fixed it! My menu.xml looks like this now:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_search"
      android:title="@string/menu_search"
      android:showAsAction="always|withText|collapseActionView"
      android:actionViewClass="android.widget.SearchView"
      android:icon="@android:drawable/ic_menu_search" /> 
</menu>

The downside of this is that on tablets the SearchView will appear on the left side of the ActionBar instead of where the searchicon is, but I don't mind that.

Mathieu Post
  • 416
  • 4
  • 10
  • 2
    I tried this and it really doesn't work. After expanding the searchview, pressing the back button does not properly dismiss it. Instead the old icon appears on the left side of the actionbar. It takes a second press of the backbutton to properly hide it. – Kyle Ivey Oct 25 '13 at 02:59
  • It works for me on my test device (SM-G925V) but I'm not sure how many other devices will have strange behaviors like Kyle Ivey had. I do have the same behavior that Mathieu Post said about the search being on the left instead of the right but that's likely because I do not know the collapseActionView's intended purpose. – Sherlock Jul 04 '17 at 19:20
15

I defined a style to do it .

here is my xml:

  <android.support.v7.widget.SearchView
        android:id="@+id/sv_search"
        android:icon="@android:drawable/ic_menu_search"
        android:layout_width="fill_parent"
        **style="@style/CitySearchView"**
        android:layout_height="wrap_content"/>

and this is my style:

<style name="CitySearchView" parent="Base.Widget.AppCompat.SearchView">
    <item name="searchIcon">@drawable/ic_more_search</item>
</style>

That it!

After finish that,just take a look at Base.Widget.AppCompat.SearchView.

<style name="Base.Widget.AppCompat.SearchView" parent="android:Widget">
    <item name="layout">@layout/abc_search_view</item>
    <item name="queryBackground">@drawable/abc_textfield_search_material</item>
    <item name="submitBackground">@drawable/abc_textfield_search_material</item>
    <item name="closeIcon">@drawable/abc_ic_clear_mtrl_alpha</item>
    <item name="searchIcon">@drawable/abc_ic_search_api_mtrl_alpha</item>
    <item name="goIcon">@drawable/abc_ic_go_search_api_mtrl_alpha</item>
    <item name="voiceIcon">@drawable/abc_ic_voice_search_api_mtrl_alpha</item>
    <item name="commitIcon">@drawable/abc_ic_commit_search_api_mtrl_alpha</item>
    <item name="suggestionRowLayout">@layout/abc_search_dropdown_item_icons_2line</item>
</style>

every item can be override by define a new style .

Hope it helps!

woodyhou
  • 151
  • 1
  • 3
14

There's a way to do this. The trick is to recover the ImageView using its identifier and setting a new image with setImageResource(). This solution is inspired on Changing the background drawable of the searchview widget.

private SearchView searchbox;

private void customizeSearchbox() {
    setSearchHintIcon(R.drawable.new_search_icon);
}

private void setSearchHintIcon(int resourceId) {
    ImageView searchHintIcon = (ImageView) findViewById(searchbox, 
        "android:id/search_mag_icon");
    searchHintIcon.setImageResource(resourceId);
}

private View findViewById(View v, String id) {
    return v.findViewById(v.getContext().getResources().
        getIdentifier(id, null, null));        
}
Community
  • 1
  • 1
Diego Pino
  • 11,278
  • 1
  • 55
  • 57
6

SearchView searchView = (SearchView) findViewById(R.id.address_search);

    try {
        Field searchField = SearchView.class
                .getDeclaredField("mSearchButton");
        searchField.setAccessible(true);
        ImageView searchBtn = (ImageView) searchField.get(searchView);
        searchBtn.setImageResource(R.drawable.search_glass);
    } catch (NoSuchFieldException e) {
    } catch (IllegalAccessException e) {
    }
SumeetP
  • 109
  • 1
  • 2
5

After some research I found the solution here. The trick is that the icon is not in an ImageView but in the Spannable object.

// Accessing the SearchAutoComplete
int queryTextViewId = getResources().getIdentifier("android:id/search_src_text", null, null);  
View autoComplete = searchView.findViewById(queryTextViewId);

Class<?> clazz = Class.forName("android.widget.SearchView$SearchAutoComplete");

SpannableStringBuilder stopHint = new SpannableStringBuilder("   ");  
stopHint.append(getString(R.string.your_new_text));

// Add the icon as an spannable
Drawable searchIcon = getResources().getDrawable(R.drawable.ic_action_search);  
Method textSizeMethod = clazz.getMethod("getTextSize");  
Float rawTextSize = (Float)textSizeMethod.invoke(autoComplete);  
int textSize = (int) (rawTextSize * 1.25);  
searchIcon.setBounds(0, 0, textSize, textSize);  
stopHint.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

// Set the new hint text
Method setHintMethod = clazz.getMethod("setHint", CharSequence.class);  
setHintMethod.invoke(autoComplete, stopHint);
znat
  • 13,144
  • 17
  • 71
  • 106
5

In menu xml:

<item
    android:id="@+id/menu_filter"
    android:actionLayout="@layout/menu_filter"
    android:icon="@drawable/ic_menu_filter"
    android:orderInCategory="10"
    android:showAsAction="always"
    android:title="@string/menu_filter"/>

and create the layout/menu_filter:

<?xml version="1.0" encoding="utf-8"?>
<SearchView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:searchIcon="@drawable/ic_menu_filter"/>

then in activity's onCreateOptionsMenu or onPrepareOptionsMenu:

SearchView searchView = (SearchView) menu.findItem(R.id.menu_filter).getActionView();
searchView.setOnQueryTextListener(this);
Pnemonic
  • 1,685
  • 17
  • 17
  • 2
    Btw, use `app:actionLayout` instead of `android:actionLayout` if you want to call `getActionView`. – algrid Mar 05 '19 at 15:13
3

It looks like the actionViewClass overides the icon and it doesn't look like you can change it from this class.

You got two solutions:

Community
  • 1
  • 1
Snicolas
  • 37,840
  • 15
  • 114
  • 173
  • Yeah... you are right... actually my search icon was very similar to the default widget icon, but more similar with the rest of icons used in the app. – Valdemar May 04 '12 at 09:33
3
<SearchView
            android:searchIcon="@drawable/ic_action_search"
..../>

use the searchIcon xml tag

TomTaila
  • 1,694
  • 1
  • 20
  • 33
3

This works with Material Design (MaterialComponents theme) and BottomAppBar.

If you are using androidx library, for example:

 <item
    android:id="@+id/sv"
    android:title="@string/search"
    app:actionViewClass="androidx.appcompat.widget.SearchView"
    app:showAsAction="always" />

You can create a method and invoke it from wherever you want:

  /**
 * Set SearchView Icon
 * @param i Drawable icon
 */
private void setSVIcon(int i) {
    ImageView iv = searchView.findViewById(androidx.appcompat.R.id.search_button);
    iv.setImageDrawable(ResourcesCompat.getDrawable(getResources(), i, null));
}

Usage example:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(m, menu);
    MenuItem mn = menu.findItem(R.id.sv);
    if (mn != null) {
        searchview = (SearchView) mn.getActionView();
        setSVIcon(R.drawable.ic_sr);
    }
}
VasileM
  • 606
  • 7
  • 13
2

Update hint of AutocompleteTextView for updating search icon in the expanded mode, copied from android source,

@Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        mSearchMenuItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) mSearchMenuItem.getActionView();
        int searchImgId = getResources().getIdentifier("android:id/search_button", null, null);
        ImageView v = (ImageView) searchView.findViewById(searchImgId);
        v.setImageResource(R.drawable.search_or);

        int searchTextViewId = searchView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null);
        AutoCompleteTextView searchTextView = (AutoCompleteTextView) searchView.findViewById(searchTextViewId);
        searchTextView.setHintTextColor(getResources().getColor(R.color.hint_color_white));
        searchTextView.setTextColor(getResources().getColor(android.R.color.white));
        searchTextView.setTextSize(18.0f);


        SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
        ssb.append(hintText);
        Drawable searchIcon = getResources().getDrawable(R.drawable.search_or);
        int textSize = (int) (searchTextView.getTextSize() * 1.25);
        searchIcon.setBounds(0, 0, textSize, textSize);
        ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        searchTextView.setHint(ssb);


        return super.onPrepareOptionsMenu(menu);
    }
Pravin Patil
  • 418
  • 5
  • 23
1

From API 21 you can change it in xml:

android:searchIcon="@drawable/loupe"
android:closeIcon="@drawable/x_white"
buxik
  • 2,583
  • 24
  • 31
  • This also works with current compat library below api level 21 (just tested searchIcon). SearchView within Toolbar and AppCompat Library v7:23 – drlue Nov 10 '15 at 12:43
1

for api level < 21, i did this:

        int searchImgId = getResources().getIdentifier("android:id/search_mag_icon", null, null);
    ImageView ivIcon = (ImageView) searchView.findViewById(searchImgId);
    if(ivIcon!=null)
        ivIcon.setImageResource(R.drawable.ic_search);

from this

enter image description here

to this

enter image description here

Razel Soco
  • 349
  • 2
  • 8
1

There are three magnifying glass icons. two of them are shown when IconizedByDefault is true(one which is shown before pressing and one is shown in the "hint") and one is shown all the time when IconizedByDefault is false. all the fields are private so the way to get them is by their xml id. (most of the code is mentioned separately in other answers in this post already)

when IconizedByDefault is true change the icon in the hint (which is seen only after pressing the icon) by :

mSearchSrcTextView = (SearchAutoComplete)findViewById(R.id.search_src_text);

then do the same as in the android source code:

final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
    newSearchIconDrawable.setBounds(0, 0, textSize, textSize);

    final SpannableStringBuilder ssb = new SpannableStringBuilder("   ");
    ssb.setSpan(new ImageSpan(newSearchIconDrawable), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    ssb.append(hintText);

mSearchHintIcon was replaced with newSearchIconDrawable which is your new search icon. Then set the hint with

mSearchSrcTextView.setHint(ssb); 

The other 2 icons are in an ImageView, which can be found by their Id. for the icon when searchview is closed (when iconizedByDefault is true) do:

mSearchButton = (ImageView) findViewById(R.id.search_button);

and for the one that always appears (if iconizedByDefault is false)

mCollapsedIcon = (ImageView) findViewById(R.id.search_mag_icon);
KeLiuyue
  • 8,149
  • 4
  • 25
  • 42
YehudaW
  • 39
  • 4
1

Desperate solution using Kotlin

    val s = (searchView.getAllChildren().firstOrNull() as? LinearLayout)?.getAllChildren()?.filter { it is AppCompatImageView }?.firstOrNull() as? AppCompatImageView
    s?.setImageResource(R.drawable.search)

getAllChildren:

fun ViewGroup.getAllChildren() : ArrayList<View> {
val views = ArrayList<View>()
for (i in 0..(childCount-1)) {
    views.add(getChildAt(i))
}
return views
}

Hope it helps someone.

Shubham Naik
  • 410
  • 1
  • 7
  • 18
0

My solution: Use two menu xml files. In one of the xmls the menu item has an actionView and in the other one no. Initially inflate the collapsed menu and when the menu item is clicked, invalidate the menu and inflate the expanded menu xml and make sure you call setIconified(false);

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
    if(!mShowSearchView)
    {
        inflater.inflate(R.menu.menu_collapsed, menu);
    }
    else
    {
        inflater.inflate(R.menu.menu_expanded, menu);
        MenuItem searchItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
        searchView.setIconified(false);
        searchView.setOnCloseListener(new OnCloseListener()
        {
            @Override
            public boolean onClose()
            {
                mShowSearchView = false;
                ActivityCompat.invalidateOptionsMenu(getActivity());
                return false;
            }
        });
    }
    super.onCreateOptionsMenu(menu, inflater);
}

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
    if (item.getItemId() == R.id.action_filter)
    {
        menu.showMenu();
    }
    else if (item.getItemId() == R.id.action_search)
    {
        mShowSearchView = true;
        ActivityCompat.invalidateOptionsMenu(getActivity());
    }
    return super.onOptionsItemSelected(item);
}
0

Just name your icon the same name as the icon that is used by the search view. When it compiles it takes the resource in the project over the icon in the library.

SjoerdvGestel
  • 391
  • 2
  • 14
-1

I use the AppCompat library. Yes, specifying android:icon="@drawable/search_icon_png" doesnt work. So i looked into the source code of @style/Theme.AppCompat and found the icon that android uses.

<item name="searchViewSearchIcon">@drawable/abc_ic_search</item>

So if you rename your search icon inside your drawables to abc_ic_search.png, this icon is rendered as its found in your app drawable first, rather than the appcompat drawable folder. Works for me :)

Using this approach you can customize the close and clear icons for the search widget as well.

Karan M
  • 97
  • 1
  • 1