具体代码如下:
public class EditText_Clear extends android.support.v7.widget.AppCompatEditText {
* 步骤1:定义左侧搜索图标 & 一键删除图标
private Drawable clearDrawable,searchDrawable;
public EditText_Clear(Context context) {
super(context);
init();
public EditText_Clear(Context context, AttributeSet attrs) {
super(context, attrs);
init();
public EditText_Clear(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
* 步骤2:初始化 图标资源
private void init() {
clearDrawable = getResources().getDrawable(R.drawable.delete);
searchDrawable = getResources().getDrawable(R.drawable.search);
setCompoundDrawablesWithIntrinsicBounds(searchDrawable, null,
null, null);
* 步骤3:通过监听复写EditText本身的方法来确定是否显示删除图标
* 监听方法:onTextChanged() & onFocusChanged()
* 调用时刻:当输入框内容变化时 & 焦点发生变化时
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
setClearIconVisible(hasFocus() && text.length() > 0);
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
setClearIconVisible(focused && length() > 0);
* 关注1
* 作用:判断是否显示删除图标
private void setClearIconVisible(boolean visible) {
setCompoundDrawablesWithIntrinsicBounds(searchDrawable, null,
visible ? clearDrawable : null, null);
* 步骤4:对删除图标区域设置点击事件,即"点击 = 清空搜索框内容"
* 原理:当手指抬起的位置在删除图标的区域,即视为点击了删除图标 = 清空搜索框内容
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
Drawable drawable = clearDrawable;
if (drawable != null && event.getX() <= (getWidth() - getPaddingRight())
&& event.getX() >= (getWidth() - getPaddingRight() - drawable.getBounds().width())) {
setText("");
break;
return super.onTouchEvent(event);
对于含有一键清空功能 & 更多自定义样式的EditText自定义控件具体请看我的另外一个简单 & 好用的开源组件:Android自定义EditText:手把手教你做一款含一键删除&自定义样式的SuperEditText
分析2:SearchListView.java
作用:解决 ListView
& ScrollView
的嵌套冲突
具体代码如下:
public class Search_Listview extends ListView {
public Search_Listview(Context context) {
super(context);
public Search_Listview(Context context, AttributeSet attrs) {
super(context, attrs);
public Search_Listview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
复制代码
分析3: search_layout.xml
作用:搜索框的布局
具体代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:focusableInTouchMode="true"
android:orientation="vertical">
<LinearLayout
android:id="@+id/search_block"
android:layout_width="match_parent"
android:layout_height="10dp"
android:orientation="horizontal"
android:paddingRight="10dp"
// 返回按钮
<ImageView
android:layout_width="38dp"
android:layout_height="38dp"
android:layout_gravity="center_vertical"
android:padding="10dp"
android:src="@drawable/back" />
// 搜索框(采用上面写的自定义EditText
<scut.carson_ho.searchview.EditText_Clear
android:id="@+id/et_search"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="264"
android:background="@null"
android:drawablePadding="8dp"
android:gravity="start|center_vertical"
android:imeOptions="actionSearch"
android:singleLine="true"
// 最后2行 = 更换输入键盘按钮:换行 ->>搜索
</LinearLayout>
// 下方搜索记录布局 = ScrollView+Listview
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
// Listview布局(采用上述讲解的SearchListView,解决了与ScrollView的冲突)
<scut.carson_ho.searchview.SearchListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</scut.carson_ho.searchview.SearchListView>
<TextView
android:id="@+id/tv_clear"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#F6F6F6"
android:gravity="center"
android:visibility="invisible"
android:text="清除搜索历史" />
</LinearLayout>
</ScrollView>
</LinearLayout>
复制代码
分析4:ICallBack.java、bCallBack.java
作用:搜索按键、返回按键回调接口
具体代码如下:
* ICallBack.java
public interface ICallBack {
void SearchAciton(String string);
* bCallBack.java
public interface bCallBack {
void BackAciton();
复制代码
分析5:SearchView.java
作用:涵盖搜索框中所有功能,此处主要讲解 关键字搜索 功能实现
具体代码如下:
* 步骤1:初始化成员变量
private
EditText et_search;
private
LinearLayout search_block;
private
ImageView searchBack;
private
ICallBack mCallBack;
private
bCallBack bCallBack;
private
SearchListView listView;
private
BaseAdapter adapter;
* 步骤2:绑定 搜索框 组件
private
void
initView
()
{
LayoutInflater.
from
(context).inflate(R.layout.search_layout,
this
);
et_search = (EditText) findViewById(R.id.et_search);
search_block = (LinearLayout)findViewById(R.id.search_block);
listView = (Search_Listview) findViewById(R.id.listView);
tv_clear = (TextView) findViewById(R.id.tv_clear);
tv_clear.setVisibility(INVISIBLE);
* 步骤3
* 监听输入键盘更换后的搜索按键
* 调用时刻:点击键盘上的搜索键时
et_search.setOnKeyListener(
new
View.OnKeyListener() {
public
boolean
onKey
(
View v,
int
keyCode, KeyEvent
event
)
{
if
(keyCode == KeyEvent.KEYCODE_ENTER &&
event
.getAction() == KeyEvent.ACTION_DOWN) {
if
(!(mCallBack ==
null
)){
mCallBack.SearchAciton(et_search.getText().toString());
Toast.makeText(context,
"需要搜索的是"
+ et_search.getText(), Toast.LENGTH_SHORT).show();
return
false
;
* 步骤4:回调接口
public
interface
ICallBack
{
void
SearchAciton
(
String
string
)
;
public
void
setOnClickBack
(
bCallBack bCallBack
)
{
this
.bCallBack = bCallBack;
6.2 实时显示历史搜索记录
描述:包括 最近搜索记录 & 相似搜索记录
分析1:RccordSQLiteOpenHelper.java
作用:创建、管理数据库 & 版本控制
该数据库用于存储用户的搜索历史记录
具体代码如下:
对于
Android SQLlite
数据库的操作请看文章:
Android:SQLlite数据库操作最详细解析
public
class
RecordSQLiteOpenHelper
extends
SQLiteOpenHelper
{
private
static
String
name
=
"temp.db"
;
private
static
Integer
version
=
1
;
public
RecordSQLiteOpenHelper
(Context context)
{
super
(context, name,
null
, version);
@Override
public
void
onCreate
(SQLiteDatabase db)
{
db.execSQL(
"create table records(id integer primary key autoincrement,name varchar(200))"
);
@Override
public
void
onUpgrade
(SQLiteDatabase db,
int
oldVersion,
int
newVersion)
{
复制代码
分析2:SearchView.java
作用:涵盖搜索框中所有功能,此处主要讲解 实时显示历史搜索记录 功能实现
具体代码如下:
* 步骤1:初始化变量
private
RecordSQLiteOpenHelper helper ;
private
SQLiteDatabase db;
private
SearchListView listView;
listView = (SearchListView)
findViewById
(R.id.listView);
private
BaseAdapter adapter;
helper =
new
RecordSQLiteOpenHelper
(context);
* 步骤2:搜索框的文本变化实时监听
et_search.
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) {
@Override
public
void
afterTextChanged
(Editable s) {
String
tempName = et_search.
getText
().
toString
();
queryData
(tempName);
* 步骤3:搜索记录列表(ListView)监听
* 即当用户点击搜索历史里的字段后,会直接将结果当作搜索字段进行搜索
listView.
setOnItemClickListener
(
new
AdapterView.
OnItemClickListener
() {
@Override
public
void
onItemClick
(AdapterView<?> parent, View view,
int
position,
long
id) {
TextView textView = (TextView) view.
findViewById
(android.R.id.text1);
String
name = textView.
getText
().
toString
();
et_search.
setText
(name);
Toast.
makeText
(context, name, Toast.LENGTH_SHORT).
show
();
* 关注1
* 模糊查询数据 & 显示到ListView列表上
private
void
queryData
(
String
tempName)
{
Cursor cursor = helper.
getReadableDatabase
().
rawQuery
(
"select id as _id,name from records where name like '%"
+ tempName +
"%' order by id desc "
, null);
adapter =
new
SimpleCursorAdapter
(context, android.R.layout.simple_list_item_1, cursor,
new
String
[] {
"name"
},
new
int
[] { android.R.id.text1 }, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listView.
setAdapter
(adapter);
adapter.
notifyDataSetChanged
();
System.out.
println
(cursor.
getCount
());
if
(tempName.
equals
(
""
) && cursor.
getCount
() !=
0
){
tv_clear.
setVisibility
(VISIBLE);
else
{
tv_clear.
setVisibility
(INVISIBLE);
复制代码
6.3 删除历史搜索记录
描述:清空所有历史搜索记录
* 步骤1:初始化变量
private TextView tv_clear;
tv_clear = (TextView)
findViewById
(R.id.tv_clear);
tv_clear
.setVisibility
(INVISIBLE);
* 步骤2:设置"清空搜索历史"按钮
tv_clear
.setOnClickListener
(new View.OnClickListener() {
@Override
public void onClick(View v) {
deleteData
();
queryData
("");
* 关注2:清空数据库
private void
deleteData
() {
db = helper
.getWritableDatabase
();
db
.execSQL
("delete from records");
db
.close
();
tv_clear
.setVisibility
(INVISIBLE);
* 关注3
* 模糊查询数据、显示到ListView列表上 & 确定显示 “删除历史按钮”条件
private void
queryData
(String tempName) {
Cursor
cursor
= helper
.getReadableDatabase
()
.rawQuery
(
"select id as _id,name from records where name like '%" + tempName + "%' order by id desc ", null);
adapter = new
SimpleCursorAdapter
(context, android.R.layout.simple_list_item_1, cursor, new String[] { "name" },
new int[] { android.R.id.text1 }, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listView
.setAdapter
(adapter);
adapter
.notifyDataSetChanged
();
if (tempName.equals("") &&
cursor
.getCount
() !=
0
){
tv_clear
.setVisibility
(VISIBLE);
else {
tv_clear
.setVisibility
(INVISIBLE);
复制代码
6.4 保存历史搜索记录
描述:将用户输入的搜索字段保存到数据库中
* 监听输入键盘更换后的搜索按键
* 调用时刻:点击键盘上的搜索键时
et_search
.setOnKeyListener
(new View.OnKeyListener() {
public boolean
onKey
(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent
.ACTION_DOWN
) {
if (!(mCallBack == null)){
mCallBack
.SearchAciton
(et_search.getText()
.toString
());
Toast
.makeText
(context, "需要搜索的是" + et_search.getText(), Toast
.LENGTH_SHORT
)
.show
();
boolean hasData =
hasData
(et_search.getText()
.toString
()
.trim
());
if (!hasData) {
insertData
(et_search.getText()
.toString
()
.trim
());
queryData
("");
return false;
* 关注3
* 检查数据库中是否已经有该搜索记录
private boolean
hasData
(String tempName) {
Cursor
cursor
= helper
.getReadableDatabase
()
.rawQuery
(
"select id as _id,name from records where name =?", new String[]{tempName});
return
cursor
.moveToNext
();
* 关注4
* 插入数据到数据库,即写入搜索字段到历史搜索记录
private void
insertData
(String tempName) {
db = helper
.getWritableDatabase
();
db
.execSQL
("insert into records(name)
values
('" + tempName + "')");
db
.close
();
至此,关于搜索框的全部源码讲解完毕。
完整源代码请看:
Carson_Ho的Github地址:SearchView
7. 具体使用
具体请看文章:
Android开源库:这里有一个简单好用、含历史搜索记录的搜索框
完整Demo地址:
Carson_Ho的Github地址:SearchView
8. 贡献代码
希望你们能和我一起完善这款简单 & 好用的
SearchView
控件,具体请看:
贡献说明
关于该开源项目的意见 & 建议可在Issue上提出。欢迎
Star
!
9. 总结
相信你一定会喜欢上
这款简单 & 好用的
SearchView
控件
已在
Github
上开源:
SearchView
,欢迎
Star
!
下一篇文章我将继续进行一些有趣的自定义
View
实例讲解,有兴趣可以继续关注
Carson_Ho的安卓开发笔记
更多简单好用的开源库:简单 & 好用的开源组件:
自定义EditText:手把手教你做一款含一键删除&自定义样式的SuperEditText
你也可以自己写一个可爱 & 小资风格的Android加载等待自定义View
请点赞!因为你的鼓励是我写作的最大动力!
欢迎关注carson_ho的微信公众号
-
28.6w
-
MacroZheng
Spring Boot
-
123
-
androidwing
Android
Kotlin