RecyclerView내에서 빠르게 검색하는 기능을 만들고 싶어서 시작.
1. menu 만들기
안드로이드 공식 사이트에서의 코드를 참고
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/search"
android:title="@string/search_title"
android:icon="@drawable/ic_search"
android:showAsAction="collapseActionView|ifRoom"
android:actionViewClass="android.widget.SearchView" />
</menu>
Toolbar에 들어가는 게 SearchView 하나면 상관없는데, 여러 개를 사용할 경우는 위처럼 했을 때 다른 메뉴에 같이 나오게 됨. 내 경우는 옵션 메뉴가 하나 더 있었는데 옵션 메뉴 리스트에 SearchView도 같이 잡힘. 그래서 검색해서 알아본 결과 ifRoom말고 always를 적용
실제 적용 코드
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_search"
android:icon="@drawable/ic_search"
android:title="Search"
android:enabled="true"
android:visible="true"
app:showAsAction="always|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
<group
android:id="@+id/menu_group">
<item
android:id="@+id/menu_account"
android:title="account"
/>
<item
android:id="@+id/menu_logout"
android:title="logout"
/>
</group>
</menu>
2. onCreateOptionMenu 구현
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.options_menu, menu)
return true
}
3. 검색 가능한 구성 만들기
res>xml(폴더 생성)>searchable.xml 생성
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/search_hint" />
4. 위에 만든 xml을 Manifest에 meta-data로 추가
<activity ... >
...
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
5. 앞에어 만든 onCreateOptionsMenu() 메서드에서 setSearchableInfo(SearchableInfo) 를 호출하여 검색 가능한 구성을 SearchView와 연결
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.options_menu, menu)
// Associate searchable configuration with the SearchView
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
(menu.findItem(R.id.search).actionView as SearchView).apply {
setSearchableInfo(searchManager.getSearchableInfo(componentName))
}
return true
}
6. RecyclerView를 구현할 때 만든 Adapter로 가서 Filterable 상속 및 메소드 구현
class AllAccountListAdapter(
val context: Context, accounts: List<AccountData>,
onClick: OnClickInterface,
onLongClick: OnLongClickInterface,
val viewModel: PageViewModel
) :
RecyclerView.Adapter<AllAccountListAdapter.ItemViewHolder>(), Filterable {
//검색
val mDataListAll = ArrayList<AccountData>(accounts)
var mAccounts:MutableList<AccountData> = accounts as MutableList<AccountData>
...
mDataListAll 은 검색용도로 만든 모든 데이터를 가진 ArrayList
mAccounts는 검색된 결과를 담는 List
override fun getFilter(): Filter {
return exampleFilter
}
private val exampleFilter: Filter = object : Filter() {
//Automatic on background thread
override fun performFiltering(constraint: CharSequence): FilterResults {
val filteredList: MutableList<AccountData> = java.util.ArrayList<AccountData>()
if (constraint.isEmpty()) {
filteredList.addAll(mDataListAll)
} else {
val filterPattern = constraint.toString().lowercase(Locale.getDefault()).trim { it <= ' ' }
for (item in mDataListAll) {
//filter 대상 setting
if (item.name.lowercase(Locale.getDefault()).contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
//Automatic on UI thread
@SuppressLint("NotifyDataSetChanged")
override fun publishResults(constraint: CharSequence, results: FilterResults) {
mAccounts.clear()
mAccounts.addAll(results.values as Collection<AccountData>)
notifyDataSetChanged()
}
}
7. onCreateOptionMenu메소드로 다시 가서 필터링이 될 수 있도록 setOnQueryTextListener 추가
나는 Activity에 RecyclerView가 있는 게 아니라 Activity안에 Tablayout을 구현한 상황.
검색이 Activity에서 일어나야하는데 데이터를 보여주는 RecyclerView는 각 Fragment안에 있는 상태라 어떻게 할까 고민하다가 Activity쪽에 FrameLayout을 추가해서 그 안에 RecyclerView를 구현.
검색창에 값이 입력 될 때 마다 FrameLayout을 GONE, VISIBLE처리로 검색 결과 화면을 따로 만들었다.
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
(menu?.findItem(R.id.menu_search)?.actionView as SearchView).apply {
setSearchableInfo(searchManager.getSearchableInfo(componentName))
this.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
//검색(찾기) 버튼을 눌렀을 때 실행
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
//검색창에 값이 입력될 때 마다 실행
override fun onQueryTextChange(newText: String?): Boolean {
if (newText != null) {
if (newText.isNotEmpty()) {
binding.flMainSearchResult.visibility = View.VISIBLE
searchAdapter.filter.filter(newText)
} else {
binding.flMainSearchResult.visibility = View.GONE
}
}
return false
}
})
}
[이슈]
공식 사이트에서 제시한 예제로 했을 경우 옵션 메뉴에 SearchView 타이틀인 search가 뜨고 검색 중에 옵션 메뉴를 누르면 검색 아이콘이 사라지는 에러 발생. 해결책으로 위에서 말한 것 처럼 always로 변경, 그리고 아이콘이 사라지는 부분은 setOnActionExpandListener를 추가 invalidateOptionMenu()를 넣어서 해결하였다.
최종 onCreateOptionsMenu()
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.home_menu, menu) // main_menu 메뉴를 toolbar 메뉴 버튼으로 설정
//검색을 누른 후 옵션 아이콘을 누른 후 검색이 끝나면 검색 아이콘이 사라져서 넣음
menu?.findItem(R.id.menu_search)
?.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(p0: MenuItem?): Boolean {
return true
}
override fun onMenuItemActionCollapse(p0: MenuItem?): Boolean {
invalidateOptionsMenu()
return true
}
})
// Associate searchable configuration with the SearchView
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
(menu?.findItem(R.id.menu_search)?.actionView as SearchView).apply {
setSearchableInfo(searchManager.getSearchableInfo(componentName))
this.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
if (newText != null) {
if (newText.isNotEmpty()) {
binding.flMainSearchResult.visibility = View.VISIBLE
searchAdapter.filter.filter(newText)
} else {
binding.flMainSearchResult.visibility = View.GONE
}
}
return false
}
})
}
return true
}
'개발 공부 > Android' 카테고리의 다른 글
[Android Kotlin] Retrofit String으로 응답 값 받는 방법 (0) | 2022.08.23 |
---|---|
[Android] SHA1 확인하는 방법 (0) | 2022.08.22 |
[Androit Kotlin] 국가 코드 가져오기 (0) | 2022.08.18 |
[Android] TextView 줄임 표시 (ellipsize) (0) | 2022.08.11 |
[Android Kotlin]RecyclerView Adapter 내외부 ClickListener 구현하기(onClick, LongClick) (0) | 2022.08.10 |