preface

  • AndroidA search function like the one below is common in development

  • Today, I’m going to teach you how to implement oneEncapsulates historical search record functionality & stylesAndroidCustom search box open source library, hope you will like.

Available on Github: SearchView, welcome Star!


directory


1. Introduction

An encapsulation of the history of the search record function & style Android custom search box

Available on Github: SearchView, welcome Star!


2. Requirement scenario

  • In the beginningcodingBefore,Understand user needs and scenariosIt helps us to better design & implement functions
  • The requirement scenarios are as follows


3. Business flow chart

According to the scenario, the functional service flow chart is as follows:


4. Functional requirements

According to the function service flow chart, the function requirements are as follows

4.1 Function List

4.2 Functional prototype diagram

4.3 sketch


5. Overall design

Below, specific technical solutions are presented based on functional requirements

5.1 Overall Solution

5.2 Project structure description

  • Project schematic diagram

The Github address of Carson_Ho is Search_Layout

  • Structure that
The file type role
SearchView.java Search box all functions of the implementation
RecordSQLiteOpenHelper.java Create and manage databases & version control
EditText_Clear.java Custom EdiText, rich with custom styles & one-click delete
ICallBack.java Interface callback method after clicking the search button
bCallBack.java Interface callback method after clicking the Back key
SearchListView.java Resolve ListView & ScrollView nesting conflicts
search_layout.xml Search box layout

6. Detailed functional design

Detailed functional logic is given below

6.1 Keyword Search

  • Description: Search results based on the search field entered by the user
  • Prototype figure

Note: the keyword search function is different from person to person, so this source code only set aside interface for developers to achieve, not specific implementation

  • Source code analysis

Analysis 1: editText_clear.java

  • Function: CustomEdiText, and the system built-inEdiTextContrast: more left picture & right picture Settings, one click clearEdiTextContent function
  • The specific code is as follows:
Public class EditText_Clear extends android. Support. V7. Widget. AppCompatEditText {/ * * * step 1: Definition on the left side of the search icon and a button to delete the icon * / private Drawable clearDrawable, searchDrawable; public EditText_Clear(Context context) { super(context); init(); // When initializing this component, Initialize EditText_Clear ->> Step 2} 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(); } /** * Step 2: Initialize icon resource */ private voidinit() {
        clearDrawable = getResources().getDrawable(R.drawable.delete);
        searchDrawable = getResources().getDrawable(R.drawable.search);

        setCompoundDrawablesWithIntrinsicBounds(searchDrawable, null,
                null, null);
        // setDrawable CompoundDrawablesWithIntrinsicBounds (Drawable left, top, Drawable right, Drawable bottom) / / functions: Set the top, bottom, left, and right ICONS in EditText (equivalent to Android :drawableLeft=)""  android:drawableRight=""// Note 1:setCompoundDrawablesWithIntrinsicBounds () to the Drawable wide high inherent width = (automatically by getIntrinsicWidth () & getIntrinsicHeight () to obtain) / / note 2: // Set the search icon on the left here. // Another similar method:setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottomsetDifference between CompoundDrawablesWithIntrinsicBounds () : can set the icon size / / incoming Drawable object must havesetBounds(x,y,width,height), that is, the initial position,width, and height must be set // x: the component starts on the X-axis of the container y: the component starts on the Y-axis of the container width: the component length height: the component height} /** * Step 3: Determine whether to display the delete icon by listening to a method that overwrites the EditText itself * Listening methods: onTextChanged () & onFocusChanged () * calling time: */ @override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter);setClearIconVisible(hasFocus() && text.length() > 0); // hasFocus() returns whether the EditTEXT focus is obtained, that is, whether // is selectedset1} @override protected void onFocusChanged(Boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect);setClearIconVisible(focused && length() > 0); // focus = not focused //setClearIconVisible () check whether to display the delete icon} /** * Note 1 * : check whether to display the delete icon */ private voidsetClearIconVisible(boolean visible) {
        setCompoundDrawablesWithIntrinsicBounds(searchDrawable, null, visible ? clearDrawable : null, null); } /** * Step 4: Click the event to delete the icon locale, i.e"Click = to clear the search box."* the principle: When the finger is raised in the deleted icon area, */ @override public Boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) { When the finger position is in the delete icon area, it is regarded as clicking the delete icon = clear the contents of the search boxcase MotionEvent.ACTION_UP:
                Drawable drawable = clearDrawable;
                if(drawable ! = null && event.getX() <= (getWidth() - getPaddingRight()) && event.getX() >= (getWidth() - getPaddingRight() - drawable.getBounds().width())) {setText(""); GetWidth (): width of the control // getPaddingRight(): the distance from the right edge of the icon to the right edge of the EditText control // that is: GetWidth () -getPaddingright () = Remove the icon's right edge coordinates = X1 // getWidth() -getPaddingright () -drawable.getbounds ().width() = The coordinates of the left edge of the deleted icon = X2 // So the area between X1 and X2= the deleted icon area // When the finger is lifted in the deleted icon area (X2=< event.getx () <=X1), it is regarded as clicking the deleted icon = Clear the contents of the search box // Please see the following diagram for detailsbreak;
        }
        returnsuper.onTouchEvent(event); }}Copy the code

