안드로이드/androidstudio

[Android] SearchView활용 recyclerview 검색

안드로이드멩이 2022. 11. 13. 00:29

  이번 포스팅에서는 recycler view를 사용한 리스트에서 특정 항목을 검색하여 해당 값들만 다시 출력하게 하는 검색기능을 구현하는 방법을 포스팅하겠습니다.

 

 먼저  xml에 SerachView를 추가해줍니다. serachview에서 설정할수있는 값들은 아이콘, 힌트 등이있으며 별도로 포스팅하겠습니다.

<androidx.appcompat.widget.SearchView
    android:id="@+id/search"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:iconifiedByDefault='false'
    app:queryBackground="@color/gray"
    app:searchIcon="@drawable/ic_baseline_search_24"
    app:closeIcon="@drawable/ic_baseline_clear_24"
    app:queryHint="해수욕장 검색"
    android:maxWidth="200dp"
    app:layout_constraintTop_toBottomOf="@id/title"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    />

 xml에 추가하였으면 이제 activity에 동작부를 추가하겠습니다. 저는 binding을 사용하기에 binding에 아까 추가한 searchView를 이어서 입력에 대한 리스너를 정의합니다. 그럼 2개의 함수를 override하게 되는데 상단의 onQueryTextSubmit은 최종 입력 후 엔터를 눌러야 반영되는 함수이며

onQueryTextChange는 입력이 실시간 반영되는 함수입니다. 

이 입력값을 adapter에 구현한 filter를 통해 구현하였습니다.

binding.search.setOnQueryTextListener(object : SearchView.OnQueryTextListener,
    androidx.appcompat.widget.SearchView.OnQueryTextListener {
    override fun onQueryTextSubmit(query: String?): Boolean {
        //검색 버튼 클릭되는 순간
        return false
    }
    //검색어 입력중
    override fun onQueryTextChange(newText: String?): Boolean {
        findBeach = newText.toString()
        communicationAdapter.filter.filter(newText)
        Log.e(TAG,"검색어: $findBeach")
        return false
    }
})

recyclerView에 대한 adapter class 에서 Filterable또한 추가로 상속받습니다.

 그럼 아래 getFirlter를 override해야하며 검색 결과리스트를 따로 연결하기에 기존 adpater에서 받는 리스트에 필터링할 리스트를 별도로 다시 정의하여 생성자 값을 넣어줍니다.

init {
    filteredBeach.addAll(beaches)
}

 inner class 를 통해 검색에 대한 기능을 구현합니다. 그리고 공백 혹은 글자수의 경우에 맞춰 필터링할 리스트에 검색어에 맞춰 값을 추가해줍니다.

//검색에 대한 필터링 함수
override fun getFilter(): Filter {
    return itemFiler
}
inner class ItemFilter: Filter(){
    override fun performFiltering(constraint: CharSequence?): FilterResults {
        val filterString = constraint.toString()
        val results = FilterResults()
        Log.d(TAG, "charSequence : $constraint")

        //검색이 필요없을 경우를 위해 원본 배열을 복제
        val filteredList: ArrayList<CombineBeachInfo> = ArrayList<CombineBeachInfo>()
        //공백제외 아무런 값이 없을 경우 -> 원본 배열
        if (filterString.trim { it <= ' ' }.isEmpty()) {
            results.values = beaches
            results.count = beaches.size

            return results
            //공백제외 2글자 이인 경우 -> 이름으로만 검색
        } else if (filterString.trim { it <= ' ' }.length <= 2) {
            for (i in beaches) {
                if (i.poiNm.contains(filterString)) {
                    filteredList.add(i)
                }
            }
        }
        results.values = filteredList
        results.count = filteredList.size

        return results
    }
    @SuppressLint("NotifyDataSetChanged")
    override fun publishResults(constraint: CharSequence?, results: FilterResults) {
        filteredBeach.clear()
        filteredBeach.addAll(results.values as ArrayList<CombineBeachInfo>)
        notifyDataSetChanged()
    }
}

publishResults에서 출력할 리스트의 값을 다 지우고 검색어에 해당되는 리스트를 다시 추가하여 변경하여줍니다.