android:id=“@+id/search_back”
android:layout_width=“38dp”
android:layout_height=“38dp”
android:layout_gravity=“center_vertical”
android:padding=“10dp”
android:src=“@drawable/back” />
<edu.fjnu.mysearchview.ModEditText
android:id=“@+id/et_search”
android:layout_width=“0dp”
android:layout_height=“fill_parent”
android:layout_weight=“264”
android:background=“@null”
android:drawableLeft=“@drawable/search”
android:drawablePadding=“8dp”
android:gravity=“start|center_vertical”
android:imeOptions=“actionSearch”
android:singleLine=“true”
/>
<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”>
<edu.fjnu.mysearchview.SearchListView
android:id=“@+id/listView”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”>
</edu.fjnu.mysearchview.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=“清除搜索历史” />
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:orientation=“vertical” android:layout_width=“match_parent”
android:layout_height=“match_parent”>
<ImageView
android:layout_alignParentLeft=“true”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:id=“@+id/ItemImage”/>
<TextView
android:layout_width=“fill_parent”
android:layout_height=“wrap_content”
android:textSize=“20dp”
android:layout_toRightOf=“@id/ItemImage”
android:id=“@+id/ItemTitle”/>
接口
//搜索接口
public interface sCallBack{
void SearchAciton(String string);
}
//返回|退回接口
public interface bCallBack{
void BackAction(String string);
}
SearchView
package edu.fjnu.mysearchview;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
public class SearchView extends LinearLayout {
private Context context;
//初始化搜索框组件
private EditText editTextSearch;
private TextView textView_Clear;
private LinearLayout search_block;
private ImageView searchBack;
//初始化自定义ListView与adapter
private SearchListView listView;
private SimpleCursorAdapter adapter;//SimpleCursorAdapter是个很灵活的适配器,能灵活的将数据库的搜索结果作为数据源加入ListView,详见接下来的内容
//初始化数据库
private DBSqliteOpenHelper helper;
private SQLiteDatabase db;
//初始化搜索接口与返回接口
private sCallBack sCallBack;
private bCallBack bCallBack;
// 搜索字体属性设置:大小、颜色 & 默认提示
private Float textSizeSearch;
private int textColorSearch;
private String textHintSearch;
// 搜索框设置:高度 & 颜色&历史结果图标
private int searchBlockHeight;
private int searchBlockColor;
private int historyimg;
/**
-
构造函数
-
作用:对搜索框进行初始化
*/
public SearchView(Context context) {
super(context);
this.context = context;
initData(context);// ->>初始化数据
init();// ->>初始化设置
}
public SearchView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initAttrs(context, attrs); // ->>初始化自定义属性
initData(context);// ->>初始化数据
init();// ->>初始化设置
}
public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initAttrs(context, attrs); // ->>初始化自定义属性
initData(context);// ->>初始化数据
init();// ->>初始化设置
}
/**
- 作用:初始化自定义属性
*/
private void initAttrs(Context context, AttributeSet attrs) {
// 控件资源名称
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Search_View);
// 搜索框字体大小(dp)
textSizeSearch = typedArray.getDimension(R.styleable.Search_View_textSizeSearch, 20);
// 搜索框字体颜色
int defaultColor = context.getResources().getColor(R.color.colorText); // 默认颜色 = 灰色
textColorSearch = typedArray.getColor(R.styleable.Search_View_textColorSearch, defaultColor);
// 搜索框提示内容(String)
textHintSearch = typedArray.getString(R.styleable.Search_View_textHintSearch);
// 搜索框高度
searchBlockHeight = typedArray.getInteger(R.styleable.Search_View_searchBlockHeight, 150);
// 搜索框颜色
int defaultColor2 = context.getResources().getColor(R.color.colorDefault);
searchBlockColor = typedArray.getColor(R.styleable.Search_View_searchBlockColor, defaultColor2);
//设置历史记录图标
historyimg = R.mipmap.history_32px;
// 释放资源
typedArray.recycle();
}
/**
- 初始化数据库数据
*/
public void initData(Context context) {
helper = new DBSqliteOpenHelper(context);
db = helper.getWritableDatabase();
String sql=“insert into records(name,photo_id) values(?,?)”;
db.execSQL(sql,new Object[]{“C语言程序设计”,historyimg});
db.execSQL(sql,new Object[]{“算法第四版”,historyimg});
db.execSQL(sql,new Object[]{“Android实战手册”,historyimg});
db.execSQL(sql,new Object[]{“Java程序与设计”,historyimg});
db.execSQL(sql,new Object[]{“数据结构与算法”,historyimg});
db.close();
}
/**
-
作用:初始化搜索框
*/
private void init() {
// 1. 初始化UI组件
initView();
// 2. 实例化数据库对象
helper = new DBSqliteOpenHelper(context);
// 3. 第1次进入时查询所有的历史搜索记录
queryData(“”);
/**
- "清空搜索历史"按钮
*/
textView_Clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 清空数据库
deleteData();
// 模糊搜索空字符 = 显示当前所有的搜索历史(此时是没有搜索记录的)
queryData(“”);
}
});
/**
-
监听输入键盘更换后的搜索按键
-
调用时刻:点击键盘上的搜索键时
*/
editTextSearch.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
// 1. 点击搜索按键后,根据输入的搜索字段进行查询
if (!(sCallBack == null)) {
sCallBack.SearchAction(editTextSearch.getText().toString());
}
Toast.makeText(context, “你搜索的是” + editTextSearch.getText(), Toast.LENGTH_SHORT).show();
// 2. 点击搜索键后,对该搜索字段在数据库是否存在进行检查(查询)->> 关注1
boolean hasData = hasData(editTextSearch.getText().toString().trim());
// 3. 若存在,则不保存;若不存在,则将该搜索字段保存(插入)到数据库,并作为历史搜索记录
if (!hasData) {
//获取搜索栏中的文字并去除两侧的符号,作为records的name属性传入insertData方法
insertData(editTextSearch.getText().toString().trim());
queryData(“”);
}
}
return false;
}
});
/**
- 搜索框的文本变化监听
*/
editTextSearch.addTextChangedListener(new TextWatcher() {
private Editable s;
@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 = editTextSearch.getText().toString();
queryData(tempName); // ->>关注1
}
});
/**
-
搜索记录列表(ListView)监听
-
即当用户点击搜索历史里的字段后,会直接将结果当作搜索字段进行搜索
*/
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 获取用户点击列表里的文字,并自动填充到搜索框内
TextView textView = (TextView) view.findViewById(R.id.ItemTitle);
String name = textView.getText().toString();
editTextSearch.setText(name);
Toast.makeText(context, name, Toast.LENGTH_SHORT).show();
}
});
/**
- 点击返回按键后的事件
*/
searchBack.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!(bCallBack == null)) {
bCallBack.BackAction();
}
//根据输入的内容模糊查询item,并跳转到另一个界面
Toast.makeText(context, “返回到上一页”, Toast.LENGTH_SHORT).show();
}
});
}
/**
- 绑定搜索框xml视图
*/
private void initView() {
// 1. 绑定R.layout.search_layout作为搜索框的xml文件
LayoutInflater.from(context).inflate(R.layout.search_layout, this);
// 2. 绑定搜索框EditText
editTextSearch = (EditText) findViewById(R.id.et_search);
editTextSearch.setTextSize(textSizeSearch);
editTextSearch.setTextColor(textColorSearch);
editTextSearch.setHint(textHintSearch);
// 3. 搜索框背景颜色
search_block = (LinearLayout) findViewById(R.id.search_block);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) search_block.getLayoutParams();
params.height = searchBlockHeight;
search_block.setBackgroundColor(searchBlockColor);
search_block.setLayoutParams(params);
// 4. 历史搜索记录 = ListView显示
listView = (SearchListView) findViewById(R.id.listView);
// 5. 删除历史搜索记录 按钮
textView_Clear = (TextView) findViewById(R.id.tv_clear);
textView_Clear.setVisibility(INVISIBLE);
// 6. 返回按键
searchBack = (ImageView) findViewById(R.id.search_back);
}
/**
- 模糊查询数据 & 显示到ListView列表上
*/
private void queryData(String tempName) {
// 1. 模糊搜索
Cursor cursor = helper.getReadableDatabase().rawQuery(
“select id as _id,name,photo_id,deletebtn from records where name like '%” + tempName + "%’ order by id desc ", null);
// 2. 创建adapter适配器对象 & 装入模糊搜索的结果
String[] from = new String[]{“name”, “photo_id”};
int[] to = new int[]{R.id.ItemTitle, R.id.ItemImage};
adapter = new SimpleCursorAdapter(context, R.layout.item, cursor, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
// 3. 设置适配器
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
System.out.println(cursor.getCount());
// 当输入框为空 & 数据库中有搜索记录时,显示 "删除搜索记录"按钮
if (tempName.equals(“”) && cursor.getCount() != 0) {
textView_Clear.setVisibility(VISIBLE);
} else {
textView_Clear.setVisibility(INVISIBLE);
}
}
/**
- 清空数据库
*/
private void deleteData() {
db = helper.getWritableDatabase();
db.execSQL(“delete from records”);
db.close();
textView_Clear.setVisibility(INVISIBLE);
}
/**
-
检查数据库中是否已经有该搜索记录
*/
private boolean hasData(String tempName) {
// 从数据库中Record表里找到name=tempName的id
Cursor cursor = helper.getReadableDatabase().rawQuery(
“select id as _id,name,photo_id from records where name =?”, new String[]{tempName});
// 判断是否有下一项数据
return cursor.moveToNext();
}
/**
- 插入数据到数据库,即写入搜索字段到历史搜索记录
*/
private void insertData(String tempName) {
db = helper.getWritableDatabase();
db.execSQL(“insert into records(name,photo_id) values(?,?)”,new Object[]{tempName,historyimg});
db.close();
}
}
SimpleCursorAdapter
我们可以看到SimpleCursorAdapter继承于灵活可拓展的BaseAdapter的子类CursorAdapter;
最后的最后
对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的
最后,互联网不存在所谓的寒冬,只是你没有努力罢了!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
}
}
SimpleCursorAdapter
我们可以看到SimpleCursorAdapter继承于灵活可拓展的BaseAdapter的子类CursorAdapter;
最后的最后
对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的
[外链图片转存中…(img-Xm5u582V-1715015964655)]
最后,互联网不存在所谓的寒冬,只是你没有努力罢了!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!