Android Custom EditText: A SuperEditText with one click delete & more custom styles

Analysis 2: SearchListView.java

  • Function: solveListView & ScrollViewThe nested conflict of
  • The specific code is as follows:
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);
    }

    // 通过复写其onMeasure方法,达到对ScrollView适配的效果
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

}
Copy the code

Analysis 3: search_layout.xml

  • Effect: Search box layout
  • The specific code is as follows:
<? 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"< 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"// The last 2 lines = replace input keyboard buttons: newline ->> search /> </LinearLayout> // Lower search record layout = 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 layout (using the explanation SearchListView, solved the conflict with the 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="Clear search History" />
        </LinearLayout>
    </ScrollView>
</LinearLayout>

Copy the code

Analysis 4: icallback. Java, bcallback. Java

  • Function: search key, return key callback interface
  • The specific code is as follows:
/**
 * ICallBack.java
 */
public interface ICallBack {
    void SearchAciton(String string);

}

/**
 * bCallBack.java
 */
public interface bCallBack {
    void BackAciton();
}
Copy the code

Analysis 5: SearchView.java

  • Functions: Covers all functions in the search box. This section mainly explains the implementation of keyword search function
  • The specific code is as follows:
/** * Step 1: Initialize the member variable */ // search box component private EditText et_search; Private LinearLayout Search_block; Private ImageView searchBack; // return key // callback interface private ICallBack mCallBack; // Search key callback interface private bCallBack bCallBack; Private SearchListView ListView; private SearchListView; private BaseAdapter adapter; /** * Step 2: Bind the search box component */ private voidinitView(){// 1. Bind r.layout.search_layout as the search box to the XML file Layoutinflater.from (context).inflate(r.layout.search_layout,this); // 2. Bind the search box EditText et_search = (EditText) findViewById(r.idt_search); // 3. Search box background color 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); // Initial status = invisible} /** * Step 3 * Listen to input the new keyboard search key * call time: click the keyboard search key */ 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) { Since the requirements here vary from situation to situation, the specific logic is implemented by the developers themselves, and only interfaces are reserved hereif(! (mCallBack == null)){ mCallBack.SearchAciton(et_search.getText().toString()); } Toast.makeText(context,"What you need to search for" + et_search.getText(), Toast.LENGTH_SHORT).show();
                    
                   
                }
                return false; }}); Public interface ICallBack {void SearchAciton(String String); } // Return key interface callback public voidsetOnClickBack(bCallBack bCallBack){
        this.bCallBack = bCallBack;

    }
Copy the code

6.2 Displaying Historical Search Records in real-time

  • Description: Includes recent search history & similar search history
  • Prototype figure

  • Source code analysis

Analysis: 1 RccordSQLiteOpenHelper. Java

  • Purpose: Create and manage databases & version control

The database is used to store the user’s search history

  • The specific code is as follows:

For Android SQLlite database operation see article: Android: SQLlite database operation most detailed analysis

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) {public void onCreate(SQLiteDatabase db) {"create table records(id integer primary key autoincrement,name varchar(200))");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

Copy the code

Analysis 2: SearchView.java

  • Functions: Covers all functions in the search box. This section mainly explains how to realize the real-time display of historical search records
  • The specific code is as follows:
Private RecordSQLiteOpenHelper Helper; private RecordSQLiteOpenHelper helper helper; private SQLiteDatabase db; // ListView list & adapter private SearchListView ListView; listView = (SearchListView) findViewById(R.id.listView); private BaseAdapter adapter; SQLiteOpenHelper subclass object Helper = new RecordSQLiteOpenHelper(context); / * * * step 2: the search box. Change the text of the real-time monitoring * / et_search addTextChangedListener (newTextWatcher() { @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) { If the search box is empty, fuzzy search null character = displays all search history String tempName = et_search.gettext ().tostring (); queryData(tempName); // ->> Attention 1}}); / * * * step 3: search record list (ListView) listen * that when users click on search history in the field, will directly to the search results as a search field * / ListView. The setOnItemClickListener (new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<? > parent, View view, int position, TextView TextView = (TextView) view.findViewById(Android.r.ext1); String name = textView.getText().toString(); et_search.setText(name); Toast.makeText(context, name, Toast.LENGTH_SHORT).show(); }}); Private void queryData(String tempName) {// 1. Fuzzy search 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); // 3. SetAdapter listview. setAdapter(adapter); adapter.notifyDataSetChanged(); System.out.println(cursor.getCount()); // Displays when the input box is empty & there is a search record in the database"Delete search history"buttonif (tempName.equals("") && cursor.getCount() ! = 0){ tv_clear.setVisibility(VISIBLE); }else {
            tv_clear.setVisibility(INVISIBLE);
        };

    }
