중고거래앱

LeeEunJae·2022년 7월 24일
2

Kotlin Project

목록 보기
4/10

📌 요구 사항

  • RecyclerView 사용하기
  • View Binding 사용하기
  • Fragment 사용하기
  • BottomNavigationView 사용하기
  • Firebase Storage 사용하기
  • Firebase Realtim DB 사용하기
  • Firebase Authenication 사용하기

📌 중고거래 앱

Firebase Authentication 기능을 사용하여 로그인 회원가입 기능을 구현할 수 있음.
회원 기반으로 중고거래 아이템을 등록할 수 있음
아이템 등록시 사진 업로드를 위해 Firebase Storage 를 사용할 수 있음
회원 기반으로 채팅 화면을 구현할 수 있음
Fragment 를 사용하여 하단 탭 화면 구조를 구현할 수 있음
FloatingActionButton을 사용하기

📌 NavigationView 로 메뉴 만들기

https://velog.io/@dldmswo1209/NavigationView-사용법

📌 게시물 업로드 기능 구현하기

https://velog.io/@dldmswo1209/Firebase-Realtime-DB-Storage-로-게시물-업로드-기능-구현하기

📌 채팅방 리스트 구현하기

채팅방 정보를 저장하기 위한 data class

data class ChatListItem(
    val buyerId: String,
    val sellerId: String,
    val buyerName: String,
    val sellerName: String,
    val itemTitle: String,
    val key: Long
){
    constructor(): this("","","","","",0)
}

채팅방 목록을 보여주기 위해 RecyclerView 가 포함되어 있는 Fragment 이다.

package com.dldmswo1209.usedgoods.chatlist

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.dldmswo1209.usedgoods.DBKey.Companion.CHILD_CHAT
import com.dldmswo1209.usedgoods.DBKey.Companion.DB_USERS
import com.dldmswo1209.usedgoods.R
import com.dldmswo1209.usedgoods.chatdetail.ChatRoomActivity
import com.dldmswo1209.usedgoods.databinding.FragmentChatlistBinding
import com.dldmswo1209.usedgoods.home.ArticleAdapter
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ValueEventListener
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase

class ChatListFragment: Fragment(R.layout.fragment_chatlist) {
    private var mBinding: FragmentChatlistBinding? = null
    private val binding get() = mBinding!!
    private lateinit var chatListAdapter: ChatListAdapter
    private val auth: FirebaseAuth by lazy {
        Firebase.auth
    }
    private val chatRoomList = mutableListOf<ChatListItem>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        mBinding = FragmentChatlistBinding.bind(view)

        chatListAdapter = ChatListAdapter(onItemClicked = { chatRoom ->
            // 채팅방으로 이동하는 코드
            context?.let{
                val intent = Intent(it, ChatRoomActivity::class.java)
                intent.putExtra("chatKey", chatRoom.key)
                startActivity(intent)
            }
        })
        chatRoomList.clear()

        binding.chatListRecyclerView.adapter = chatListAdapter
        binding.chatListRecyclerView.layoutManager = LinearLayoutManager(context)
        if(auth.currentUser == null){
            return
        }
        val chatDB = Firebase.database.reference.child(DB_USERS).child(auth.currentUser!!.uid).child(CHILD_CHAT)
        chatDB.addListenerForSingleValueEvent(object: ValueEventListener{
            override fun onDataChange(snapshot: DataSnapshot) {
                snapshot.children.forEach {
                    val model = it.getValue(ChatListItem::class.java) // ChatListItem 객체 형태로 채팅방 정보를 가져옴
                    model ?: return

                    chatRoomList.add(model) // 채팅방 리스트에 추가
                }
                chatListAdapter.submitList(chatRoomList)
                chatListAdapter.notifyDataSetChanged()
            }

            override fun onCancelled(error: DatabaseError) {}
        })
    }

    override fun onResume() {
        super.onResume()

        chatListAdapter.notifyDataSetChanged() // 뷰 갱신
    }
}

리사이클러뷰와 채팅방 목록 리스트를 연결하기 위한 어답터

package com.dldmswo1209.usedgoods.chatlist

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.dldmswo1209.usedgoods.databinding.ItemChatListBinding

