Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách sử dụng chức năng tìm kiếm trong Custom ListView Android bằng Kotlin

Giới thiệu

Ví dụ minh họa cách thêm chức năng tìm kiếm (search/filter) cho ListView sử dụng ArrayAdapter có sẵn phương thức getFilter() và TextWatcher trên EditText bằng Kotlin.

Các bước thực hiện

Bước 1: Tạo dự án mới

Android Studio > File > New Project.

Bước 2: Layout (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="4dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/etSearch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Tìm kiếm tại đây..." />

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/etSearch" />

</RelativeLayout>

Bước 3: MainActivity.kt

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    private lateinit var listView: ListView
    private lateinit var etSearch: EditText
    private val months = mutableListOf()
    private var arrayAdapter: ArrayAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = "KotlinApp"

        listView = findViewById(R.id.listView)
        etSearch = findViewById(R.id.etSearch)

        // Dữ liệu mẫu
        months.addAll(listOf(
            "January", "February", "March", "April", "May", "June",
            "July", "August", "September", "October", "November", "December"
        ))

        arrayAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, months)
        listView.adapter = arrayAdapter

        // TextWatcher để lọc real-time
        etSearch.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                arrayAdapter?.filter?.filter(s) // Lọc adapter
            }
            override fun afterTextChanged(s: Editable) {}
        })
    }
}

Bước 4: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.listviewsearch">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

Chạy ứng dụng

Chạy app, gõ vào ô tìm kiếm (ví dụ: "Jan"), ListView sẽ lọc chỉ còn "January". Xóa text -> hiện lại tất cả.

Mở rộng: Custom Adapter với Filter tùy chỉnh

ArrayAdapter filter mặc định chỉ tìm kiếm theo toString() của object. Với object phức tạp, cần override Filter:

// Model
data class Contact(val name: String, val phone: String)

// Custom Adapter implements Filterable
class ContactAdapter(context: Context, private val originalList: List) :
    ArrayAdapter(context, 0, originalList), Filterable {

    private var filteredList = originalList

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        // Inflate layout, bind name, phone...
    }

    override fun getFilter(): Filter = object : Filter() {
        override fun performFiltering(constraint: CharSequence?): FilterResults {
            val query = constraint?.toString()?.lowercase() ?: ""
            val results = FilterResults()
            if (query.isEmpty()) {
                results.values = originalList
                results.count = originalList.size
            } else {
                val filtered = originalList.filter { it.name.lowercase().contains(query) }
                results.values = filtered
                results.count = filtered.size
            }
            return results
        }

        override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
            filteredList = (results?.values as? List) ?: emptyList()
            notifyDataSetChanged()
        }
    }

    override fun getCount() = filteredList.size
    override fun getItem(position: Int) = filteredList[position]
}

Cách hiện đại: RecyclerView + SearchView (Toolbar)

Thay vì ListView + EditText, dùng SearchView trong Toolbar/ActionBar + RecyclerView:

// Menu: res/menu/search_menu.xml
<menu xmlns:android="..." xmlns:app="...">
    <item android:id="@+id/action_search"
        android:title="Tìm kiếm"
        android:icon="@android:drawable/ic_menu_search"
        app:showAsAction="collapseActionView|ifRoom"
        app:actionViewClass="androidx.appcompat.widget.SearchView" />
</menu>

// Activity
override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.search_menu, menu)
    val searchItem = menu.findItem(R.id.action_search)
    val searchView = searchItem.actionView as SearchView
    searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String?) = false
        override fun onQueryTextChange(newText: String?): Boolean {
            adapter.filter.filter(newText) // Adapter phải implements Filterable
            return true
        }
    })
    return true
}

Tóm tắt

  • ListView + ArrayAdapter: Đơn giản, filter built-in, phù hợp list nhỏ, dữ liệu đơn giản (String).
  • Custom Adapter + Filterable: Cần thiết cho object phức tạp, tùy chỉnh logic tìm kiếm (tìm theo name, phone, email...).
  • RecyclerView + SearchView: Chuẩn hiện đại, hiệu năng tốt, UI đẹp, tách biệt logic.