更改SearchView小部件的背景可绘制对象。

126
我正在尝试更改 Android 操作栏搜索视图小部件中的可绘制对象。 当前它看起来像这样: enter image description here 但我需要将蓝色背景可绘制对象更改为红色。 我已经尝试了许多方法,除了自己编写搜索小部件之外,似乎没有任何方法可以解决。 请有人指点一下我如何更改这个。

1
如果你还没有查看的话,我相信这篇Stack Overflow帖子可以帮助你实现你想要的内容。 - AggelosK
很抱歉,使用SearchView背景是不可能的,因为它在R.styleable中不可用。下面的答案描述了如何在代码中实现。 - vArDo
你是怎么获得文本“输入电影名称”的? - Zen
你可以像这样在搜索视图中设置提示文本:SearchView.SearchAutoComplete searchAutoComplete = (SearchView.SearchAutoComplete) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text); searchAutoComplete.setHint("提示文本"); - Dominik
我在这篇文章的结尾进行了解释,并提供了图片。 - vu ledang
12个回答

259

介绍

很遗憾,无法使用主题、样式和XML继承来设置SearchView文本字段的样式,就像在ActionBar下拉菜单项的背景中可以做的那样。这是因为selectableItemBackground被列为可设置的样式R.stylable中,而我们感兴趣的searchViewTextField(主题属性)不是。因此,我们不能轻松地从XML资源中访问它(您将收到一个No resource found that matches the given name: attr 'android:searchViewTextField'错误)。

从代码中设置SearchView文本字段背景

因此,正确替换SearchView文本字段的背景的唯一方法是进入其内部,获取基于searchViewTextField设置了背景的视图的访问权限,并设置我们自己的背景。

注意: 下面的解决方案仅依赖于SearchView元素中的id (android:id/search_plate),因此它比遍历子元素更独立于SDK版本(例如使用searchView.getChildAt(0)来获取SearchView内的正确视图),但它并不是绝对可靠的。特别是如果某些制造商决定重新实现SearchView的内部,并且上述id所对应的元素不存在,那么代码就无法工作。

在SDK中,SearchView中文本字段的背景是通过九宫格图案声明的,所以我们将采用相同的方式来处理。您可以在Android git存储库drawable-mdpi目录中找到原始的png图像。我们关注两个图像。一个是当文本字段被选中时的状态(名为textfield_search_selected_holo_light.9.png),另一个是当它没有被选中时的状态(名为textfield_search_default_holo_light.9.png)。

不幸的是,即使您只想自定义聚焦状态,您也需要创建这两个图像的本地副本。这是因为R.drawable中没有textfield_search_default_holo_light。因此,它不能轻松地通过@android:drawable/textfield_search_default_holo_light进行访问,该代码可以在下面显示选择器时使用,而不是引用本地可绘制对象。

注意: 我使用了Holo Light主题作为基础,但您也可以使用Holo Dark。在LightDark主题之间的选定状态9-patches中似乎没有真正的区别。然而,在默认状态的9-patches上存在差异(请参见Light vs Dark)。因此,可能没有必要为选定状态制作本地副本,对于DarkLight两种主题都是如此(假设您想处理这两种主题,并使它们看起来与Holo Theme相同)。只需制作一个本地副本并在选择器drawable中使用即可。

现在,您需要根据自己的需求编辑下载的九宫格图案(例如将蓝色更改为红色)。您可以使用draw 9-patch tool查看文件,在编辑后是否正确定义。
我使用GIMP的一像素铅笔工具(非常容易)编辑文件,但您可能会使用自己的工具。这是我自定义的聚焦状态九宫格图案:

enter image description here

注意: 为了简单起见,我仅使用了mdpi密度下的图像。 如果您想在任何设备上获得最佳结果,则需要为多个屏幕密度创建9-patches。 Holo SearchView的图像可以在mdpihdpixhdpi可绘制文件中找到。

现在,我们需要创建一个可绘制选择器,以便根据视图状态显示正确的图像。 创建文件res/drawable/texfield_searchview_holo_light.xml并添加以下内容:

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true"
        android:drawable="@drawable/textfield_search_selected_holo_light" />
    <item android:drawable="@drawable/textfield_search_default_holo_light" />
</selector>

