Trong bài viết này, chúng ta sẽ tìm hiểu về SearchView trong Android và xây dựng một ứng dụng đơn giản để lọc dữ liệu trong ListView dựa trên văn bản người dùng nhập vào. Chúng ta sẽ sử dụng DataBinding để liên kết layout trong Activities và Adapters.

Android SearchView
Android cho phép chúng ta tích hợp chức năng tìm kiếm vào ứng dụng bằng cách hiển thị SearchView widget. Widget này có thể được đặt trên ToolBar/ActionBar hoặc chèn trực tiếp vào một file layout. SearchView có sẵn từ phiên bản Android 3.0 (API 11) trở đi. Bạn có thể khai báo nó trong XML như sau:
<android.support.v7.widget.SearchView
android:id="@+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Android hỗ trợ nhiều hình thức tìm kiếm khác nhau, chẳng hạn như tìm kiếm bằng giọng nói, hiển thị gợi ý, v.v. Trong hướng dẫn này, chúng ta sẽ tập trung vào việc sử dụng hai interface chính là SearchView.OnQueryTextListener và Filterable.
Interface Filterable được dùng để lọc danh sách dữ liệu (ví dụ như trong một ListView) dựa trên từ khóa tìm kiếm và chỉ hiển thị những kết quả phù hợp. Trong khi đó, interface OnQueryTextListener giúp chúng ta lắng nghe hai sự kiện chính từ người dùng:
onQueryTextChange(): Được gọi ngay lập tức mỗi khi người dùng thay đổi nội dung trong ô tìm kiếm (ví dụ: gõ hoặc xóa một ký tự).onQueryTextSubmit(): Được kích hoạt khi người dùng hoàn tất việc tìm kiếm (ví dụ: nhấn phím Enter hoặc nút tìm kiếm trên bàn phím).
Ví dụ Android SearchView
Hình ảnh bên dưới minh họa kết quả cuối cùng của ứng dụng mẫu sử dụng SearchView trong Android.

Ví dụ này gồm có một Activity và một Adapter dùng cho ListView.
Mã nguồn ví dụ Android SearchView
activity_main.xml được trình bày dưới đây. Layout này gồm một SearchView nằm phía trên và một ListView bên dưới:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="<https://schemas.android.com/apk/res/android>">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<android.support.v7.widget.SearchView
android:id="@+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true" />
<ListView
android:id="@+id/list_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/search" />
</RelativeLayout>
</layout>
MainActivity.java được trình bày như sau:
package com.journaldev.searchview;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import com.journaldev.searchview.databinding.ActivityMainBinding;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding activityMainBinding;
ListAdapter adapter;
List<String> arrayList= new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
arrayList.add("January");
arrayList.add("February");
arrayList.add("March");
arrayList.add("April");
arrayList.add("May");
arrayList.add("June");
arrayList.add("July");
arrayList.add("August");
arrayList.add("September");
arrayList.add("October");
arrayList.add("November");
arrayList.add("December");
adapter= new ListAdapter(arrayList);
activityMainBinding.listView.setAdapter(adapter);
activityMainBinding.search.setActivated(true);
activityMainBinding.search.setQueryHint("Type your keyword here");
activityMainBinding.search.onActionViewExpanded();
activityMainBinding.search.setIconified(false);
activityMainBinding.search.clearFocus();
activityMainBinding.search.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
adapter.getFilter().filter(newText);
return false;
}
});
}
}
Trong đoạn mã trên, một danh sách ArrayList chứa các tháng trong năm được truyền vào ListAdapter. Mỗi khi người dùng thay đổi nội dung trong SearchView, phương thức filter() của adapter sẽ được gọi để lọc dữ liệu.
package com.journaldev.searchview;
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import com.journaldev.searchview.databinding.RowItemBinding;
import java.util.ArrayList;
import java.util.List;
public class ListAdapter extends BaseAdapter implements Filterable {
List<String> mData;
List<String> mStringFilterList;
ValueFilter valueFilter;
private LayoutInflater inflater;
public ListAdapter(List<String> cancel_type) {
mData=cancel_type;
mStringFilterList = cancel_type;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public String getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, final ViewGroup parent) {
if (inflater == null) {
inflater = (LayoutInflater) parent.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
RowItemBinding rowItemBinding = DataBindingUtil.inflate(inflater, R.layout.row_item, parent, false);
rowItemBinding.stringName.setText(mData.get(position));
return rowItemBinding.getRoot();
}
@Override
public Filter getFilter() {
if (valueFilter == null) {
valueFilter = new ValueFilter();
}
return valueFilter;
}
private class ValueFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null && constraint.length() > 0) {
List<String> filterList = new ArrayList<>();
for (int i = 0; i < mStringFilterList.size(); i++) {
if ((mStringFilterList.get(i).toUpperCase()).contains(constraint.toString().toUpperCase())) {
filterList.add(mStringFilterList.get(i));
}
}
results.count = filterList.size();
results.values = filterList;
} else {
results.count = mStringFilterList.size();
results.values = mStringFilterList;
}
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
mData = (List<String>) results.values;
notifyDataSetChanged();
}
}
}
Như bạn có thể thấy, việc lọc dữ liệu được thực hiện thông qua một lớp bên trong (inner class) có tên là ValueFilter, lớp này kế thừa từ Filter. ValueFilter thực hiện việc lọc bằng cách kiểm tra xem chuỗi người dùng nhập vào có khớp với bất kỳ chuỗi nào trong ArrayList hay không. Dưới đây là layout XML được sử dụng cho mỗi hàng (row) trong ListView, được định nghĩa trong file row_item.xml.
<layout xmlns:android="<https://schemas.android.com/apk/res/android>">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/stringName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:padding="@dimen/activity_horizontal_margin"
android:textAllCaps="false"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
</layout>
Giao diện kết quả khi chạy ứng dụng SearchView

SearchView chỉ hiển thị biểu tượng tìm kiếm, người dùng phải nhấn vào biểu tượng này mới có thể nhập nội dung tìm kiếm. Ngoài ra, nó cũng không hiển thị văn bản gợi ý (hint). Để cải thiện trải nghiệm người dùng, bạn có thể kích hoạt sẵn ô tìm kiếm và thêm hint bằng đoạn mã sau trong MainActivity:
activityMainBinding.search.setActivated(true);
activityMainBinding.search.setQueryHint("Type your keyword here");
activityMainBinding.search.onActionViewExpanded();
activityMainBinding.search.setIconified(false);
activityMainBinding.search.clearFocus();
SearchView sau khi được tùy chỉnh sẽ hiển thị như sau:

Như vậy, chúng ta vừa hoàn thành hướng dẫn sử dụng SearchView trong Android với ListView và DataBinding. Trong các bài tiếp theo, chúng ta sẽ tìm hiểu sâu hơn về những tính năng nâng cao của SearchView.