برنامه نویسی ۵۰۳ بازدید

اگر به طور مکرر نیازمند پیاده‌سازی قابلیت جستجو برای لیست‌ها در اپلیکیشن‌های اندرویدی هستید، این مقاله برای شما کاملاً مفید خواهد بود. در این راهنما روشی عالی برای پیاده‌سازی یک آداپتر دینامیک مجرد معرفی می‌کنیم که می‌تواند برای استفاده از فیلتر جستجو روی Recycler View به کار گرفته شود.

فهرست مطالب این نوشته

شروع

ابتدا باید یک آداپتر Recycler View بسازید تا لیست‌بندی‌ها را مدیریت کنید. با این حال به جای نوع استاندارد، DynamicSearchAdapter (+) را بسط می‌دهیم.

زمانی که این کار را انجام دادید، لیست خود را در اختیار آداپتر والد قرار دهید و ضمناً لیست را داخل <> به عنوان پارامتر نوع قرار دهید. اینک می‌توانید از قابلیت جستجو در هر تعداد از لیست‌ها در اکتیویتی‌ها یا فرگمان‌ها استفاده کنید.

برای نمونه یک پیاده‌سازی ساده برای 2 مورد از آداپترهای متفاوت می‌تواند مانند زیر باشد:

class SearchAdapter1(private val mutableList: MutableList<SearchModel1>) :
    DynamicSearchAdapter<SearchModel1>(mutableList) {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val textView = TextView(parent.context)
        return ViewHolder(textView)
    }

    override fun getItemCount(): Int {
        return mutableList.count()
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val tv = holder.containerView as TextView
        tv.text = mutableList[position].data

    }


}
class SearchAdapter2(private val mutableList: MutableList<SearchModel2>) :
    DynamicSearchAdapter<SearchModel2>(mutableList) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val textView = TextView(parent.context)
        return ViewHolder(textView)
    }

    override fun getItemCount(): Int {
        return mutableList.count()
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val tv = holder.containerView as TextView
        tv.text = mutableList[position].data

    }


}

کد آداپتر جستجوی دینامیک مجرد به صورت زیر است:

abstract class DynamicSearchAdapter<T : DynamicSearchAdapter.Searchable>(private val searchableList: MutableList<T>) :
        RecyclerView.Adapter<ViewHolder>(), Filterable {

    // Single not-to-be-modified copy of original data in the list.
    private val originalList = ArrayList(searchableList)
    // a method-body to invoke when search returns nothing. It can be null.
    private var onNothingFound: (() -> Unit)? = null

    /**
     * Searches a specific item in the list and updates adapter.
     * if the search returns empty then onNothingFound callback is invoked if provided which can be used to update UI
     * @param s the search query or text. It can be null.
     * @param onNothingFound a method-body to invoke when search returns nothing. It can be null.
     */
    fun search(s: String?, onNothingFound: (() -> Unit)?) {
        this.onNothingFound = onNothingFound
        filter.filter(s)

    }

    override fun getFilter(): Filter {
        return object : Filter() {
            private val filterResults = FilterResults()
            override fun performFiltering(constraint: CharSequence?): FilterResults {
                searchableList.clear()
                if (constraint.isNullOrBlank()) {
                    searchableList.addAll(originalList)
                } else {
                    val searchResults = originalList.filter { it.getSearchCriteria().contains(constraint) }
                    searchableList.addAll(searchResults)
                }
                return filterResults.also {
                    it.values = searchableList
                }
            }

            override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                // no need to use "results" filtered list provided by this method.
                if (searchableList.isNullOrEmpty())
                    onNothingFound?.invoke()
                notifyDataSetChanged()

            }
        }
    }

    interface Searchable {
        /** This method will allow to specify a search string to compare against
        your search this can be anything depending on your use case.
         */
        fun getSearchCriteria(): String
    }


}

انواع لیست که دارید باید اینترفیس Searchable را که در کلاس آداپتر فوق لیست شده پیاده‌سازی کنند تا قابلیت جستجو شدن را پیدا کنند. برای آشنایی با یک نوع از لیست SearchModel1 و SearchModel2 به مثال‌های زیر توجه کنید:

class SearchModel1(val data: String) : DynamicSearchAdapter.Searchable {


    override fun getSearchCriteria(): String {
        return data
    }
}
class SearchModel2(val data: String) : DynamicSearchAdapter.Searchable {


    override fun getSearchCriteria(): String {
        return data
    }
}

لیست‌های شما می‌توانند مانند <List<SearchModel1 و <List<SearchModel2 باشند و همچنین باید یک کوئری جستجو بازگشت دهید که با متد getSearchCriteria هر کلاس مقایسه می‌شود. این متد برای فیلتر کردن داده‌های هر لیست منفرد مورد استفاده قرار می‌گیرد. تا زمانی که الزامات فوق را رعایت کرده‌اید، می‌توانید هر تعداد لیست که دوست دارید ایجاد نمایید. متد search می‌تواند برای تحریک جستجو روی لیست مورد استفاده قرار گیرد. متد search به صورت ناهمگام از سوی اینترفیس Filterable اندروید اجرا می‌شود و از جستجوی خطی با دو پارامتر بهره می‌گیرد:

  • String: کوئری جستجو
  • ?(Unit<-()): یک بدنه لامبدا برای فراخوانی در مواردی که هیچ نتیجه‌ای برای جستجو یافت نشده است. از آن می‌توان برای به‌روزرسانی UI یا null استفاده کرد.

مثالی از یک اکتیویتی ساده مانند زیر کار می‌کند به این صورت است که کاربر داده‌ها را در نمای جستجو وارد می‌کند. در صورتی که هیچ نتیجه‌ای مطابق ورودی در لیست پیدا نشود یک toast نمایش پیدا می‌کند.

class SampleActivity2 : AppCompatActivity(), SearchView.OnQueryTextListener {


    private lateinit var searchAdapter3: SearchAdapter3

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.fragment_main)
        rv.layoutManager = LinearLayoutManager(this)
        searchAdapter3 = SearchAdapter3(mutableListOf<SearchModel3>().populateWithUUIDSM3())
        rv.adapter = searchAdapter3
        searchV.setOnQueryTextListener(this)

    }

    override fun onQueryTextChange(newText: String?): Boolean {
        search(newText)
        return true
    }

    override fun onQueryTextSubmit(query: String?): Boolean {
        search(query)
        return true
    }

    private fun search(s: String?) {
        searchAdapter3.search(s) {
            // update UI on nothing found
            Toast.makeText(this, "Nothing Found", Toast.LENGTH_SHORT).show()
        }
    }

شیوه اجرا

سورس کد این پروژه را می‌توانید در این آدرس (+) ببینید. این سورس شامل یک اکتیویتی و 3 فرگمان به عنوان مثال است که همگی یک لیست قابل جستجو را با استفاده از یک recycler view نمایش می‌دهند. همچنین آداپترهایی دارند که آداپتر Dynamic Search را بسط می‌دهد. می‌توانید این پروژه را در اندروید استودیو کلون کرده و اجرا نمایید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

«میثم لطفی» در رشته‌های ریاضیات کاربردی و مهندسی کامپیوتر به تحصیل پرداخته و شیفته فناوری است. وی در حال حاضر علاوه بر پیگیری علاقه‌مندی‌هایش در رشته‌های برنامه‌نویسی، کپی‌رایتینگ و محتوای چندرسانه‌ای، در زمینه نگارش مقالاتی با محوریت نرم‌افزار با مجله فرادرس همکاری دارد.