我们将使用上面创建的drawable来设置SearchView中包含文本字段的LinearLayout视图的背景 - 它的id是android:id/search_plate。因此,在创建选项菜单时,以下是如何快速完成此操作的代码:
public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }       

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

        // Getting SearchView from XML layout by id defined there - my_search_view in this case
        SearchView searchView = (SearchView) menu.findItem(R.id.my_search_view).getActionView();
        // Getting id for 'search_plate' - the id is part of generate R file,
        // so we have to get id on runtime.
        int searchPlateId = searchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null);
        // Getting the 'search_plate' LinearLayout.
        View searchPlate = searchView.findViewById(searchPlateId);
        // Setting background of 'search_plate' to earlier defined drawable.            
        searchPlate.setBackgroundResource(R.drawable.textfield_searchview_holo_light);          

        return super.onCreateOptionsMenu(menu);
    }

}

最终效果

这是最终结果的截图:

enter image description here

我是如何做到这一点的

我认为值得提及的是我如何做到这一点,以便在定制其他视图时可以使用这种方法。

查看视图布局

我已经检查了SearchView布局的外观。在SearchView构造函数中,可以找到一个充气布局的行:

inflater.inflate(R.layout.search_view, this, true);

现在我们知道SearchView的布局在名为res/layout/search_view.xml的文件中。查看search_view.xml,我们可以找到一个内部的LinearLayout元素(带有id search_plate),其中包含android.widget.SearchView$SearchAutoComplete(看起来像我们的搜索视图文本字段):
    <LinearLayout
        android:id="@+id/search_plate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_gravity="center_vertical"
        android:orientation="horizontal"
        android:background="?android:attr/searchViewTextField">

现在,我们知道背景是基于当前主题的searchViewTextField属性设置的。

调查属性(是否容易设置?)

为了检查如何设置searchViewTextField属性,我们调查res/values/themes.xml。在默认的Theme中,有一组与SearchView相关的属性:
<style name="Theme">
    <!-- (...other attributes present here...) -->

    <!-- SearchView attributes -->
    <item name="searchDropdownBackground">@android:drawable/spinner_dropdown_background</item>
    <item name="searchViewTextField">@drawable/textfield_searchview_holo_dark</item>
    <item name="searchViewTextFieldRight">@drawable/textfield_searchview_right_holo_dark</item>
    <item name="searchViewCloseIcon">@android:drawable/ic_clear</item>
    <item name="searchViewSearchIcon">@android:drawable/ic_search</item>
    <item name="searchViewGoIcon">@android:drawable/ic_go</item>
    <item name="searchViewVoiceIcon">@android:drawable/ic_voice_search</item>
    <item name="searchViewEditQuery">@android:drawable/ic_commit_search_api_holo_dark</item>
    <item name="searchViewEditQueryBackground">?attr/selectableItemBackground</item>

我们可以看到,默认主题的值是@drawable/textfield_searchview_holo_dark。对于Theme.Light,该值也在那个文件中设置
现在,如果这个属性可以通过R.styleable访问就太好了,但不幸的是它无法访问。相比之下,查看其他出现在themes.xmlR.attr中的主题属性,例如textAppearanceselectableItemBackground。如果searchViewTextField存在于R.attr(和R.stylable)中,我们可以在XML中为整个应用程序定义主题时使用我们的可绘制选择器。例如:
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <style name="AppTheme" parent="android:Theme.Light">
        <item name="android:searchViewTextField">@drawable/textfield_searchview_holo_light</item>
    </style>
</resources>

需要修改什么?

现在我们知道,我们将通过代码访问search_plate。然而,我们仍然不知道它应该长什么样子。简而言之,我们搜索默认主题中用作值的可绘制对象:textfield_searchview_holo_dark.xmltextfield_searchview_holo_light.xml。查看内容后,我们发现可绘制对象是一个selector,它引用了两个其他可绘制对象(稍后会变成9-patch),这些对象基于视图状态。你可以在androiddrawables.com上找到几乎所有版本的Android聚合的9-patch可绘制对象。

自定义

我们识别九宫格之一中的蓝线,因此我们创建它的本地副本并按需更改颜色。

