模仿风行App实现的一个自定义搜索框类库,实现了监听文本框输入的变化,清空文本框内容,提示列表,热搜列表、自动保存搜索记录等功能。
实现效果如下:
类库的主要代码如下:
分别是
下面将主要代码粘出来,文档最后会附上下载地址
//继承LinearLayout,实现构造方法
public SearchView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
LayoutInflater.from(context).inflate(R.layout.search_view, this);
initView();
}
搜索的历史纪录用sp保存
/**
* 用SP存储保存搜索的历史纪录
*/
if (sp == null) {
sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
}
外部可通过keepSearchHistory(true/false)方法设置是否保存搜索记录
/**
* 设置是否自动保存搜索记录
*
* @param isAutoKeep
*/
public void keepSearchHistory(boolean isAutoKeep) {
this.isAutoKeep = isAutoKeep;
//如果没有设为自动保存,则返回
if (!isAutoKeep) {
return;
}
String history_search_datas = sp.getString("history_search_datas", "");
if (!TextUtils.isEmpty(history_search_datas)) {//不为空
historySearchDatas = new Gson().fromJson(history_search_datas, List.class);
} else {//SP中没有数据,直接返回
return;
}
if (historyAdapter == null && isAutoKeep) {
ll_search_history.setVisibility(VISIBLE);
historyAdapter = new CommonAdapter(context, historySearchDatas);
gv_search_history.setAdapter(historyAdapter);
}
}
外部可设置历史纪录的最大保存个数,默认为6
private int maxHistoryRecordCount = 6;
/**
* 设置最大的历史纪录数,默认为6条
*
* @param maxHistoryRecordCount
*/
public void setMaxHistoryRecordCount(int maxHistoryRecordCount) {
this.maxHistoryRecordCount = maxHistoryRecordCount;
}
在执行搜索时保存记录,最新搜索的记录放在首位,如果该条记录已经有包含在列表中,则移除原来位置的记录并重新保存在首位,当超过maxHistoryRecordCount 的值时,删除最先保存的历史纪录
/**
* 保存历史纪录
*
* @param text
*/
private void keepSearchRecord(String text) {
if (historySearchDatas == null) {
historySearchDatas = new ArrayList<>(maxHistoryRecordCount);
historySearchDatas.add(0, text);
} else {
for (int i = 0; i < historySearchDatas.size(); i++) {
if ((historySearchDatas.get(i).equals(text))) {//判断数据是否已在历史记录中
//如果存在就移除
historySearchDatas.remove(i);
}
}
//超过最大值
if (historySearchDatas.size() >= maxHistoryRecordCount) {
//移除最老的一条记录
historySearchDatas.remove(maxHistoryRecordCount - 1);
}
//新添加的记录放在集合的首位
historySearchDatas.add(0, text);
//保存搜索的历史记录到sp中
sp.edit().putString("history_search_datas", new Gson().toJson(historySearchDatas)).commit();
}
if (historyAdapter == null) {
historyAdapter = new CommonAdapter(context, historySearchDatas);
gv_search_history.setAdapter(historyAdapter);
} else {
historyAdapter.updateRecordList(historySearchDatas);
}
}
当点击“清空”按钮时,删除历史纪录
/**
* 清空历史纪录
*/
private void clearHistoryRecord() {
if (historySearchDatas != null && historyAdapter != null) {
historySearchDatas.clear();//清空列表
historyAdapter.updateRecordList(historySearchDatas);
ll_search_history.setVisibility(GONE);
//删除SP存储的历史纪录
sp.edit().putString("history_search_datas", "").commit();
}
}
外部通过setHotSearchDatas(List hotSearchDatas)传入热门搜索的数据,如不调用此方法,则不显示热搜列表
/**
* 设置热搜的数据集合
*
* @param hotSearchDatas
*/
public void setHotSearchDatas(List<String> hotSearchDatas) {
this.hotSearchDatas = hotSearchDatas;
if (hotSearchDatas != null && hotSearchDatas.size() > 0) {
ll_search_hot.setVisibility(VISIBLE);
hotAdapter = new CommonAdapter(context, hotSearchDatas);
gv_search_hot.setNumColumns(hotNumColumns);//设置列数
gv_search_hot.setAdapter(hotAdapter);//设置热搜数据
} else {
ll_search_hot.setVisibility(GONE);
}
}
设置提示列表的最大显示行数和热搜列表的列表
private int maxHintLines = -1;
/**
* 设置提示的最大显示行数,默认显示所有
*
* @param maxHintLines
*/
public void setMaxHintLines(int maxHintLines) {
this.maxHintLines = maxHintLines;
}
private int hotNumColumns = 2;
/**
* 设置热搜数据的显示列数,默认为2列
* 需要在setHotSearchDatas()前设置
*
* @param hotNumColumns
*/
public void setHotNumColumns(int hotNumColumns) {
this.hotNumColumns = hotNumColumns;
}
核心方法,添加EditText文本的改变监听,在文本改变后调用自定义的监听的回调方法,在外部实现刷新提示列表的操作
//刷新提示列表的数据
if (onSearchListener != null) {
onSearchListener.onRefreshHintList(s.toString());
}
et_search_content.addTextChangedListener(new SearchTextChangedListener());
/**
* 监听EditText文本的改变
*/
class SearchTextChangedListener implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
/**
* 当文本改变时调用
*
* @param s
* @param start
* @param before
* @param count
*/
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!TextUtils.isEmpty(s.toString().trim())) {//搜索框不为空
ll_search_hot.setVisibility(GONE);
ll_search_history.setVisibility(GONE);
iv_search_clear.setVisibility(VISIBLE);//显示清除搜索框内容的按钮
lv_search_hint.setVisibility(VISIBLE);
} else {
iv_search_clear.setVisibility(GONE);
lv_search_hint.setVisibility(GONE);
if (hotSearchDatas != null && hotSearchDatas.size() > 0) {
ll_search_hot.setVisibility(VISIBLE);
}
if (historyAdapter != null && historySearchDatas.size() > 0 && isAutoKeep) {
ll_search_history.setVisibility(VISIBLE);
}
}
//刷新提示列表的数据
if (onSearchListener != null) {
onSearchListener.onRefreshHintList(s.toString());
}
}
@Override
public void afterTextChanged(Editable s) {
}
}
设置自定义监听
/**
* 自定义searchview的监听
*/
public interface OnSearchListener {
/**
* 当进行搜索时回调此方法
*
* @param searchText 进行搜索的文本
*/
void onSearch(String searchText);
/**
* 当输入框文本变化,需刷新提示的ListView时调用
*
* @param changedText 改变后的文本
*/
void onRefreshHintList(String changedText);
}
private OnSearchListener onSearchListener;
public void setOnSearchListener(OnSearchListener onSearchListener) {
this.onSearchListener = onSearchListener;
}
设置返回按钮、搜索按钮、清空搜索框按钮、提示列表Item、热搜列表Item、历史列表Item的点击监听
iv_search_back.setOnClickListener(new SearchOnClickListener());
iv_search_clear.setOnClickListener(new SearchOnClickListener());
tv_search_search.setOnClickListener(new SearchOnClickListener());
/**
* 热搜列表的Item点击监听
*/
gv_search_hot.setOnItemClickListener(new listItemOnClickListener(HOT_ITEM));
/**
* 历史纪录的Item点击监听
*/
gv_search_history.setOnItemClickListener(new listItemOnClickListener(HISTORY_ITEM));
/**
* 提示列表的Item点击监听
*/
lv_search_hint.setOnItemClickListener(new listItemOnClickListener(HINT_ITEM));
/**
* 监听软键盘的搜索按钮
*/
et_search_content.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
startSearch(et_search_content.getText().toString());
}
return true;
}
});
ll_item_delete.setOnClickListener(new SearchOnClickListener());
点击监听的回调方法
/**
* 点击监听的回调
*/
class SearchOnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.iv_search_back) {
((Activity) context).finish();
} else if (i == R.id.iv_search_clear) {
et_search_content.setText(null);
} else if (i == R.id.tv_search_search) {
startSearch(et_search_content.getText().toString());
} else if (i == R.id.ll_item_delete) {
clearHistoryRecord();
}
}
}
private static final int HOT_ITEM = 1;
private static final int HINT_ITEM = 2;
private static final int HISTORY_ITEM = 3;
//Item的点击回调
class listItemOnClickListener implements AdapterView.OnItemClickListener {
private int tag;
private List<String> datas;
public listItemOnClickListener(int tag) {
this.tag = tag;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (tag == HOT_ITEM) {
datas = hotSearchDatas;
} else if (tag == HINT_ITEM) {
datas = hintDatas;
} else if (tag == HISTORY_ITEM) {
datas = historySearchDatas;
}
String item = datas.get(position);
et_search_content.setText(item);
startSearch(item);
}
}
执行搜索的方法和刷新提示列表和热搜列表的状态
/**
* 开始执行搜索方法
*
* @param text
*/
private void startSearch(String text) {
if (onSearchListener != null) {
onSearchListener.onSearch(text);
refreshListState();
if (isAutoKeep) {
//保存搜索记录
keepSearchRecord(text);
}
}
}
/**
* 刷新热搜和提示列表的状态
*/
private void refreshListState() {
et_search_content.setSelection(et_search_content.getText().length());
lv_search_hint.setVisibility(GONE);
ll_search_hot.setVisibility(GONE);
//隐藏软键盘
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
}
搜索规则:支持拼音的首字母搜索和全拼音搜索(注意:不能从中间开始搜索)
eg:爱情买卖
可以通过 aqmm匹配
也可以通过aiqingmaimai匹配
但是不支持直接从中间的字符匹配,如qing…、qmm
也不支持首字母和全拼音的混合搜索,如aqmaim
- 关联类库
compile project(':searchview_library')
- 在布局文件中定义
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.searchview_library.SearchView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
- 初始化
private SearchView searchView;
searchView = (SearchView) findViewById(R.id.searchView);
- 设置数据
//热搜数据
private List<String> hot_datas;
//提示列表数据
private List<String> hint_datas;
private void initData() {
hot_datas = new ArrayList<>();
hint_datas = new ArrayList<>();
searchALG = new SearchALG(this);
for (int i = 0; i < 10; i++) {
hot_datas.add("Android Hot " + i);
}
//注:若不需要热搜列表,可以不设置
//设置热搜数据显示的列数
searchView.setHotNumColumns(2);
//设置热搜数据
searchView.setHotSearchDatas(hot_datas);
/**
* 设置提示数据的集合
*/
for (int i = 0; i < 10; i++) {
hint_datas.add("ts"+"安卓学习" + "Android Hint " + i + " 你好");
}
/**
* 设置自动保存搜索记录
*/
searchView.keepSearchHistory(true);
//设置提示列表的最大显示列数
searchView.setMaxHintLines(8);
//设置保存搜索记录的个数
searchView.setMaxHistoryRecordCount(6);
}
- 设置监听
private SearchALG searchALG;
private List<String> changedHintDatas;
searchView.setOnSearchListener(new MyOnSearchListener());
/**
* 设置searview的监听
*/
class MyOnSearchListener implements SearchView.OnSearchListener {
/**
* 搜索回调
* @param searchText 进行搜索的文本
*/
@Override
public void onSearch(String searchText) {
if (!TextUtils.isEmpty(searchText)) {
Toast.makeText(MainActivity.this, "完成搜索" + searchText, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "搜索内容不能为空!", Toast.LENGTH_SHORT).show();
}
}
/**
* 刷新提示列表
* @param changedText 改变后的文本
*/
@Override
public void onRefreshHintList(String changedText) {
if (changedHintDatas == null) {
changedHintDatas = new ArrayList<>();
} else {
changedHintDatas.clear();
}
if (TextUtils.isEmpty(changedText)) {
return;
}
for (int i = 0; i < hint_datas.size(); i++) {
String hint_data = hint_datas.get(i);
boolean isAdd = searchALG.isAddToHintList(hint_data, changedText);
if (isAdd) {
changedHintDatas.add(hint_datas.get(i));
}
}
/**
* 根据搜索框文本的变化,动态的改变提示的listView
*/
searchView.updateHintList(changedHintDatas);
}
}
demo下载地址:
http://download.csdn.net/detail/benhuo931115/9480849