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;
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lrdXRhcmlhbg==,size_16,color_FFFFFF,t_70)
我们可以看到SimpleCursorAdapter继承于灵活可拓展的BaseAdapter的子类CursorAdapter;
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-CesiWM7e-1715857592281)]
[外链图片转存中…(img-p8cjWnMz-1715857592282)]
[外链图片转存中…(img-IethyEfU-1715857592283)]
[外链图片转存中…(img-5qReO76o-1715857592284)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!