Android手把手教你實(shí)現(xiàn)搜索框
2018-07-20 來(lái)源:編程學(xué)習(xí)網(wǎng)

前言
-
像下圖的搜索功能在Android開(kāi)發(fā)中非常常見(jiàn)
搜索功能
-
今天我將手把手教大家如何實(shí)現(xiàn)具備歷史搜索記錄的搜索框
目錄
1. 使用場(chǎng)景
在敲下代碼前,理解用戶(hù)的功能使用場(chǎng)景是非常重要的,這樣有助于我們更好地去進(jìn)行功能的實(shí)現(xiàn),使用場(chǎng)景如下:
-
用戶(hù)需要進(jìn)行某類(lèi)事物的搜索(通過(guò)文字輸入進(jìn)行精確搜索)
-
在搜索框輸入時(shí),通過(guò)顯示搜索歷史從而降低用戶(hù)二次搜索的成本
簡(jiǎn)單來(lái)說(shuō),就是輸入過(guò)字段會(huì)保存,當(dāng)用戶(hù)再次搜索該字段時(shí),能快速幫助用戶(hù)輸入
2. 功能業(yè)務(wù)流程
3. 明確功能點(diǎn)
-
功能1:關(guān)鍵字搜索
-
功能2:實(shí)時(shí)顯示歷史搜索記錄
-
功能3:歷史搜索記錄保存
-
功能4:將軟鍵盤(pán)上的”回車(chē)按鈕“改為”搜索按鈕“
4. 涉及到的知識(shí)點(diǎn)
4.1 SQLite數(shù)據(jù)庫(kù)的增刪改查操作
-
在數(shù)據(jù)庫(kù)建立一個(gè)叫records的表用于存儲(chǔ)搜索歷史記錄
里面只有一列name來(lái)存儲(chǔ)歷史記錄
db.execSQL("create table records(id integer primary key autoincrement,name varchar(200))");
- 通過(guò)插入和刪除操作數(shù)據(jù)庫(kù)里面的數(shù)據(jù)
-
通過(guò)查詢(xún)操作來(lái)顯示數(shù)據(jù)庫(kù)里面的數(shù)據(jù)到ListView上
已搜索的關(guān)鍵字再次搜索不重復(fù)添加到數(shù)據(jù)庫(kù)
4.2 ListView和ScrollView的嵌套沖突
- 問(wèn)題:ListView和ScrollView一起使用會(huì)有沖突,導(dǎo)致ListView顯示不全
-
解決方案:本人采用繼承ListView并重寫(xiě)它的onMeasure()方法來(lái)解決沖突。
- 至于兩者的滑動(dòng)沖突則暫時(shí)不需要處理(默認(rèn)拉動(dòng)ScrollView),這樣用戶(hù)就會(huì)感覺(jué)里面的搜索歷史項(xiàng)和清空搜索記錄項(xiàng)是在ListView里面一樣,符合設(shè)計(jì)。
- 更多解決方法請(qǐng)看: ListView和ScrollView的嵌套沖突解決方案
-
具體代碼如下:
//解決ListView和ScrollView的沖突 public class Search_Listview extends ListView { public Search_Listview(Context context) { super(context); } //通過(guò)復(fù)寫(xiě)其onMeasure方法、達(dá)到對(duì)ScrollView適配的效果 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
4.3 監(jiān)聽(tīng)軟鍵盤(pán)回車(chē)按鈕設(shè)置為搜索按鈕
給搜索框EditText添加一個(gè)OnKeyListener監(jiān)聽(tīng)器,重寫(xiě)里面的回車(chē)鍵,實(shí)現(xiàn)按下回車(chē)鍵搜索。
et_search.setOnKeyListener(new View.OnKeyListener() {// 輸入完后按鍵盤(pán)上的搜索鍵 // 修改回車(chē)鍵功能 public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) { 寫(xiě)入搜索操作和數(shù)據(jù)庫(kù)插入搜索記錄操作 } });
4.4 使用TextWatcher實(shí)時(shí)篩選
給搜索框EditText添加一個(gè)TextWatcher來(lái)檢測(cè)EditText里面的每個(gè)字符變化,根據(jù)變化之后的內(nèi)容查詢(xún)數(shù)據(jù)庫(kù)得到結(jié)果。
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) { } //輸入有變化調(diào)用該方法 @Override public void afterTextChanged(Editable s) { //設(shè)置查詢(xún)數(shù)據(jù)庫(kù)并顯示在列表的操作 } });
5. 實(shí)例Demo
接下來(lái),我將給出詳細(xì)代碼手把手教你實(shí)現(xiàn)具備歷史搜索記錄的搜索框
先下載Demo再進(jìn)行閱讀效果會(huì)更好: Carson的Search_Layout_Demo地址
5.1 目錄結(jié)構(gòu)
5.2 具體實(shí)現(xiàn)如下:
MainActivity.java
- 作用:顯示搜索框
- 具體代碼如下:
package scut.carson_ho.search_layout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
RccordSQLiteOpenHelper.java
- 作用:繼承自SQLiteOpenHelper數(shù)據(jù)庫(kù)類(lèi)的子類(lèi),用于創(chuàng)建、管理數(shù)據(jù)庫(kù)和版本控制
-
具體代碼如下:
package scut.carson_ho.search_layout; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /** * Created by Carson_Ho on 16/11/15. */ //SQLiteOpenHelper子類(lèi)用于打開(kāi)數(shù)據(jù)庫(kù)并進(jìn)行對(duì)用戶(hù)搜索歷史記錄進(jìn)行增刪減除的操作 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) { //打開(kāi)數(shù)據(jù)庫(kù),建立了一個(gè)叫records的表,里面只有一列name來(lái)存儲(chǔ)歷史記錄: db.execSQL("create table records(id integer primary key autoincrement,name varchar(200))"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
Search_ListView.java
- 作用:用于解決ListView和ScrollView的嵌套沖突
- 具體代碼如下:
package scut.carson_ho.search_layout; import android.content.Context; import android.util.AttributeSet; import android.widget.ListView; /** * Created by Carson_Ho on 16/11/15. */ //解決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); } //通過(guò)復(fù)寫(xiě)其onMeasure方法、達(dá)到對(duì)ScrollView適配的效果 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
Search_View.java
- 作用:用于封裝搜索框功能的所有操作(涵蓋歷史搜索記錄的插入、刪除、查詢(xún)和顯示)
- 具體代碼如下:
package scut.carson_ho.search_layout; import android.content.Context; 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.CursorAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.Toast; /** * Created by Carson_Ho on 16/11/15. */ public class Search_View extends LinearLayout { private Context context; /*UI組件*/ private TextView tv_clear; private EditText et_search; private TextView tv_tip; private ImageView iv_search; /*列表及其適配器*/ private Search_Listview listView; private BaseAdapter adapter; /*數(shù)據(jù)庫(kù)變量*/ private RecordSQLiteOpenHelper helper ; private SQLiteDatabase db; /*三個(gè)構(gòu)造函數(shù)*/ //在構(gòu)造函數(shù)里直接對(duì)搜索框進(jìn)行初始化 - init() public Search_View(Context context) { super(context); this.context = context; init(); } public Search_View(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; init(); } public Search_View(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; init(); } /*初始化搜索框*/ private void init(){ //初始化UI組件 initView(); //實(shí)例化數(shù)據(jù)庫(kù)SQLiteOpenHelper子類(lèi)對(duì)象 helper = new RecordSQLiteOpenHelper(context); // 第一次進(jìn)入時(shí)查詢(xún)所有的歷史記錄 queryData(""); //"清空搜索歷史"按鈕 tv_clear.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //清空數(shù)據(jù)庫(kù) deleteData(); queryData(""); } }); //搜索框的文本變化實(shí)時(shí)監(jiān)聽(tīng) 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) { } //輸入后調(diào)用該方法 @Override public void afterTextChanged(Editable s) { if (s.toString().trim().length() == 0) { //若搜索框?yàn)榭?則模糊搜索空字符,即顯示所有的搜索歷史 tv_tip.setText("搜索歷史"); } else { tv_tip.setText("搜索結(jié)果"); } //每次輸入后都查詢(xún)數(shù)據(jù)庫(kù)并顯示 //根據(jù)輸入的值去模糊查詢(xún)數(shù)據(jù)庫(kù)中有沒(méi)有數(shù)據(jù) String tempName = et_search.getText().toString(); queryData(tempName); } }); // 搜索框的鍵盤(pán)搜索鍵 // 點(diǎn)擊回調(diào) et_search.setOnKeyListener(new View.OnKeyListener() {// 輸入完后按鍵盤(pán)上的搜索鍵 // 修改回車(chē)鍵功能 public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) { // 隱藏鍵盤(pán),這里getCurrentFocus()需要傳入Activity對(duì)象,如果實(shí)際不需要的話(huà)就不用隱藏鍵盤(pán)了,免得傳入Activity對(duì)象,這里就先不實(shí)現(xiàn)了 // ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow( // getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); // 按完搜索鍵后將當(dāng)前查詢(xún)的關(guān)鍵字保存起來(lái),如果該關(guān)鍵字已經(jīng)存在就不執(zhí)行保存 boolean hasData = hasData(et_search.getText().toString().trim()); if (!hasData) { insertData(et_search.getText().toString().trim()); queryData(""); } //根據(jù)輸入的內(nèi)容模糊查詢(xún)商品,并跳轉(zhuǎn)到另一個(gè)界面,這個(gè)需要根據(jù)需求實(shí)現(xiàn) Toast.makeText(context, "點(diǎn)擊搜索", Toast.LENGTH_SHORT).show(); } return false; } }); //列表監(jiān)聽(tīng) //即當(dāng)用戶(hù)點(diǎn)擊搜索歷史里的字段后,會(huì)直接將結(jié)果當(dāng)作搜索字段進(jìn)行搜索 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //獲取到用戶(hù)點(diǎn)擊列表里的文字,并自動(dòng)填充到搜索框內(nèi) 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(); } }); //點(diǎn)擊搜索按鈕后的事件 iv_search.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { boolean hasData = hasData(et_search.getText().toString().trim()); if (!hasData) { insertData(et_search.getText().toString().trim()); //搜索后顯示數(shù)據(jù)庫(kù)里所有搜索歷史是為了測(cè)試 queryData(""); } //根據(jù)輸入的內(nèi)容模糊查詢(xún)商品,并跳轉(zhuǎn)到另一個(gè)界面,這個(gè)根據(jù)需求實(shí)現(xiàn) Toast.makeText(context, "clicked!", Toast.LENGTH_SHORT).show(); } }); } /** * 封裝的函數(shù) */ /*初始化組件*/ private void initView(){ LayoutInflater.from(context).inflate(R.layout.search_layout,this); et_search = (EditText) findViewById(R.id.et_search); tv_clear = (TextView) findViewById(R.id.tv_clear); tv_tip = (TextView) findViewById(R.id.tv_tip); listView = (Search_Listview) findViewById(R.id.listView); iv_search = (ImageView) findViewById(R.id.iv_search); } /*插入數(shù)據(jù)*/ private void insertData(String tempName) { db = helper.getWritableDatabase(); db.execSQL("insert into records(name) values('" + tempName + "')"); db.close(); } /*模糊查詢(xún)數(shù)據(jù) 并顯示在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); // 創(chuàng)建adapter適配器對(duì)象,裝入模糊搜索的結(jié)果 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); // 設(shè)置適配器 listView.setAdapter(adapter); adapter.notifyDataSetChanged(); } /*檢查數(shù)據(jù)庫(kù)中是否已經(jīng)有該條記錄*/ private boolean hasData(String tempName) { //從Record這個(gè)表里找到name=tempName的id Cursor cursor = helper.getReadableDatabase().rawQuery( "select id as _id,name from records where name =?", new String[]{tempName}); //判斷是否有下一個(gè) return cursor.moveToNext(); } /*清空數(shù)據(jù)*/ private void deleteData() { db = helper.getWritableDatabase(); db.execSQL("delete from records"); db.close(); } }
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" android:focusableInTouchMode="true" android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" android:layout_height="50dp" android:background="#E54141" android:orientation="horizontal" android:paddingRight="16dp"> <ImageView android:layout_width="45dp" android:layout_height="45dp" android:layout_gravity="center_vertical" android:padding="10dp" android:src="@drawable/back" /> <EditText 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:hint="輸入查詢(xún)的關(guān)鍵字" android:imeOptions="actionSearch" android:singleLine="true" android:textColor="@android:color/white" android:textSize="16sp" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/search" android:layout_gravity="center_vertical" android:id="@+id/iv_search"/> </LinearLayout> <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"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingLeft="20dp" > <TextView android:id="@+id/tv_tip" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="left|center_vertical" android:text="搜索歷史" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#EEEEEE"/> <scut.carson_ho.search_layout.Search_Listview android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="wrap_content"> </scut.carson_ho.search_layout.Search_Listview> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#EEEEEE"/> <TextView android:id="@+id/tv_clear" android:layout_width="match_parent" android:layout_height="40dp" android:background="#F6F6F6" android:gravity="center" android:text="清除搜索歷史" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginBottom="20dp" android:background="#EEEEEE"/> </LinearLayout> </ScrollView> </LinearLayout>
activity_main.xml
-
作用:顯示搜索框
-
具體代碼如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="scut.carson_ho.search_layout.MainActivity"> <scut.carson_ho.search_layout.Search_View android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/search_layout"/> </RelativeLayout>
5.3 測(cè)試結(jié)果
5.4 Demo地址
6. 總結(jié)
-
通過(guò)閱讀本文,你已經(jīng)全面了解Android中如何實(shí)現(xiàn)具備歷史搜索記錄的搜索框
-
接下來(lái)會(huì)介紹繼續(xù)介紹Android開(kāi)發(fā)中的常用知識(shí)。
來(lái)自:http://www.jianshu.com/p/3682f6536e49
標(biāo)簽: 代碼 數(shù)據(jù)庫(kù) 搜索
版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。