Как реализовать поиск по аббревиатуре в SearchView - PullRequest
0 голосов
/ 24 марта 2019

У меня есть Fragment, содержащий список строк (названий компаний), который работает с SearchView. Тем не менее, из-за того, что многие компании имеют длинные имена, есть ли способ, в котором я могу ввести сокращение для названия компании, а не вводить полное название компании? «FTSE 150» и «FTSE 250» говорят сами за себя, поэтому аббревиатуры не нужны.

Аббревиатуры для названий компаний

  • GSK - GlaxoSmithKline plc
  • HSX - Hiscox Ltd
  • IHG - ИнтерКонтиненталь Hotels Group plc
  • MKS - Marks & Spencer Group plc

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="company_names">
        <item>@string/glaxosmithkline_plc</item>
        <item>@string/hiscox_ltd</item>
        <item>@string/intercontinental_hotels_group_plc</item>
        <item>@string/marks_and_spencer_group_plc</item>
        <item>@string/ftse_150</item>
        <item>@string/ftse_250</item>
    </string-array>

    <string name="glaxosmithkline_plc">GlaxoSmithKline plc</string>
    <string name="hiscox_ltd">Hiscox Ltd</string>
    <string name="intercontinental_hotels_group_plc">InterContinental Hotels Group plc</string>
    <string name="marks_and_spencer_group_plc">Marks &amp; Spencer Group plc</string>
    <string name="ftse_150">FTSE 150</string>
    <string name="ftse_250">FTSE 250</string>
</resources>

фрагмент класса

class MyFragment : androidx.fragment.app.Fragment() {

    private var mAdapter: MyListAdapter? = null

    private lateinit var mRecyclerView: androidx.recyclerview.widget.RecyclerView

    private var mTwoPane: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.layout_recyclerview, container, false)
        mTwoPane = (activity as androidx.fragment.app.FragmentActivity).findViewById<View>(R.id.detail_container) != null

        mRecyclerView = view.findViewById(R.id.recyclerView_list)
        mRecyclerView.setHasFixedSize(true)
        mRecyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this.activity)
        mRecyclerView.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(Objects.requireNonNull<Context>(context), LinearLayout.VERTICAL))

        val myList = ArrayList<Companies>()

//        val items = resources.getStringArray(R.array.company_names)
//        for (n in items) {
//            val company = Companies(0, "", "")
//            myList.add(company)
//        }

        val companyA = Companies(1, "GlaxoSmithKline plc", "GSK")
        val companyB = Companies(2, "Hiscox Ltd", "HSX")
        val companyC = Companies(3, "InterContinental Hotels Group plc", "IHG")
        val companyD = Companies(4, "Marks & Spencer Group plc", "MKS")
        val companyE = Companies(5, "FTSE 150", "")
        val companyF = Companies(6, "FTSE 250", "")

        val myList = DatabaseHandler(this.context!!)
        myList.insertData(companyA)
        myList.insertData(companyB)
        myList.insertData(companyC)
        myList.insertData(companyD)
        myList.insertData(companyE)
        myList.insertData(companyF)

        mAdapter = MyListAdapter(activity!!, myList, mTwoPane)

        mRecyclerView.adapter = mAdapter

        return view
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        val mInflater = Objects.requireNonNull<androidx.fragment.app.FragmentActivity>(activity).menuInflater
        mInflater.inflate(R.menu.menu_search, menu)

        val searchView = searchitem.actionView as SearchView

        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String): Boolean {
                return false
            }

            override fun onQueryTextChange(newText: String): Boolean {
                mAdapter!!.filter.filter(newText)
                return false
            }
        })

        super.onCreateOptionsMenu(menu, inflater)
    }
}

MyListAdapter class

class MyListAdapter(private val mCtx: Context, private val myList: MutableList<Companies>,
                          private val
mTwoPane: Boolean) : androidx.recyclerview.widget.RecyclerView.Adapter<MyListAdapter
.CompanyViewHolder>(), Filterable {
    private var myListFull = myList.toMutableList()

    private val companyFilter = object : Filter() {
        override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
            val filteredList = ArrayList<Companies>()

            when {
                constraint == null || constraint.isEmpty() -> filteredList.addAll(myListFull)
                else -> {
                    val filterPattern = constraint.toString().toLowerCase().trim { it <= ' ' }

                    for (item in myListFull) {
                        when {
                            item.companyName!!.toLowerCase().contains(filterPattern) ->
                                filteredList.add(item)
                        }
                    }
                }
            }

            val results = Filter.FilterResults()
            results.values = filteredList
            return results
        }

        override fun publishResults(constraint: CharSequence?, results: Filter.FilterResults?) {
            myList.clear()
            myList.addAll(results!!.values as List<Companies>)
            notifyDataSetChanged()
        }
    }

    inner class CompanyViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
    .ViewHolder(itemView) {
        var tvTitle: TextView = itemView.findViewById(R.id.tv_RVItem)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CompanyViewHolder {
        val inflater = LayoutInflater.from(mCtx)
        val v = inflater.inflate(R.layout.recyclerview_item_textview, parent, false)
        return CompanyViewHolder(v)
    }

    override fun onBindViewHolder(holder: CompanyViewHolder, position: Int) {
        val product = myList[holder.adapterPosition]

        holder.tvTitle.text = product.companyfuName
    }

    override fun getItemCount(): Int {
        return myList.size
    }

    override fun getFilter(): Filter {
        return companyFilter
    }
}