Copy the code

6.3 Deleting Historical Search Records

  • Description: Clears all historical search records
  • Prototype figure

  • Source code analysis
/** * step 1: Initialize variable */ private TextView tv_clear; // Delete search record tv_clear = (TextView) findViewById(r.id.tv_clear); tv_clear.setVisibility(INVISIBLE); // Initial status = Invisible /** * Step 2: Set"Clear your search history"Button */ tv_clear.setOnClickListener(new View.OnClickListener() {@override public void onClick(View v) {// deleteData(); // Fuzzy search null character = display all search history (at this point there is no search record) & display criteria for this button ->> Attention 3 queryData(""); }}); /** * 2: Empty the database */ private voiddeleteData() {

        db = helper.getWritableDatabase();
        db.execSQL("delete from records"); db.close(); tv_clear.setVisibility(INVISIBLE); } private void queryData(String tempName) {private void queryData(String tempName) { Go to step 4 // 1. Fuzzy search 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); // 3. SetAdapter listview. setAdapter(adapter); adapter.notifyDataSetChanged(); // 4. Display only when the input box is empty & there are search records in the database"Delete search history"buttonif (tempName.equals("") && cursor.getCount() ! = 0){ tv_clear.setVisibility(VISIBLE); }else {
            tv_clear.setVisibility(INVISIBLE);
        };

    }
Copy the code

6.4 Saving Historical Search Records

  • Description: Saves the search field entered by the user to the database
  • Prototype figure

  • Source code analysis
/** */ 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) { After clicking the search button, query according to the entered search field. // Note: The specific logic is implemented by the developers themselves because the requirements may vary according to their own situations. Only interfaces are reserved hereif(! (mCallBack == null)){ mCallBack.SearchAciton(et_search.getText().toString()); } Toast.makeText(context,"What you need to search for"+ et_search.getText(), Toast.LENGTH_SHORT).show(); 3 Boolean hasData = hasData(et_search.gettext ().toString().trim())); // 3. If yes, do not save; If it does not exist, the search field is saved (inserted) into the database as a historical search recordif(! hasData) { insertData(et_search.getText().toString().trim()); // ->> follow 4 queryData(""); }}return false; }}); */ private Boolean hasData(String tempName) {// Select ID Cursor from Record where name=tempName cursor = helper.getReadableDatabase().rawQuery("select id as _id,name from records where name =?", new String[]{tempName}); // Determine if there is a next onereturncursor.moveToNext(); } / attention to 4 * * * * insert data to the database, that is, into the search field to search history records * / private void insertData (String tempName) {db = helper. GetWritableDatabase (); db.execSQL("insert into records(name) values('" + tempName + "')");
        db.close();
    }
Copy the code
  • At this point, on the search box all the source code explained.
  • Carson_Ho Github address: SearchView

7. Specific use

  • See article: Android Open Source Library: here is a simple, easy-to-use search box with historical search history
  • Carson_Ho Github address: SearchView


Contribute code

  • I hope you can work with me to improve this simple & easy to useSearchViewControl, see:Contribution to illustrate
  • Comments & suggestions about the open source project can be made on Issue. welcomeStar

9. To summarize

  • You will love this simple and easy to use SearchView control

Open source on Github: SearchView, welcome Star!

  • In the next article I’ll continue with some interesting customizationsViewExamples explained, interested can continue to pay attention toCarson_Ho android Development Notes

More easy to Use Open Source libraries: Easy & Easy to use Open source components:

  1. Custom EditText: A manual guide to creating a SuperEditText with one-click delete & custom styles
  2. You can also write a cute & xiaozi style Android load waiting custom View yourself

Thumb up, please! Because your encouragement is the biggest power that I write!


Welcome to follow Carson_ho on wechat