简介
不幸的是,您无法像can do with background of items in ActionBar dropdown 那样使用 XML 中的主题、样式和继承来设置 SearchView 文本字段样式。这是因为selectableItemBackground is listed as styleable 在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 中的文本字段的背景是通过nine-patches 声明的,所以我们会这样做。您可以在Android git repository 的drawable-mdpi 目录中找到原始png 图像。我们对两个图像感兴趣。一种用于选择文本字段时的状态(命名为textfield_search_selected_holo_light.9.png),另一种用于未选择文本字段的状态(命名为textfield_search_default_holo_light.9.png)。
不幸的是,即使您只想自定义 focused 状态,您也必须为这两个图像创建本地副本。这是因为textfield_search_default_holo_light 不存在于R.drawable 中。因此,它不容易通过@android:drawable/textfield_search_default_holo_light 访问,可以在下面显示的选择器中使用,而不是引用本地drawable。
注意:我使用的是 Holo Light 主题作为基础,但您可以使用 Holo Dark 做同样的事情。 Light 和 Dark 主题之间的 选定状态 9 补丁似乎没有真正的区别。但是,默认状态的 9 个补丁存在差异(请参阅 Light 与 Dark)。因此,对于 Dark 和 Light 主题,可能不需要为 selected state 制作 9 补丁的本地副本(假设您想要处理两者,并使它们看起来与 Holo 主题 中的相同)。只需制作一个本地副本并在 selector drawable 中为两个主题使用它。
现在,您需要根据需要编辑下载的九个补丁(即将蓝色更改为红色)。您可以使用draw 9-patch tool 查看文件,以检查在您编辑后它是否正确定义。
我使用GIMP 和单像素铅笔工具(非常简单)编辑了文件,但您可能会使用自己的工具。这是我为 focused state 定制的 9 补丁:
注意:为简单起见,我仅使用图像来表示 mdpi 密度。如果您想在任何设备上获得最佳效果,则必须为多种屏幕密度创建 9-patches。 Holo SearchView 的图像可以在 mdpi、hdpi 和 xhdpidrawable 中找到。
现在,我们需要创建可绘制选择器,以便根据视图状态显示正确的图像。创建文件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>
我们将使用上面创建的可绘制对象为 LinearLayout 视图设置背景,该视图在 SearchView 中保存文本字段 - 它的 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);
}
}
最终效果
这是最终结果的截图:
我是怎么做到的
我认为值得一提的是我是如何做到这一点的,以便在自定义其他视图时可以使用这种方法。
查看视图布局
我检查了SearchView 布局的样子。在SearchView contructor 中可以找到一条使布局膨胀的行:
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 值is also set in that file。
现在,如果可以通过R.styleable 访问此属性,那就太好了,但不幸的是,事实并非如此。为了比较,请参阅themes.xml 和R.attr 中都存在的其他主题 属性,例如textAppearance 或selectableItemBackground。如果searchViewTextField 出现在R.attr(和R.stylable)中,我们可以在为整个XML 应用程序定义主题时简单地使用我们的drawable 选择器。例如:
<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.xml 和 textfield_searchview_holo_light.xml。查看内容,我们看到可绘制对象是 selector,它基于视图状态引用了另外两个可绘制对象(稍后会出现 9 个补丁)。您可以在androiddrawables.com上找到来自(几乎)所有 Android 版本的聚合 9-patch drawables
定制
我们识别出one of the 9-patches 中的蓝线,因此我们创建它的本地副本并根据需要更改颜色。