class ChatListAdapter(val onItemClicked: (ChatListItem) -> Unit): ListAdapter<ChatListItem, ChatListAdapter.ViewHolder>(diffUtil) {
    inner class ViewHolder(private val binding: ItemChatListBinding): RecyclerView.ViewHolder(binding.root){
        fun bind(chatListItem: ChatListItem){
            binding.root.setOnClickListener {
                onItemClicked(chatListItem)
            }
            binding.chatRoomTitleTextview.text = chatListItem.itemTitle
        }
    }

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(currentList[position])
    }
    companion object{
        val diffUtil = object : DiffUtil.ItemCallback<ChatListItem>() {
            override fun areItemsTheSame(oldItem: ChatListItem, newItem: ChatListItem): Boolean {
                return oldItem.key== newItem.key
            }

            override fun areContentsTheSame(oldItem: ChatListItem, newItem: ChatListItem): Boolean {
                return oldItem == newItem
            }
        }
    }
}

📌 채팅방 구현하기

채팅 정보를 저장하기 위한 data class

data class ChatItem (
    val senderId: String,
    val senderName: String,
    val message: String
    ){
    constructor(): this("","","")
}

ChatRoomActivity.kt


import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.disklrucache.DiskLruCache
import com.dldmswo1209.usedgoods.DBKey.Companion.DB_CHATS
import com.dldmswo1209.usedgoods.DBKey.Companion.DB_USERS
import com.dldmswo1209.usedgoods.databinding.ActivityChatRoomBinding
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.UserInfo
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.*
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase

class ChatRoomActivity : AppCompatActivity() {
    private lateinit var binding: ActivityChatRoomBinding
    private val auth: FirebaseAuth by lazy{
        Firebase.auth
    }
    private val chatList = mutableListOf<ChatItem>()
    private val adapter = ChatItemAdapter()
    private var chatDB: DatabaseReference? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityChatRoomBinding.inflate(layoutInflater)
        setContentView(binding.root)
        val chatKey = intent.getLongExtra("chatKey",-1) // ChatListFragment 에서 클릭된 채팅방의 키값을 가져옴

        chatDB = Firebase.database.reference.child(DB_CHATS).child("$chatKey")
        chatDB?.addChildEventListener(object: ChildEventListener{
            override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
                val chatItem = snapshot.getValue(ChatItem::class.java)  // 채팅 정보를 가져옴
                chatItem ?: return
                chatList.add(chatItem) // 채팅리스트에 추가
                adapter.submitList(chatList)
                adapter.notifyDataSetChanged() // 채팅 갱신
            }

            override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {}

            override fun onChildRemoved(snapshot: DataSnapshot) {}

            override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {}

            override fun onCancelled(error: DatabaseError) {}
        })

        binding.chatRecyclerView.adapter = adapter
        binding.chatRecyclerView.layoutManager = LinearLayoutManager(this)
        binding.sendButton.setOnClickListener { // 채팅 전송 버튼 클릭 이벤트
            // 현재 유저의 이름을 가져오는 과정
            val userDB = Firebase.database.reference.child(DB_USERS).child(auth.currentUser!!.uid)
            userDB.addListenerForSingleValueEvent(object: ValueEventListener{
                override fun onDataChange(snapshot: DataSnapshot) {
                    val user = snapshot.getValue(com.dldmswo1209.usedgoods.mypage.UserInfo::class.java)
                    val chatItem = ChatItem(
                        senderId = auth.currentUser!!.uid,
                        senderName = user!!.name, // 이름을 가져와서 채팅정보를 생성
                        message = binding.messageEditText.text.toString()
                    )
                    chatDB?.push()?.setValue(chatItem) // db에 저장

                }
                override fun onCancelled(error: DatabaseError) {}

            })
        }


    }
}

채팅을 리사이클러뷰에 표시하기 위해 필요한 어답터

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.dldmswo1209.usedgoods.databinding.ItemChatBinding


class ChatItemAdapter: ListAdapter<ChatItem, ChatItemAdapter.ViewHolder>(diffUtil) {
    inner class ViewHolder(private val binding: ItemChatBinding): RecyclerView.ViewHolder(binding.root){
        fun bind(chatItem: ChatItem){
            binding.senderTextView.text = chatItem.senderName
            binding.messageTextView.text = chatItem.message
        }
    }

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(currentList[position])
    }
    companion object{
        val diffUtil = object : DiffUtil.ItemCallback<ChatItem>() {
            override fun areItemsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
                return oldItem == newItem
            }

            override fun areContentsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
                return oldItem == newItem
            }
        }
    }
}

✅ 추가해볼만한 기능

게시물을 터치 했을 때 게시물의 상세정보를 볼 수 있음
채팅할 때 나와 상대방을 구분되게 함

❗️ 해당 프로젝트는 FastCampus의 "30개 프로젝트로 배우는 Android 앱 개발 with Kotlin 초격차 패키지 Online" 강의를 수강하면서 만든 프로젝트 입니다.

profile
매일 조금씩이라도 성장하자

0개의 댓글