我们如何更改放大镜图标?我尝试了所有方法,但是我无法更改它...谢谢...还有,在Sherlock Action Bar中如何实现这一点? - Jovan
嗨,回答得非常好!我在紧凑的 ActionBar 横向模式下使用该解决方案时遇到了一些麻烦。如果您能在此链接中给我一些建议,那就太好了:https://dev59.com/EHPYa4cB1Zd3GeqPkIke。谢谢! - Chris
我刚刚在Gimp中打开了九宫格图片,并发现下划线下面还有额外的空白区域。我剪切了这个区域,现在下划线在ActionBar中显得更低了。这个九宫格是使用这个工具http://android-holo-colors.com/生成的。 - Etienne Lawlor
非常好的答案,解释得非常清楚,点赞+1。对于任何遇到与我一样的小问题并在应用您的解决方案后仍然存在的人,这里是解决它的提示: 当使用语音搜索时,EditText 的右侧仍然以标准 Holo 蓝色着色。LinearLayout 的 ID 是“submit_area”,用于该部分的 9-patch-images 称为“textfield_search_right_selected_holo_light.9.png”等。因此,有了这些信息,人们也可以将您的解决方案用于此。 - chuky
很好的解释,伙计 :) 它解决了我隐藏搜索视图与折叠菜单项的问题。 - Maveňツ
显示剩余14条评论

33

如果您使用的是appcompat库,则以上解决方案可能无效。您可能需要修改代码,以使其适用于appcompat库。

以下是适用于appcompat库的可行方案。

  @Override
  public boolean onCreateOptionsMenu(Menu menu)
  {

    getMenuInflater().inflate(R.menu.main_menu, menu); 

    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    MenuItem searchMenuItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));

    SearchView.SearchAutoComplete searchAutoComplete = (SearchView.SearchAutoComplete)searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
    searchAutoComplete.setHintTextColor(Color.WHITE);
    searchAutoComplete.setTextColor(Color.WHITE);

    View searchplate = (View)searchView.findViewById(android.support.v7.appcompat.R.id.search_plate);
    searchplate.setBackgroundResource(R.drawable.texfield_searchview_holo_light);

    ImageView searchCloseIcon = (ImageView)searchView.findViewById(android.support.v7.appcompat.R.id.search_close_btn);
    searchCloseIcon.setImageResource(R.drawable.abc_ic_clear_normal);

    ImageView voiceIcon = (ImageView)searchView.findViewById(android.support.v7.appcompat.R.id.search_voice_btn);
    voiceIcon.setImageResource(R.drawable.abc_ic_voice_search);

    ImageView searchIcon = (ImageView)searchView.findViewById(android.support.v7.appcompat.R.id.search_mag_icon);
    searchIcon.setImageResource(R.drawable.abc_ic_search);


    return super.onCreateOptionsMenu(menu);
}

1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Lavanya
1
搜索框对我来说不起作用(它会执行某些操作,但我看不到底部行的任何变化),但AppCompat的一般思路非常有帮助。 - guy_m

14

你的 onCreateOptionsMenu 方法必须是:

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.option, menu);
        SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
        int linlayId = getResources().getIdentifier("android:id/search_plate", null, null);
        ViewGroup v = (ViewGroup) searchView.findViewById(linlayId);
        v.setBackgroundResource(R.drawable.searchviewredversion);
        return super.onCreateOptionsMenu(menu);
    }

搜索项名称为menu_search

以下是searchviewredversion的图片(这是xhdpi版本):http://img836.imageshack.us/img836/5964/searchviewredversion.png

输入图像描述


有没有办法以类似的方式更改搜索图标和关闭按钮? - Harsha M V
我不知道SherlockActionBar 4.2的情况,但是你可以用同样的方式更改搜索图标和关闭按钮! - VinceFR
ActionbarSherlock有自己的主题并定义了自己的SearchView。这意味着您只需向主题添加一个名为searchViewTextField的项目,并使用上面的可绘制定义和9个插图即可。 - NKijak
1
不要忘记将文件重命名为searchviewredversion.9.png,以指示将资产导出为9-patch。 - 1owk3y

13

以上的解决方案不适用于ActionBarSherlock 4.2,因此无法向后兼容到Android 2.x。以下是可行的代码,它可以在ActionBarSherlock 4.2上设置SearchView背景和提示文本:

