[안드로이드] 파이어베이스 파이어스토어 검색 기능 구현하기

동현·2020년 9월 9일
2
post-thumbnail

전에 데이터를 불러와서 리사이클러뷰에 나타내는 글을 올린 적이 있다.

[안드로이드] 파이어베이스 파이어스토어의 데이터를 리사이클러뷰에 나타내기

여기에 한 번 검색 기능을 구현해 보았다. 위 글에서 만든 프로젝트에 이어서 진행할 것이므로 프로젝트 생성, 파이어스토어 연결의 과정은 생략하도록 하겠다.

1. 검색바 추가

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Spinner
            android:id="@+id/spinner"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:entries="@array/option" />

        <EditText
            android:id="@+id/searchWord"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="검색어를 입력해주세요."
            android:inputType="text" />

        <Button
            android:id="@+id/searchBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="검색" />

    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

activity_main.xml

이름을 검색할지 번호를 검색할지 옵션을 선택할 수 있는 방법을 찾다가 스피너를 통해 옵션 선택을 구현하기로 하였다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="option">
        <item>이름</item>
        <item>번호</item>
    </string-array>
</resources>

array.xml

스피너에 들어갈 내용을 구현하기 위해 res-values에 array.xml을 생성해준다. item 태그 안에 들어갈 내용을 써주면 된다.

<Spinner
            android:id="@+id/spinner"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:entries="@array/option" />

activity_main.xml

위와 같이 entries에 @array/array의 name을 넣어주면 된다.
검색어를 쓸 칸은 EditText으로, 검색 버튼은 Button으로 구현하였다.

2. 스피너에 기능 추가

일단 스피너에 기능을 넣어주도록 하자.

        // 검색 옵션 변수
        var searchOption = "name"

        // 스피너 옵션에 따른 동작
        spinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
            override fun onNothingSelected(parent: AdapterView<*>?) {
            }

            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                when (spinner.getItemAtPosition(position)) {
                    "이름" -> {
                        searchOption = "name"
                    }
                    "번호" -> {
                        searchOption = "phoneNumber"
                    }
                }
            }
        }

MainActivity.kt

검색 옵션 변수를 만들어주고 스피너가 선택한 항목에 따라 검색 옵션이 달라지도록 하였다.

3. 검색 기능 구현

검색 기능을 구현할 땐 주로 LIKE절을 사용하였으나, 파이어스토어에서는 안타깝게도 LIKE절을 쓰지 못한다. 그러던 중 String 클래스의 contains 메소드를 사용하기로 하였다.

위는 C#에 대한 문서지만 자바의 contains 메소드도 이와 똑같은 기능을 가지고 있고 설명이 더 잘 되어있다고 생각해 가져왔다. 자바의 contains 메소드를 볼려면 왼쪽 링크를 클릭하면 된다.

        // 파이어스토어에서 데이터를 불러와서 검색어가 있는지 판단
        fun search(serachWord : String, option : String) {
            firestore?.collection("telephoneBook")?.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
                // ArrayList 비워줌
                telephoneBook.clear()

                for (snapshot in querySnapshot!!.documents) {
                    if (snapshot.getString(option)!!.contains(serachWord)) {
                        var item = snapshot.toObject(Person::class.java)
                        telephoneBook.add(item!!)
                    }
                }
                notifyDataSetChanged()
            }
        }
        
MainActivity.kt

이를 이용해서 검색 함수를 짜보았다. 검색 옵션(name or phoneNumber)에 따라 해당 필드에 검색어(searchWord)가 있을 시 데이터를 불러와 item의 형태로 저장해 리사이클러뷰에 추가해주는 형태이다.

        // 검색 옵션에 따라 검색
        searchBtn.setOnClickListener {
            (recyclerview.adapter as RecyclerViewAdapter).search(searchWord.text.toString(), searchOption)
        }
        
MainActivity.kt

이제 앞서 activity_main.xml에 추가한 버튼에 해당 함수를 실행할 수 있도록 OnClickListener를 추가해주면 된다.

package com.example.samplerecyclerview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.firestore.FirebaseFirestore
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.item.view.*

class MainActivity : AppCompatActivity() {
    var firestore : FirebaseFirestore? = null

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

        // 파이어스토어 인스턴스 초기화
        firestore = FirebaseFirestore.getInstance()

        recyclerview.adapter = RecyclerViewAdapter()
        recyclerview.layoutManager = LinearLayoutManager(this)

        // 검색 옵션 변수
        var searchOption = "name"

        // 스피너 옵션에 따른 동작
        spinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
            override fun onNothingSelected(parent: AdapterView<*>?) {
            }

            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                when (spinner.getItemAtPosition(position)) {
                    "이름" -> {
                        searchOption = "name"
                    }
                    "번호" -> {
                        searchOption = "phoneNumber"
                    }
                }
            }
        }

        // 검색 옵션에 따라 검색
        searchBtn.setOnClickListener {
            (recyclerview.adapter as RecyclerViewAdapter).search(searchWord.text.toString(), searchOption)
        }
    }

    inner class RecyclerViewAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        // Person 클래스 ArrayList 생성성
       var telephoneBook : ArrayList<Person> = arrayListOf()

        init {  // telephoneBook의 문서를 불러온 뒤 Person으로 변환해 ArrayList에 담음
            firestore?.collection("telephoneBook")?.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
                // ArrayList 비워줌
                telephoneBook.clear()

                for (snapshot in querySnapshot!!.documents) {
                    var item = snapshot.toObject(Person::class.java)
                    telephoneBook.add(item!!)
                }
                notifyDataSetChanged()
            }
        }

        // xml파일을 inflate하여 ViewHolder를 생성
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            var view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
            return ViewHolder(view)
        }

        inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        }

        // onCreateViewHolder에서 만든 view와 실제 데이터를 연결
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            var viewHolder = (holder as ViewHolder).itemView

            viewHolder.name.text = telephoneBook[position].name
            viewHolder.phoneNumber.text = telephoneBook[position].phoneNumber
        }

        // 리사이클러뷰의 아이템 총 개수 반환
        override fun getItemCount(): Int {
            return telephoneBook.size
        }

        // 파이어스토어에서 데이터를 불러와서 검색어가 있는지 판단
        fun search(searchWord : String, option : String) {
            firestore?.collection("telephoneBook")?.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
                // ArrayList 비워줌
                telephoneBook.clear()

                for (snapshot in querySnapshot!!.documents) {
                    if (snapshot.getString(option)!!.contains(searchWord)) {
                        var item = snapshot.toObject(Person::class.java)
                        telephoneBook.add(item!!)
                    }
                }
                notifyDataSetChanged()
            }
        }
    }
}

MainActivity.kt

전체 코드는 다음과 같다.

4. 실행 결과

실행을 해보면 다음과 같이 정상적으로 작동한다.

5. 참조

Microsoft docs, "String.Contains 메서드 (System)", https://docs.microsoft.com/ko-kr/dotnet/api/system.string.contains?view=netcore-3.1
자바에 대한 글은 아니지만 동일한 기능을 수행하므로 가지고 왔다.

profile
https://github.com/DongChyeon

1개의 댓글

comment-user-thumbnail
2020년 11월 12일

잘 봤습니다.

답글 달기