LoginSignup
15
12

More than 5 years have passed since last update.

Kotlinでインクリメンタルサーチをやってみる

Posted at

インクリメンタルサーチとは

そもそもインクリメンタルサーチとはなんぞや:yum:

インクリメンタルサーチ(Wikipedia)

アプリケーションにおける検索方法のひとつ。 検索したい単語をすべて入力した上で検索するのではなく、入力のたびごとに即座に候補を表示させる。 逐語検索、逐次検索とも。

アプリでよく使われている検索方法ですね。
早速やってみます。

準備

記事ではRecyclerViewを使用して、ツールバーに検索ボックス(SearchView)を実装してみます。

app/build.gradle
dependencies {
    compile 'com.android.support:recyclerview-v7:26.1.0'
}
styles.xml
<style name="AppTheme" parent="android:Theme.Material.Light.NoActionBar">
    /// 省略
</style>

SearchView

検索用のメニューを作成します。

search.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/menu_search"
        android:title="検索"
        android:actionViewClass="android.widget.SearchView"
        android:showAsAction="always"/>
</menu>



SearchView.OnQueryTextListenerから検索文字を取得します。

val searchView = menu.findItem(R.id.menu_search).actionView as SearchView
    searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        .
        .
        .

  
今回はインクリメンタルで検索したいので、onQueryTextChangeから
検索文字が変更されるたびに文字を受け取るようにします。

ItemListActivity.kt
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.search, menu)
    if (menu != null) {
        val searchView = menu.findItem(R.id.menu_search).actionView as SearchView
        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(text: String?): Boolean {
                // 検索キーが押下された
                Log.d(TAG, "submit text: $text")
                return false
            }

            override fun onQueryTextChange(text: String?): Boolean {
                // テキストが変更された
                Log.d(TAG, "change text: $text")
                val itemListFragment = fragmentManager.findFragmentById(R.id.container)
                if (itemListFragment is ItemListFragment && text != null) {
                    itemListFragment.searchRequest(text)
                }
                return false
            }

        })
    }
    return super.onCreateOptionsMenu(menu)
}


検索文字を受け取ったら検索対象にフィルターをかけて更新します。

ItemListFragment.kt
fun searchRequest(text: String) {
    val adapter = adapter
    if (adapter != null) {
        adapter.data = items.filter { it.contains(text) }
        adapter.notifyDataSetChanged()
    }
}


実行結果

search_ss.gif


テキストの入力の度に検索結果を表示させることができました:smile:

コード

以下、最終的なソースコードです。
レイアウトは各々で作成してみてください。

ItemListAdapter.kt
class ItemListAdapter(private val context: Context,
                      var data: List<String>) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(context)
        return ViewHolder(inflater.inflate(R.layout.item, parent, false))
    }

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

    override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
        holder?.title = data[position]
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private var titleTextView: TextView = itemView.findViewById(R.id.title_text)

        var title: String
            get() = titleTextView.text.toString()
            set(value) {
                titleTextView.text = value
            }
    }

}


ItemListFragment.kt
class ItemListFragment : Fragment() {

    private var mView: View? = null
    private var recyclerView: RecyclerView? = null
    private val items: MutableList<String> = mutableListOf()
    private var adapter: ItemListAdapter? = null

    init {
        for (i in 1..100) {
            items.add("アイテム$i")
        }
    }

    override fun onCreateView(inflater: LayoutInflater?,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View {
        super.onCreateView(inflater, container, savedInstanceState)

        mView = inflater!!.inflate(R.layout.fragment_item_list, container, false)
        val linerLayoutManager = LinearLayoutManager(mView!!.context)
        recyclerView = mView!!.findViewById(R.id.recycler_view)
        recyclerView!!.layoutManager = linerLayoutManager

        val dividerDecoration = DividerItemDecoration(recyclerView!!.context, linerLayoutManager.orientation)
        recyclerView!!.addItemDecoration(dividerDecoration)

        return mView!!
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        adapter = ItemListAdapter(mView!!.context, items)
        recyclerView!!.adapter = adapter
    }

    fun searchRequest(text: String) {
        val adapter = adapter
        if (adapter != null) {
            adapter.data = items.filter { it.contains(text) }
            adapter.notifyDataSetChanged()
        }
    }
}


ItemListActivity.kt
class ItemListActivity : Activity() {

    companion object {
        private val TAG = ItemListActivity::class.java.simpleName
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_item_list)

        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        toolbar.title = "検索"
        setActionBar(toolbar)

        if (savedInstanceState == null) {
            val itemListFragment = ItemListFragment()
            val transaction = fragmentManager.beginTransaction()
            transaction.replace(R.id.container, itemListFragment)
            transaction.commit()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.search, menu)
        if (menu != null) {
            val searchView = menu.findItem(R.id.menu_search).actionView as SearchView
            searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
                override fun onQueryTextSubmit(text: String?): Boolean {
                    Log.d(TAG, "submit text: $text")
                    return false
                }

                override fun onQueryTextChange(text: String?): Boolean {
                    Log.d(TAG, "change text: $text")
                    val itemListFragment = fragmentManager.findFragmentById(R.id.container)
                    if (itemListFragment is ItemListFragment && text != null) {
                        itemListFragment.searchRequest(text)
                    }
                    return false
                }
            })
        }
        return super.onCreateOptionsMenu(menu)
    }
}

参考

Toolbar に 13行で SearchView を実装する

15
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
12