enter image description here

enter image description here

enter image description here

ОБНОВЛЕНИЕ

Пользовательский класс модели

data class Companies (val id: String, val fullName: String, val abbreviation: String)

обновлен класс адаптера

class MyListAdapter(private val mCtx: Context,
                    private val mCompanies: MutableList<Companies>,
                    private val mTwoPane: Boolean) : androidx.recyclerview.widget.RecyclerView.Adapter<MyListAdapter
.CompanyViewHolder>(), Filterable {
    private val mCompaniesFull = mCompanies.toMutableList()

    private val companyFilter = object : Filter() {
        override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
            val filteredList = if (constraint == null || constraint.isEmpty()) {
                mCompanies
            } else {
                val filterText = constraint.toString()
                mCompanies.filter { it.companyName.matchesIgnoreCase(filterText) || it.companyAbbreviation.matchesIgnoreCase(filterText) }
            }

            val results = Filter.FilterResults()
            results.values = filteredList
            return results
        }

        override fun publishResults(constraint: CharSequence?, results: Filter.FilterResults?) {
            mCompanies.clear()
            mCompanies.addAll(results!!.values as List<Companies>)
            notifyDataSetChanged()
        }
    }

    private fun String.matchesIgnoreCase(otherString: String): Boolean {
        return this.toLowerCase().contains(otherString.trim().toLowerCase())
    }

    inner class CompanyViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
    .ViewHolder(itemView) {
        var tvTitle: TextView = itemView.findViewById(R.id.tv_RVItem)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CompanyViewHolder {
        val inflater = LayoutInflater.from(mCtx)
        val v = inflater.inflate(R.layout.recyclerview_item_textview, parent, false)
        return CompanyViewHolder(v)
    }

    override fun onBindViewHolder(holder: CompanyViewHolder, position: Int) {
        val product = mCompanies[holder.adapterPosition]
        holder.tvTitle.text = product.companyName
    }

    override fun getItemCount(): Int {
        return mCompanies.size
    }

    override fun getFilter(): Filter {
        return companyFilter
    }
}

обновлен класс фрагментов

class MonFragment : androidx.fragment.app.Fragment() {

    private var mAdapter: MyListAdapter? = null

    private lateinit var mRecyclerView: androidx.recyclerview.widget.RecyclerView

    private var mTwoPane: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.layout_recyclerview, container, false)
        mTwoPane = (activity as androidx.fragment.app.FragmentActivity).findViewById<View>(R.id.detail_container) != null

        mRecyclerView = view.findViewById<RecyclerView>(R.id.recyclerView_list)
        mRecyclerView.setHasFixedSize(true)
        mRecyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this.activity)
        mRecyclerView.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(Objects.requireNonNull<Context>(context), LinearLayout.VERTICAL))

        mCompanies.add(Companies("GlaxoSmithKline plc", "GSK"))
        mCompanies.add(Companies("Hiscox Ltd", "HSX"))
        mCompanies.add(Companies("InterContinental Hotels Group plc", "IHG"))
        mCompanies.add(Companies("Marks & Spencer Group plc", "MKS"))
        mCompanies.add(Companies("FTSE 150", ""))
        mCompanies.add(Companies("FTSE 250", ""))

        mAdapter = MyListAdapter(activity!!, mCompanies, mTwoPane)

        mRecyclerView.adapter = mAdapter

        return view
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        val mInflater = Objects.requireNonNull<androidx.fragment.app.FragmentActivity>(activity).menuInflater
        mInflater.inflate(R.menu.menu_search, 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): Boolean {
                return false
            }

            override fun onQueryTextChange(newText: String): Boolean {
                mAdapter!!.filter.filter(newText)
                mAdapter!!.notifyDataSetChanged()

                return false
            }
        })

        super.onCreateOptionsMenu(menu, inflater)
    }
}

1 Ответ

1 голос
/ 25 марта 2019

После обсуждения в комментариях я бы предложил два подхода для хранения данных:

  1. Хранилище устройства с SharedPreferences или базами данных: SQLite , Комната (это легкие локальные базы данных, подключение к Интернету не требуется).
  2. Память устройства ( не рекомендуется ) в виде списка жестко закодированных объектов, инициализированных в методе MainActivity или Fragment onCreate (зависит от использования).

Как @CoolMind предложил вам реализовать пользовательский класс модели CompanyName(id, fullName, abbreviation), а затем отфильтровать по альтернативе fullName и abbreviation.

EDIT: В MyListAdapter избавьтесь от myListFull, так как он ничего не делает и положите:

private val stockFilter = object : Filter() {
        override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
            val filteredList = if (constraint == null || constraint.isEmpty()) myList
                else {
                   val filterText = constraint.toString()
                   myList.filter { it.fullName.matchesIgnoreCase(filterText ) || it.abbreviation.matchesIgnoreCase(filterText) }
                }
            val results = Filter.FilterResults()
            results.values = filteredList
            return results
        }
}

Также добавьте в класс MyListAdapter эту функцию расширения:

private fun String.matchesIgnoreCase(otherString: String): Boolean {
    return this.toLowerCase().contains(otherString.trim().toLowerCase())
}
...