Firebase Authentication 기능을 사용하여 로그인 회원가입 기능을 구현할 수 있음.
회원 기반으로 중고거래 아이템을 등록할 수 있음
아이템 등록시 사진 업로드를 위해 Firebase Storage 를 사용할 수 있음
회원 기반으로 채팅 화면을 구현할 수 있음
Fragment 를 사용하여 하단 탭 화면 구조를 구현할 수 있음
FloatingActionButton을 사용하기
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" 강의를 수강하면서 만든 프로젝트 입니다.