(Kotlin) SearchView를 이용한 RecyclerView filter 사용하기
- 안드로이드 개발/Kotlin으로 어플 만들기
- 2019. 8. 11. 20:02

이번 포스팅에서는 SearchView를 이용해서 RecyclerView의 데이터들을 검색하는 기능을 추가할 계획입니다.
우선 결과물부터 보겠습니다. 결과물이 굉장히 만족스럽네요.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
이전 포스팅까지 만든 코드에서는 filter를 사용하려고 하니 굉장히 복잡하네요...
또다시 코드를 전면 수정했습니다. ㅠㅠ
코드 중간중간에 설명이 다 있습니다. 이대로 복! 붙! 하시면 됩니다.
1. Menu폴더 만들어주기
툴바의 검색 아이콘을 눌러 검색할 수 있도록 menu 폴더를 만들어 주겠습니다.
res 우클릭 → New 클릭 → Android Resource Directory를 눌러줍니다.

New Resource Directory 창이 뜨면,
Resource type을 menu로 바꿔주고 OK를 눌러주세요.

2. menu (menu_search.xml)
menu폴더 안에 menu_search.xml파일을 하나 만들어줍니다.
<?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">
<!-- hide share button by default -->
<item
android:id="@+id/menu_action_search"
android:icon="@drawable/ic_search"
android:title="Search"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>
ic_search는 돋보기 아이콘인데, material icons 홈페이지에서 다운로드하여서 drawable 폴더에 넣어주세요.
링크는 아래 참고하세요.
https://material.io/resources/icons/?style=baseline
Resources
Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.
material.io
3. Kotlin (SearchAdapter.kt)
class SearchAdapter(private val context: Context, private val excelList: MutableList<SearchData>, private val listener: ItemClickListener) : RecyclerView.Adapter<SearchAdapter.SearchViewHolder>(), Filterable{
//검색기능을 위해서 별도의 List를 하나더 만들어주겠습니다.
private var excelSearchList: List<SearchData>? = null
//이너 클래스
inner class SearchViewHolder(view: View) : RecyclerView.ViewHolder(view){
//TextView 선언
val info: TextView
val quiz: TextView
init{
info = view.findViewById(R.id.search_info)
quiz = view.findViewById(R.id.search_quiz)
//온클릭리스너는 여기에!
view.setOnClickListener{
//여기서 !!의 의미는 nullable이면 오류가 발생하게 해줍니다. (?는 null)
listener.onItemClicked(excelSearchList!![adapterPosition])
}
}
}
//초기화 구문 init
init {
this.excelSearchList = excelList
}
//뷰홀더 생성
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
//리스트 아이템 인플레이터
val view = LayoutInflater.from(context).inflate(R.layout.search_list_item, parent, false)
return SearchViewHolder(view)
}
//온바인드뷰홀더 생성
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: SearchViewHolder, position: Int){
val excel = excelSearchList!![position]
//TextView에 excel 데이터 입력
holder.info.text = "#"+excel.fullname + " #"+excel.cate1+"("+excel.cate2+")" + " #"+excel.source
holder.quiz.text = excel.quiz.replace(".(?!$)".toRegex(), "$0\u200b")
}
//item 사이즈
override fun getItemCount(): Int = excelSearchList!!.size
//필터를 위한 코드
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(charSequence: CharSequence): FilterResults {
val charString = charSequence.toString()
if (charString.isEmpty()) {
excelSearchList = excelList
} else {
val filteredList = ArrayList<SearchData>()
//이부분에서 원하는 데이터를 검색할 수 있음
for (row in excelList) {
if (row.fullname.toLowerCase().contains(charString.toLowerCase()) || row.quiz.toLowerCase().contains(charString.toLowerCase())
|| row.cate2.toLowerCase().contains(charString.toLowerCase()) || row.source.toLowerCase().contains(charString.toLowerCase())) {
filteredList.add(row)
}
}
excelSearchList = filteredList
}
val filterResults = FilterResults()
filterResults.values = excelSearchList
return filterResults
}
override fun publishResults(charSequence: CharSequence, filterResults: FilterResults) {
excelSearchList = filterResults.values as ArrayList<SearchData>
notifyDataSetChanged()
}
}
}
interface ItemClickListener {
fun onItemClicked(item : SearchData)
}
}
자세히 확인해야 할 코드들..
- Filterable
- excelSearchList (이전에 excelList 부분을 바꿔줘야 합니다.)
- private var excelsearchList: List? = null
- override fun getFilter(): Filter {...}
'필터를 위한 코드'에서 빨간 줄 그이는 부분은 List의 이름이 다르거나 하는 경우일 거예요.
본인의 코드에 맞게 바꿔주세요.
2. Kotlin(SearchActivity.kt)
여기도 이대로 복!붙!
class SearchActivity : AppCompatActivity(),SearchAdapter.ItemClickListener {
private var recyclerView: RecyclerView? = null
private var itemlist: MutableList<SearchData> = mutableListOf()
private var mAdapter: SearchAdapter? = null
private var searchView: SearchView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.search)
val toolbar = findViewById(R.id.toolbar) as Toolbar
setSupportActionBar(toolbar)
val ab = supportActionBar!!
ab.setDisplayShowTitleEnabled(false)
ab.setDisplayHomeAsUpEnabled(true)
//정의
recyclerView = findViewById(R.id.search_recyclerview)
itemlist = ArrayList()
mAdapter = SearchAdapter(this, itemlist as ArrayList<SearchData>, this)
val mLayoutManager = LinearLayoutManager(applicationContext)
recyclerView!!.layoutManager = mLayoutManager
recyclerView!!.itemAnimator = DefaultItemAnimator()
recyclerView!!.adapter = mAdapter
//엑셀 불러오기
readExcelFileFromAssets()
}
//엑셀에서 가져온 데이터를 MutableList인 itemList에 추가해주고 Reutrn
private fun readExcelFileFromAssets(): MutableList<SearchData> {
try {
val myInput: InputStream
// assetManager 초기 설정
val assetManager = assets
// 엑셀 시트 열기
myInput = assetManager.open("hb.xls")
// POI File System 객체 만들기
val myFileSystem = POIFSFileSystem(myInput)
//워크 북
val myWorkBook = HSSFWorkbook(myFileSystem)
// 워크북에서 시트 가져오기
val sheet = myWorkBook.getSheetAt(0)
//행을 반복할 변수 만들어주기
val rowIter = sheet.rowIterator()
//행 넘버 변수 만들기
var rowno = 0
//행 반복문
while (rowIter.hasNext()) {
val myRow = rowIter.next() as HSSFRow
if (rowno != 0) {
//열을 반복할 변수 만들어주기
val cellIter = myRow.cellIterator()
//열 넘버 변수 만들기
var colno = 0
var item_no = ""
var year= ""
var exam = ""
var fullname = ""
var cate1 = ""
var cate2 = ""
var quiz = ""
var answer = ""
var source = ""
var explain = ""
var link = ""
//열 반복문
while (cellIter.hasNext()) {
val myCell = cellIter.next() as HSSFCell
if (colno === 0) {//no,
item_no = myCell.toString()
} else if (colno === 1) {//year,
year = myCell.toString()
} else if (colno === 2) {//exam,
exam = myCell.toString()
} else if (colno === 3) {//fullname,
fullname = myCell.toString()
} else if (colno === 4) {//cate1
cate1 = myCell.toString()
} else if (colno === 5) {//cate2
cate2 = myCell.toString()
} else if (colno === 6) {//quiz
quiz = myCell.toString()
} else if (colno === 7) {//answer
answer = myCell.toString()
} else if (colno === 8) {//source
source = myCell.toString()
} else if (colno === 9) {//explain
explain = myCell.toString()
} else if (colno === 10) {//link
link = myCell.toString()
}
colno++
}
//4,8번째 열을 Mutablelist에 추가
itemlist.add(SearchData(item_no, year, exam, fullname, cate1, cate2, quiz, answer, source, explain, link))
}
rowno++
}
} catch (e: Exception) {
Toast.makeText(this, "에러 발생", Toast.LENGTH_LONG).show()
}
return itemlist
}
//아이템 클릭시 TODO
override fun onItemClicked(item: SearchData) {
//여기에 원하는 코드 입력!!
val Intent = Intent(this, Search_Detail::class.java)
Intent.putExtra("fullname", item.fullname)
Intent.putExtra("cate1", item.cate1)
Intent.putExtra("cate2", item.cate2)
Intent.putExtra("quiz", item.quiz)
Intent.putExtra("answer", item.answer)
Intent.putExtra("source", item.source)
Intent.putExtra("explain", item.explain)
Intent.putExtra("link", item.link)
startActivity(Intent)
}
//우측상단메뉴
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.menu_search, menu)
var searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
searchView = menu.findItem(R.id.menu_action_search).actionView as SearchView
searchView!!.setSearchableInfo(searchManager.getSearchableInfo(componentName))
searchView!!.maxWidth = Integer.MAX_VALUE
searchView!!.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
mAdapter!!.filter.filter(query)
return false
}
override fun onQueryTextChange(query: String?): Boolean {
mAdapter!!.filter.filter(query)
return false
}
})
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
return if(id== R.id.menu_action_search) {
true
} else if(id==android.R.id.home){
finish()
true
} else super.onOptionsItemSelected(item)
}
override fun onBackPressed() {
// close search view on back button pressed
if (!searchView!!.isIconified) {
searchView!!.isIconified = true
return
}
super.onBackPressed()
}
}
끝.
'안드로이드 개발 > Kotlin으로 어플 만들기' 카테고리의 다른 글
| (Kotlin) Card Flip Animation으로 Flashcard 만들기 (0) | 2019.08.15 |
|---|---|
| (Kotlin) Flip View(플립뷰)로 플래시카드 만들기 (0) | 2019.08.12 |
| (Kotlin) RecyclerView, OnclickListener 사용하기 (3) | 2019.08.10 |
| (Kotlin) Poi를 사용하여 엑셀 데이터 가져오기(2) (2) | 2019.08.07 |
| (Kotlin) Poi를 사용하여 엑셀 데이터 가져오기(1) (5) | 2019.08.06 |