SearchView คืออะไร?
SearchView เป็น Widget สำหรับรับคำค้นหาจากผู้ใช้ มักวางที่ Toolbar/ActionBar หรือใน Layout โดยตรง รองรับการกรองข้อมูลแบบ Real-time (Filter) และการส่งคำค้นหา (Submit)
โครงสร้างพื้นฐาน
- SearchView - UI รับคำค้นหา
- ListView/RecyclerView - แสดงรายการข้อมูล
- Adapter + Filter - จัดการข้อมูลและกรอง
ตัวอย่างครบถ้วน: SearchView กับ ListView
1. Layout (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.SearchView
android:id="@+id/searchView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:queryHint="ค้นหาภาษาโปรแกรม..."
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:iconifiedByDefault="false" />
<ListView
android:id="@+id/listView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/searchView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2. MainActivity.java
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ListView listView;
private SearchView searchView;
private ArrayAdapter adapter;
private List originalData = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listView);
searchView = findViewById(R.id.searchView);
// ข้อมูลตัวอย่าง
originalData.add("Java");
originalData.add("Kotlin");
originalData.add("Android");
originalData.add("Swift");
originalData.add("Dart");
originalData.add("JavaScript");
originalData.add("TypeScript");
originalData.add("Python");
originalData.add("C++");
originalData.add("C#");
originalData.add("Go");
originalData.add("Rust");
// Adapter ใช้ simple_list_item_1 (built-in layout)
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>(originalData));
listView.setAdapter(adapter);
// ตั้งค่า SearchView
setupSearchView();
// คลิกที่รายการ
listView.setOnItemClickListener((parent, view, position, id) -> {
String item = adapter.getItem(position);
Toast.makeText(this, "เลือก: " + item, Toast.LENGTH_SHORT).show();
});
}
private void setupSearchView() {
searchView.setIconifiedByDefault(false); // ขยายค้นหาเต็ม
searchView.setQueryHint("พิมพ์คำค้นหา...");
searchView.clearFocus(); // ไม่ให้ Focus อัตโนมัติ
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
// เมื่อกด Enter/ค้นหา
searchView.clearFocus();
return true; // consume event
}
@Override
public boolean onQueryTextChange(String newText) {
// กรองข้อมูล Real-time
filterList(newText);
return true;
}
});
}
private void filterList(String query) {
List filtered = new ArrayList<>();
if (query.isEmpty()) {
filtered.addAll(originalData);
} else {
String lowerQuery = query.toLowerCase();
for (String item : originalData) {
if (item.toLowerCase().contains(lowerQuery)) {
filtered.add(item);
}
}
}
adapter.clear();
adapter.addAll(filtered);
adapter.notifyDataSetChanged();
}
}
SearchView ใน Toolbar/ActionBar (แนะนำ)
วาง SearchView ใน Menu เพื่อให้อยู่ใน ActionBar
menu/search_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:title="ค้นหา"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView" />
</menu>
ใน Activity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search_menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchItem.getActionView();
// ตั้งค่า listener เหมือนด้านบน
searchView.setOnQueryTextListener(...);
return true;
}
RecyclerView + Filterable (แนะนำสำหรับ Production)
ListView ล้าสมัยแล้ว ควรใช้ RecyclerView พร้อม implements Filterable ใน Adapter
public class MyAdapter extends RecyclerView.Adapterimplements Filterable { private List originalList; private List filteredList; private Filter filter; // ... constructor, onCreateViewHolder, onBindViewHolder @Override public Filter getFilter() { if (filter == null) { filter = new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { List result = new ArrayList<>(); if (constraint == null || constraint.length() == 0) { result.addAll(originalList); } else { String query = constraint.toString().toLowerCase(); for (String item : originalList) { if (item.toLowerCase().contains(query)) result.add(item); } } FilterResults results = new FilterResults(); results.values = result; results.count = result.size(); return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { filteredList = (List ) results.values; notifyDataSetChanged(); } }; } return filter; } }
เคล็ดลับและ Best Practices
- Debounce: ใช้ RxJava/Kotlin Flow `debounce(300ms)` เพื่อลดการกรองซ้ำ
- Large Dataset: ใช้ `Filterable` ใน Background Thread (performFiltering รันบน Worker Thread)
- Clear Focus: เรียก `searchView.clearFocus()` หลังค้นหาเพื่อซ่อนคีย์บอร์ด
- Voice Search: เพิ่ม `android:voiceSearchMode` ใน XML เพื่อรองรับค้นหาด้วยเสียง
- Recent Queries: ใช้ `setOnSuggestionListener` และ `SearchRecentSuggestionsProvider`