public static void styleSearchView(SearchView searchView, Context context) {
    View searchPlate = searchView.findViewById(R.id.abs__search_plate);
    searchPlate.setBackgroundResource(R.drawable.your_custom_drawable);
    AutoCompleteTextView searchText = (AutoCompleteTextView) searchView.findViewById(R.id.abs__search_src_text);
    searchText.setHintTextColor(context.getResources().getColor(R.color.your_custom_color));
}

我应该把这段代码放在哪里?我正在尝试在ActionBar的4.2版本中完成此操作。我的问题链接:http://stackoverflow.com/questions/13992424/android-sherlock-actionbar-search-view-styling - Harsha M V
嗨,在onCreateOptionsMenu方法中调用它。找到searchView并将其传递给此方法。 - David Vávra
谢谢。有什么办法可以删除这个内联的Mag符号,就像这里显示的一样http://i.imgur.com/YvBz2.png - Harsha M V
3
使用 ABS 你将会扩展 Theme.Sherlock 或 Theme.Sherlock.Light,而不是 Android 的主题。这意味着你只需要在应用的主题定义中添加一个名为 searchViewTextField 的项目即可。所有操作都在 XML 中完成,无需编写代码。 - NKijak

5

我也尝试过这样做,我正在使用v7。 当我尝试通过getIdentifier()获取searchPlate时,应用程序崩溃了,所以我是这样做的:

View searchPlate = searchView.findViewById(android.support.v7.appcompat.R.id.search_plate);

4

更新

如果您正在使用AndroidX,则可以像这样实现

    View searchPlate = svSearch. findViewById(androidx.appcompat.R.id.search_plate);
    if (searchPlate != null) {
        AutoCompleteTextView searchText = searchPlate.findViewById(androidx.appcompat.R.id.search_src_text);
        if (searchText != null){
            searchText.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.edittext_text_size));
            searchText.setMaxLines(1);
            searchText.setSingleLine(true);
            searchText.setTextColor(getResources().getColor(R.color.etTextColor));
            searchText.setHintTextColor(getResources().getColor(R.color.etHintColor));
            searchText.setBackground(null);
        }
    }

3

我也曾遇到相同的问题。我使用了appcompat v7库并为其定义了自定义样式。 在drawable文件夹中放置bottom_border.xml文件,它看起来像这样:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
  <shape >
      <solid android:color="@color/blue_color" />
  </shape>
</item>
<item android:bottom="0.8dp"
  android:left="0.8dp"
  android:right="0.8dp">
  <shape >
      <solid android:color="@color/background_color" />
  </shape>
</item>

<!-- draw another block to cut-off the left and right bars -->
<item android:bottom="2.0dp">
  <shape >
      <solid android:color="@color/main_accent" />
  </shape>
 </item>
</layer-list>

在values文件夹下的styles_myactionbartheme.xml中:
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="AppnewTheme" parent="Theme.AppCompat.Light">
    <item name="android:windowBackground">@color/background</item>
    <item name="android:actionBarStyle">@style/ActionBar</item>
    <item name="android:actionBarWidgetTheme">@style/ActionBarWidget</item>
</style> 
<!-- Actionbar Theme -->
<style name="ActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse">
    <item name="android:background">@color/main_accent</item>
    <!-- <item name="android:icon">@drawable/abc_ic_ab_back_holo_light</item> -->
</style> 
<style name="ActionBarWidget" parent="Theme.AppCompat.Light">
    <!-- SearchView customization-->
     <!-- Changing the small search icon when the view is expanded -->
    <!-- <item name="searchViewSearchIcon">@drawable/ic_action_search</item> -->
     <!-- Changing the cross icon to erase typed text -->
  <!--   <item name="searchViewCloseIcon">@drawable/ic_action_remove</item> -->
     <!-- Styling the background of the text field, i.e. blue bracket -->
    <item name="searchViewTextField">@drawable/bottom_border</item>
     <!-- Styling the text view that displays the typed text query -->
    <item name="searchViewAutoCompleteTextView">@style/AutoCompleteTextView</item>        
</style>

  <style name="AutoCompleteTextView" parent="Widget.AppCompat.Light.AutoCompleteTextView">
    <item name="android:textColor">@color/text_color</item>
  <!--   <item name="android:textCursorDrawable">@null</item> -->
    <!-- <item name="android:textColorHighlight">@color/search_view_selected_text</item> -->
</style>
</resources>

我定义了一个custommenu.xml文件来显示菜单:

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

