안드로이드

[Android/Kotlin] 파이어베이스(DB기능) 사용해보기

핸디_ 2021. 8. 29. 22:02

만약 내가 만든 앱화면에서 입력한 글자들을 다른 사람과 함께 보고싶다면..?

-> 웹서버를 만들어야 한다. 

-> 웹서버를 만드는 방법도 알아야 하고 운영과정에서 비용이 많이 들 수 있다.

 

파이어베이스는 데이터의 양이 많지 않다면 무료로 웹서버를 운영할 수 있도록 도와주고

푸시 메시지를 보낼 수 있도록 해준다!

Firebase의 다양한 기능을 알아보려면 여길 참고해보자

 

영화앱의 한줄평을 작성해서 파이어베이스에 저장하고 리사이클러뷰에 뿌려주는 걸 연습해보자!

 

1. 안드로이드와 파이어베이스를 연동

 

www.console.firebase.com  으로 이동해서 새로운 프로젝트를 생성한다. (FirebasePractice) 

해당 프로젝트를 클릭하고 왼쪽 메뉴 중 Cloud Firestore메뉴를 눌러 데이터베이스 설정을 한다.

데이터베이스 만들기 -> 테스트모드로 시작 -> 사용설정 클릭

이렇게 하면 데이터베이스 화면에서는 데이터베이스에 저장된 데이터를 보거나 사용량을 살펴 볼 수 있다.

 

안드로이드 스튜디오로 돌아와

[Tools->Firebase->Realtime Database->Save and retrieve data] -> Connect to Firebase]

->[Add the Realtime Database to your App] 을 누르고 웹 화면 왼쪽 메뉴 중 Realtime Database 클릭!

데이터베이스 만들기-> 사용설정

[규칙] 탭으로 들어가서 .read와 .write속성값을 모두 true로 변경 -> 게시

 

권한 규칙이 변경되어서 앱에서 읽기 또는 쓰기 가능 -> 데이터베이스에 데이터를 쓰거나 읽을 수 있다!

 

2. activity_main.xml화면, movie_comment_item.xml 작성하기

 activity_main.xml
 리사이클러뷰 안에 들어가 아이템 레이아웃

3. 리사이클러뷰에 띄워줄 data class Moviecomment.kt

package com.example.firebasepractice

import com.google.firebase.database.Exclude

data class MovieComment(
    var objectId: String,
    var author:String,
    var date:String,
    var rating: Long,
    var contents:String,
    var recommendCount:Long,
    var timestamp:Long=0
) {

    @Exclude
    fun toMap(): HashMap<String, Any> {
        val result: HashMap<String, Any> = HashMap()
        result["objectId"] = objectId
        result["author"] = author
        result["date"] = date
        result["rating"]=rating
        result["contents"]=contents
        result["recommendCount"]=recommendCount
        result["timestamp"]=timestamp

        return result

    }
}

 toMap() :파이어베이스에 데이터를 저장할 때 Map 자료형으로 저장할 수 있기 때문에 속성들을 HashMap 객체에 넣어서 반환

 

2. MovieCommentAdapter.kt 

class MovieCommentAdapter : RecyclerView.Adapter<MovieCommentAdapter.ViewHolder>() {
    var items = ArrayList<MovieComment>()


    lateinit var listener: OnMovieCommentClickListener

    inner class ViewHolder(private val binding: MovieCommentItemBinding) : RecyclerView.ViewHolder(binding.root) {

        init { 
            itemView.setOnClickListener {
                listener?.onItemClick(this, itemView, adapterPosition)
            }
        }

        fun bind(item: MovieComment) {
            binding.idTextView.text = item.author
            binding.dateTextView.text = item.date
            binding.ratingBar.rating = item.rating.toFloat()?:0.0f
            binding.contentsTextView.text = item.contents
            binding.recommendCountTextView.text = item.recommendCount.toString()
        }

    }

    override fun getItemCount() = items.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding=MovieCommentItemBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(items[position])
    }



}

 

3. MainActivity에서 어댑터 달아주기

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var databaseRef:DatabaseReference
    private lateinit var adapter:MovieCommentAdapter
    var inputDateFormat= SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    var outputDateFormat=SimpleDateFormat("yyyy.MM.dd")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding=ActivityMainBinding.inflate(layoutInflater)

        setContentView(binding.root)

         val layoutManager= LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
        layoutManager.setReverseLayout(true)
        layoutManager.setStackFromEnd(true)
        binding.recyclerView.layoutManager=layoutManager

        adapter=MovieCommentAdapter()

        binding.recyclerView.adapter=adapter

        adapter.listener=object:OnMovieCommentClickListener{
            override fun onItemClick(holder: MovieCommentAdapter.ViewHolder?, view: View?, position: Int) {
                val name=adapter.items[position]
                Toast.makeText(applicationContext,"아이템선택됨${name}",Toast.LENGTH_LONG).show()
            }

       }

   

    }

 

4. MainActivity에서 Firebase에 한줄평 저장하는 코드 작성

class MainActivity : AppCompatActivity() {
	   private lateinit var databaseRef:DatabaseReference
       
