CodeNewbie
  • [Android] 개발공부 42일차 TIL - RecyclerView Filter(검색)
    2024년 01월 18일 15시 53분 20초에 업로드 된 글입니다.
    작성자: 짧은 코딩끈

    일자 : 2024.01.18

     

     

    📝TIL 정리

     

    💬RecyclerView Filter(검색)


    RecyclerView에서 검색기능을 구현하고자 했다

    EditText가 아닌

    SearchView를 사용하게 되어(XML, 레이아웃)

    OnQueryTextListener를 통해 실시간 text를 확인할 수 있게 하였다.

     

    SearchView

    <SearchView
            android:id="@+id/sv_contactlist_search"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:queryBackground="@drawable/shape_search_background"
            android:queryHint="이름 또는 번호로 검색해주세요"
            app:iconifiedByDefault="true"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

     

    SearchView 속성은 아래 사이트에서~

    https://landroid.tistory.com/5

     

     

    건들어야하는곳은 2곳이다

    1. Adapter (Filterable 인터페이스를 상속)

    2. RecylerView를 구현하고, SearchView를 만든 레이아웃을 가진  Activity 또는 Fragment

     

     

    먼저 Fragment (이번에는 프래그먼트에 구현했다)

    // 목록 검색 기능
        var searchViewTextListener: SearchView.OnQueryTextListener =
            object : SearchView.OnQueryTextListener, androidx.appcompat.widget.SearchView.OnQueryTextListener {
                //검색버튼 입력시 호출, 검색버튼이 없으므로 사용하지 않음
                override fun onQueryTextSubmit(s: String): Boolean {
                    return false
                }
                //텍스트 입력/수정시에 호출
                override fun onQueryTextChange(s: String): Boolean {
                    adapter.filter.filter(s)
                    return false
                }
            }
        svContactlistSearch.setOnQueryTextListener(searchViewTextListener)

    svContactlistSearch가 searchview의 id값이며 , 뷰바인딩으로 바로 참고했다.

    adapter.filter.filter(s)에서 중간 filter는 getFilter()이다 (어댑터에서 Filterable 상속하면서 구현한 멤버, 오버라이드된 함수)

     

    Adpater

    class ContactAdapter(val dataList : ArrayList<Contact>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() , Filterable{
    ...
    
    interface ItemClick {
            fun onClick(view: View?, data: Contact)
        }
        interface ItemLongClick {
            fun onLongClick(view : View, position : Int)
            
     ...
     
      // 리사이클러뷰 검색 기능 (필터)
        private var filteredList: ArrayList<Contact> = dataList
        override fun getFilter(): Filter {
            return object : Filter() {
                override fun performFiltering(constraint: CharSequence?): FilterResults {
                    val charString = constraint.toString()
                    filteredList = if(charString.isBlank()) {
                        dataList
                    } else {
                        val filteredList = dataList.filter {
                            it is Contact.Person && (it.name.contains(charString, true)
                                    || it.phoneNumber.contains(charString, true))
                        }
                        filteredList as ArrayList<Contact>
                    }
                    val filterResults = FilterResults()
                    filterResults.values = filteredList
                    return filterResults
                }
    
                @SuppressLint("NotifyDataSetChanged")
                override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                    filteredList = results?.values as ArrayList<Contact>
                    notifyDataSetChanged()
                }
            }
        }

    Adapter가 Filterable을 상속하게 되면

    필수 구현해야할 함수 (오버라이드 해야할)는 한가지, fun getFilter이다.

    그리고, 블로그 참고시 어떤곳은 완전히 원본데이터배열을 복제한 데이터 배열(Filter용)을 사용하도록 하였는데

    (https://greensky0026.tistory.com/226#--%--SerachView%EC%-D%--%C-%A-OnQueryTextListener)

     

    그렇게 하게되면 구현해놓은 RecyclerView 목록 추가, 삭제 그리고 검색기능 자체에도 영향을 주게되어서(원본데이터가 사라지지만, 복제 데이터는 그대로 이기 때문 , 거기다가 메모리영역이 달리 저장되면서 영역도 차지하기떄문?)

     

    최종적으로 위 코드를 작성하는데 참고한 사이트는 아래이다.

    https://velog.io/@simsubeen/Android-Kotlin-SearchView%EC%99%80-Filter%EB%A1%9C-RecyclerView-%EA%B2%80%EC%83%89%ED%95%98%EA%B8%B0

     

    잘못된 경우

     

    우리가 데이터를 주고 받을 때

    리사이클러뷰의 위치값을 보내준다

    클랙스에서 생성된 더미데이터가 만약 20개라면

    0번~19번까지의 위치값이 있고

    그 위치값을 전달하게 되었을 때는

    원본 데이터의 위치값과

    filter에 의해 나오게되는 목록 위치값과 연동이 되지 않는다.

     

    fun onClick(view: View?, position: Int)  ->  fun onClick(view: View?, data: Contact)

    처럼 position값을 대신 클래스 객체 자체의 자료형을 전달하도록 하고

     

    이후에 그 위치값을 받는 곳에서 

    IndextOf를 통해서 위치를 찾도록 하면

    요렇게 필터로 인해 목록이 달라지더라도

    클릭했을 때 그 값의 위치를 정확히 인식한다

    물론 중복데이터는 중복이라서 0~19번중에서 같은 데이터가 3, 5번이라면 

    3번째 5번째를 서로 클릭해도 3번째 값만 나오게 된다. (indexOf 때문에.)

    그렇기에 더미데이터를 만들게 된곳인

    data class에서 중복데이터를 식별할 수 있는 값을 추가해서 디테일하게 관리할 수 있도록 해야한다.

     

    💡금일 회고

    상태:😁

    회고: 

    검색기능을 드디어 구현했다

    물론 완벽하지 않지만 어느정도 검색도 되었는데당연히 끝인줄 알았던 상황에서 필터된 목록 위치값이 클릭했을 때그 위치가 아닌 기존의 데이터 위치값을 보내어서 눌렀을때 상세페이지에서 달랐다..

     

    결국 다시 튜터님을 찾아뵙고 확인한결과 위치값이 아닌 데이터객체를 보내고 대신 그 데이터를 받아서 정보를 띄우는 곳에서 인덱스를 통해 위치값을 찾는 식으로 했더니 적용되었다.

     

    오늘은 그래도 뭔가 잘 해결되어서 다행이었다

     

    슬슬 전체적인 앱 모형이 나오고 최종적으로 정리하고 발표준비하면 팀과제는 끝이다 

     

    내일만 더 열심히 하자~

     

     

    댓글