<item android:id="@+id/search"
      android:title="@string/search_title"
      android:icon="@drawable/search_buttonn" 
     com.example.actionbartheme:showAsAction="ifRoom|collapseActionView"   
                 com.example.actionbartheme:actionViewClass="android.support.v7.widget.SearchView"/>

你的活动应该继承ActionBarActivity而不是Activity。

@Override
public boolean onCreateOptionsMenu(Menu menu) 
{
    // Inflate the menu; this adds items to the action bar if it is present.
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.custommenu, menu);
}

在清单文件中:
<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppnewTheme" >

了解更多信息请点击这里:
这里是http://www.jayway.com/2014/06/02/android-theming-the-actionbar/


请查看以下链接获取另一个LayerList示例:https://dev59.com/d2sy5IYBdhLWcg3w8Slc - Jared Burrows

2

首先,让我们创建一个名为search_widget_background.xml的XML文件,用作drawable,即在drawable目录下使用。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid android:color="@color/red" />

</shape>

这个可绘制对象将用作搜索小部件的背景。我将颜色设置为红色,因为这是您要求的,但您可以将其设置为由@color标签定义的任何颜色。您甚至可以使用shape标签定义的属性对其进行进一步修改(使圆角,做椭圆形背景等)。
下一步是将我们的搜索小部件的背景设置为此对象。可以通过以下操作完成:
    public boolean onCreateOptionsMenu(Menu menu) {

        SearchView searchView = (SearchView)menu.findItem(R.id.my_search_view).getActionView();
        Drawable d = getResources().getDrawable(R.drawable.search_widget_background);
        searchView.setBackground(d);
...
    }

@shkschneider 我个人认为这不会起作用,因为背景必须设置在SearchView内部的一个元素(名为search_plate)上,而不是SearchView本身。请参阅我的答案以获取更多详细信息。但是,我必须承认,我还没有尝试使用@(baba tenor)的答案。 - vArDo
没有注意到SearchView在ActionBar中。现在应该可以工作了。 - Erol
@babatenor 这样做不起作用,因为SearchView本身的背景总是在SearchView内部的元素背景下面。SearchView内部有一个LinearLayout视图,其ID为“search_plate”,它将此蓝色下划线元素设置为背景。请参见我的答案以获取详细信息。因此,使用您的解决方案会导致整个部件具有蓝色背景,并仍然可见蓝色下划线。我已在* Jelly Bean *上进行了测试,但我认为SDK目标版本在这里并不重要。 - vArDo
谢谢vArDo。我知道SearchView的分层结构,但我只是假设这会起作用(至少在我看来很合理)。 - Erol
1
@babatenor 如果它能正常工作,那就太好了,这样所有的黑客技巧都是不必要的 :) - vArDo

1
public boolean onCreateOptionsMenu(Menu menu) {
........

        // Set the search plate color
        int linlayId = getResources().getIdentifier("android:id/search_plate", null, null);
        View view = searchView.findViewById(linlayId);
        Drawable drawColor = getResources().getDrawable(R.drawable.searchcolor);
        view.setBackground( drawColor );
........
    }

这是可搜索的 searchablecolor.xml 文件。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
  <item>
      <shape >
          <solid android:color="#ffffff" />
      </shape>
  </item>

  <!-- main color -->
  <item android:bottom="1.5dp"
      android:left="1.5dp"
      android:right="1.5dp">
      <shape >
          <solid android:color="#2c4d8e" />
      </shape>
  </item>

  <!-- draw another block to cut-off the left and right bars -->
  <item android:bottom="18.0dp">
      <shape >
          <solid android:color="#2c4d8e" />
      </shape>
  </item>
</layer-list>

1

我搜索了很多关于编程的内容,最终找到了这个解决方案并且它对我有效!
你可以使用这个方法

如果你使用appcompat,请尝试这个:

ImageView searchIconView = (ImageView) searchView.findViewById(android.support.v7.appcompat.R.id.search_button);
searchIconView.setImageResource(R.drawable.yourIcon);

如果您使用androidx,请尝试以下操作:
ImageView searchIconView = (ImageView) searchView.findViewById(androidx.appcompat.R.id.search_button);
    searchIconView.setImageResource(R.drawable.yourIcon);

它将更改默认的搜索视图图标。
希望能有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接