       override fun onCreate(savedInstanceState: Bundle?) {
			binding.saveButton.setOnClickListener { //여기부터 추가
            val author=binding.editTextId.text.toString()
            val rating=binding.ratingBar.rating.toLong()
            val contents=binding.editTextPlot.text.toString()
            val recommendCount=0L

            saveComment(author,rating,contents,recommendCount)
        }

        databaseRef= FirebaseDatabase.getInstance("https://fir-practice-acb89-default-rtdb.firebaseio.com/").reference
    }
    }
  • 파이어베이스 데이터베이스: DatabaseReference 자료형으로 참조, 
  • databaseRef 변수에 firebase데이터베이스를 접근할 수 있는 DatabaseReference 객체를 참조한다.

✔ SaveComment메서드 : 파이어베이스에 데이터 저장

 saveComment(author:String, rating: Long, contents:String, recommendCount:Long){
        val key:String?=databaseRef.child("comments").push().key
        val comment=MovieComment(key.toString(),author,"",rating,contents,recommendCount)
        val commentValues:HashMap<String,Any> = comment.toMap()
        commentValues["timestamp"]=ServerValue.TIMESTAMP

        val childUpdates:MutableMap<String,Any> =HashMap()
        childUpdates["/comments/$key"]=commentValues

        databaseRef.updateChildren(childUpdates)

        //db에서 가져오는 코드
        databaseRef.orderByKey().limitToFirst(10).addValueEventListener(object:ValueEventListener{
            override fun onDataChange(datasnapshot: DataSnapshot) {
                loadCommentList(datasnapshot)
            }

            override fun onCancelled(databaseError: DatabaseError) {
                println("loadItem:onCancelled: ${databaseError.toException()}")
            }
        })

    }
  • 파이어베이스 데이터베이스는 객체 단위로 저장하며 계층 구조로 만들어져 있다.
  • databaseRef.child 메서드를 통해 하위 객체/속성 참조 , push: 객체추가 -> 객체를 먼저 추가하고 키를 가져오면 키를 객체의 속성으로도 저장 가능
  • comment -> MovieComment 객체 -> toMap()메서드로 HashMap 자료형으로 된 객체로 만듦
  • updateChildren: DataseReference 객체로 참조하는 정보 업데이트하기 위해 호출
  • -> 파라미터는 HashMap 객체 ( 이 객체 안에는 객체의 경로+값이 들어감)
  • (객체의 경로를 "/comments/${key}"로 하여 앞에서 만든 객체를 가리키도록 하고 그 값으로 MovieComent객체의 속성들을 HashMap으로 변환했던 객체 commentValues를 넣어줌)

 

--> Firebase Realtime Database 에 들어가면 데이터가 들어간 것을 볼 수 있다

 

 

5. Firebase에 저장된 데이터 가져와서 리사이클러뷰에 띄우기

(saveComment)아래에 다음 코드 추가

 databaseRef.orderByKey().limitToFirst(10).addValueEventListener(object:ValueEventListener{
            override fun onDataChange(datasnapshot: DataSnapshot) {
                loadCommentList(datasnapshot)
            }

            override fun onCancelled(databaseError: DatabaseError) {
                println("loadItem:onCancelled: ${databaseError.toException()}")
            }
        })
  • DataSanpshot 객체: 파이어베이스로부터 조회되는 객체에 접근할 수 있도록 해준다.

 

 

loadCommentList 메서드: 조회된 모든 객체들을 리싸이클러뷰에 추가

   fun loadCommentList(dataSnapshot: DataSnapshot){
        val collectionIterator=dataSnapshot.children.iterator()
        if(collectionIterator.hasNext()){
            adapter.items.clear()

            val comments=collectionIterator.next()
            val itemsIterator=comments.children.iterator() //각 comment컬렉션 객체 가져오기 위해
            while(itemsIterator.hasNext()){
                val currentItem=itemsIterator.next()

                val key = currentItem.key
                val map=currentItem.getValue() as HashMap<String,Any>
                //getValue()를 호출하여 HashMap자료형으로 된 객체 꺼냄
                
                val objectId=map.get("objectId") as String
                val author=map.get("author") as String
                val contents=map.get("contents") as String
                val rating=map.get("rating") as Long
                val recommendCount=map.get("recommendCount") as Long
                val curDate= Date(map.get("timestamp") as Long)
                val curTime=convertCommentTime(curDate)
                var commentTime=""
                if(curTime!=null){
                    commentTime=curTime
                }
                
                
                //꺼낸 속성들을 MovieComment객체로 만든 후 어댑터에 추가

                adapter.items.add(MovieComment(objectId,author,commentTime,rating,contents,recommendCount))
            }

            adapter.notifyDataSetChanged() //리싸이클러뷰가 갱신되면서 추가한 아이템 보여줌
        }
    }

+ 내용을 입력하려고 할때 키보드가 올라오면서 layout의 editText부분이 가려지는 문제가 있었는데

https://lakue.tistory.com/55

 

[Android/안드로이드] 키보드로 액티비티 화면 조정 adjustPan

키보드를 올렸을 때 안에 있는 레이아웃이 전체적으로 움직이거나 고정시키고 싶을 때가 있습니다. 이번 포스팅에서는 키보드의 영향에 따라 레이아웃을 조정하는 코드를 작성해보겠습니다.

lakue.tistory.com

이 블로그를 참고해서 해결했다!