-
[Android]Firebase로 채팅 앱 만들기(6)Andorid 2022. 3. 31. 15:03
이번엔 각 채팅방을 선택했을 때 이동할 채팅방 화면을 만들어보자.
우선 채팅방에 필요한 UI와, 메시지의 배경 레이아웃을 만들어준다.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/background" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white"> <TextView android:id="@+id/txt_TItle" style="@style/boldBlack" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="홍길동" android:textSize="18dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageButton android:id="@+id/imgbtn_quit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginLeft="20dp" android:background="@android:color/transparent" app:layout_constraintBottom_toBottomOf="@+id/txt_TItle" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/txt_TItle" app:srcCompat="@drawable/btn_quit" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginLeft="10dp" android:layout_marginTop="60dp" android:layout_marginEnd="10dp" android:layout_marginRight="10dp" android:text="오늘" app:layout_constraintEnd_toStartOf="@+id/divider_right" app:layout_constraintStart_toEndOf="@+id/divider_left" app:layout_constraintTop_toBottomOf="@+id/txt_TItle" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/divider_left" android:layout_width="0dp" android:layout_height="1dp" android:layout_marginEnd="10dp" android:layout_marginRight="10dp" android:background="#B6B6B6" app:layout_constraintBottom_toBottomOf="@+id/textView2" app:layout_constraintEnd_toStartOf="@+id/textView2" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/textView2"> </androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/divider_right" android:layout_width="0dp" android:layout_height="1dp" android:layout_marginStart="10dp" android:layout_marginLeft="10dp" android:background="#B6B6B6" app:layout_constraintBottom_toBottomOf="@+id/textView2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/textView2" app:layout_constraintTop_toTopOf="@+id/textView2"> </androidx.constraintlayout.widget.ConstraintLayout> <EditText android:id="@+id/edt_message" android:layout_width="0dp" android:layout_height="0dp" android:background="@android:color/transparent" android:hint="메시지 입력" android:paddingLeft="20dp" android:paddingRight="20dp" android:textColor="@color/black" android:textSize="18dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/btn_submit" app:layout_constraintHeight_percent="0.1" app:layout_constraintStart_toStartOf="parent" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/divider_bottom" android:layout_width="0dp" android:layout_height="1dp" android:background="#B6B6B6" app:layout_constraintBottom_toTopOf="@+id/edt_message" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"> </androidx.constraintlayout.widget.ConstraintLayout> <Button android:id="@+id/btn_submit" android:layout_width="wrap_content" android:layout_height="0dp" android:text="전송" android:textColor="@color/black" app:backgroundTint="@android:color/transparent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/edt_message" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_messages" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginStart="20dp" android:layout_marginTop="20dp" android:layout_marginEnd="20dp" android:layout_marginBottom="20dp" app:layout_constraintBottom_toTopOf="@+id/edt_message" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView2" tools:listitem="@layout/list_talk_item_mine" /> </androidx.constraintlayout.widget.ConstraintLayout>
위는 화면에 필요한 레이아웃에 사용되는 XML이다.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/background" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginBottom="20dp"> <TextView android:id="@+id/txt_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:autoLink="web" android:background="@drawable/background_talk_mine" android:gravity="start|center_vertical" android:linksClickable="true" android:padding="10dp" android:text="안녕하세요." android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/txt_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="10dp" android:layout_marginRight="10dp" android:text="오전 10:23" android:textSize="12dp" app:layout_constraintBottom_toBottomOf="@+id/txt_message" app:layout_constraintEnd_toStartOf="@+id/txt_message" app:layout_constraintTop_toTopOf="@+id/txt_message" app:layout_constraintVertical_bias="0.8" /> <TextView android:id="@+id/txt_isShown" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="10dp" android:text="1" android:textColor="#FFBE3C" app:layout_constraintBottom_toBottomOf="@+id/txt_date" app:layout_constraintEnd_toStartOf="@+id/txt_date" app:layout_constraintTop_toTopOf="@+id/txt_date" /> </androidx.constraintlayout.widget.ConstraintLayout>
채팅에 사용되는 메시지의 레이아웃이다.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#6365FF"/> <corners android:radius="15dp"/> </shape>
background의 배경은 drawable에 corner를 설정하여 둥근 테두리 배경을 생성했다.
나/상대방 구분은 같은 배경에 색상을 변경하여 구분하도록 했다.
@RequiresApi(Build.VERSION_CODES.O) class ChatRoomActivity : AppCompatActivity() { lateinit var binding: ActivityChatroomBinding lateinit var btn_exit: ImageButton lateinit var btn_submit: Button lateinit var txt_title: TextView lateinit var edt_message: EditText lateinit var firebaseDatabase: DatabaseReference lateinit var recycler_talks: RecyclerView lateinit var chatRoom: ChatRoom lateinit var opponentUser: User lateinit var chatRoomKey: String lateinit var myUid: String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityChatroomBinding.inflate(layoutInflater) setContentView(binding.root) initializeProperty() initializeView() initializeListener() setupChatRooms() } fun initializeProperty() { //변수 초기화 myUid = FirebaseAuth.getInstance().currentUser?.uid!! //현재 로그인한 유저 id firebaseDatabase = FirebaseDatabase.getInstance().reference!! chatRoom = (intent.getSerializableExtra("ChatRoom")) as ChatRoom //채팅방 정보 chatRoomKey = intent.getStringExtra("ChatRoomKey")!! //채팅방 키 opponentUser = (intent.getSerializableExtra("Opponent")) as User //상대방 유저 정보 } fun initializeView() { //뷰 초기화 btn_exit = binding.imgbtnQuit edt_message = binding.edtMessage recycler_talks = binding.recyclerMessages btn_submit = binding.btnSubmit txt_title = binding.txtTItle txt_title.text = opponentUser!!.name ?: "" } fun initializeListener() { //버튼 클릭 시 리스너 초기화 btn_exit.setOnClickListener() { startActivity(Intent(this@ChatRoomActivity, MainActivity::class.java)) } btn_submit.setOnClickListener() { putMessage() } } fun setupChatRooms() { //채팅방 목록 초기화 및 표시 if (chatRoomKey.isNullOrBlank()) setupChatRoomKey() else setupRecycler() } fun setupChatRoomKey() { //chatRoomKey 없을 경우 초기화 후 목록 초기화 FirebaseDatabase.getInstance().getReference("ChatRoom") .child("chatRooms").orderByChild("users/${opponentUser.uid}").equalTo(true) //상대방의 Uid가 포함된 목록이 있는지 확인 .addListenerForSingleValueEvent(object : ValueEventListener { override fun onCancelled(error: DatabaseError) {} override fun onDataChange(snapshot: DataSnapshot) { for (data in snapshot.children) { chatRoomKey = data.key!! //chatRoomKey 초기화 setupRecycler() //목록 업데이트 break } } }) } fun putMessage() { //메시지 전송 try { var message = Message(myUid, getDateTimeString(), edt_message.text.toString()) //메시지 정보 초기화 Log.i("ChatRoomKey", chatRoomKey) FirebaseDatabase.getInstance().getReference("ChatRoom").child("chatRooms") .child(chatRoomKey).child("messages") //현재 채팅방에 메시지 추가 .push().setValue(message).addOnSuccessListener { Log.i("putMessage", "메시지 전송에 성공하였습니다.") edt_message.text.clear() }.addOnCanceledListener { Log.i("putMessage", "메시지 전송에 실패하였습니다") } } catch (e: Exception) { e.printStackTrace() Log.i("putMessage", "메시지 전송 중 오류가 발생하였습니다.") } } fun getDateTimeString(): String { //메시지 보낸 시각 정보 반환 try { var localDateTime = LocalDateTime.now() localDateTime.atZone(TimeZone.getDefault().toZoneId()) var dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss") return localDateTime.format(dateTimeFormatter).toString() } catch (e: Exception) { e.printStackTrace() throw Exception("getTimeError") } } fun setupRecycler() { //목록 초기화 및 업데이트 recycler_talks.layoutManager = LinearLayoutManager(this) recycler_talks.adapter = RecyclerMessagesAdapter(this, chatRoomKey, opponentUser.uid) } }
위 레이아웃을 표시해줄 액티비티의 전체 코드이다.
처음에 뷰들을 초기화 해준 뒤에 채팅방의 메시지 목록을 불러와 셋 업해준 후,
X 버튼을 누를 시와 전송 버튼을 누를 시 메시지 전송을 실행할 리스너를 등록해준다.
fun setupChatRoomKey() { //chatRoomKey 없을 경우 초기화 후 목록 초기화 FirebaseDatabase.getInstance().getReference("ChatRoom") .child("chatRooms").orderByChild("users/${opponentUser.uid}").equalTo(true) //상대방의 Uid가 포함된 목록이 있는지 확인 .addListenerForSingleValueEvent(object : ValueEventListener { override fun onCancelled(error: DatabaseError) {} override fun onDataChange(snapshot: DataSnapshot) { for (data in snapshot.children) { chatRoomKey = data.key!! //chatRoomKey 초기화 setupRecycler() //목록 업데이트 break } } }) } fun setupRecycler() { //목록 초기화 및 업데이트 recycler_talks.layoutManager = LinearLayoutManager(this) recycler_talks.adapter = RecyclerMessagesAdapter(this, chatRoomKey, opponentUser.uid) }
상대방의 Uid를 포함하는 채팅방을 찾아서 chatRoomKey를 얻고, 그걸 토대로 해당 채팅방의 정보들을 가져온다.
그 후, 목록을 표시할 리사이클러뷰에 chatRoomKey와 상대방 유저의 uid를 함께 넘겨서 초기화해준다.
fun putMessage() { //메시지 전송 try { var message = Message(myUid, getDateTimeString(), edt_message.text.toString()) //메시지 정보 초기화 Log.i("ChatRoomKey", chatRoomKey) FirebaseDatabase.getInstance().getReference("ChatRoom").child("chatRooms") .child(chatRoomKey).child("messages") //현재 채팅방에 메시지 추가 .push().setValue(message).addOnSuccessListener { Log.i("putMessage", "메시지 전송에 성공하였습니다.") edt_message.text.clear() }.addOnCanceledListener { Log.i("putMessage", "메시지 전송에 실패하였습니다") } } catch (e: Exception) { e.printStackTrace() Log.i("putMessage", "메시지 전송 중 오류가 발생하였습니다.") } } fun getDateTimeString(): String { //메시지 보낸 시각 정보 반환 try { var localDateTime = LocalDateTime.now() localDateTime.atZone(TimeZone.getDefault().toZoneId()) var dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss") return localDateTime.format(dateTimeFormatter).toString() } catch (e: Exception) { e.printStackTrace() throw Exception("getTimeError") } }
메시지 전송 시 실행될 코드다.
getDateTimeString으로 메시지를 보내는 현재 시각을 구하고,입력란의 텍스트를 Message 객체에 넣어 채팅방 데이터의 messages 노드에 저장한 뒤 입력란을 비운다.
이제 채팅방에서 사용되는 버튼에 대한 처리들은 구현되었으니,
채팅방에서 주고받은 메시지를 표시하여야 한다.
위 형태도 마찬가지로 리사이클러뷰로 구현하였다.
그러나 상대방과 나의 메시지는 다른 레이아웃으로 구분되어야 하므로,
하나의 리사이클러뷰에 두 개의 ViewHolder를 써야만 했다.
class RecyclerMessagesAdapter( val context: Context, var chatRoomKey: String?, val opponentUid: String? ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { var messages: ArrayList<Message> = arrayListOf() //메시지 목록 var messageKeys: ArrayList<String> = arrayListOf() //메시지 키 목록 val myUid = FirebaseAuth.getInstance().currentUser?.uid.toString() val recyclerView = (context as ChatRoomActivity).recycler_talks //목록이 표시될 리사이클러 뷰 init { setupMessages() } fun setupMessages() { getMessages() } fun getMessages() { FirebaseDatabase.getInstance().getReference("ChatRoom") .child("chatRooms").child(chatRoomKey!!).child("messages") //전체 메시지 목록 가져오기 .addValueEventListener(object : ValueEventListener { override fun onCancelled(error: DatabaseError) {} override fun onDataChange(snapshot: DataSnapshot) { messages.clear() for (data in snapshot.children) { messages.add(data.getValue<Message>()!!) //메시지 목록에 추가 messageKeys.add(data.key!!) //메시지 키 목록에 추가 } notifyDataSetChanged() //화면 업데이트 recyclerView.scrollToPosition(messages.size - 1) //스크롤 최 하단으로 내리기 } }) } override fun getItemViewType(position: Int): Int { //메시지의 id에 따라 내 메시지/상대 메시지 구분 return if (messages[position].senderUid.equals(myUid)) 1 else 0 } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { 1 -> { //메시지가 내 메시지인 경우 val view = LayoutInflater.from(context) .inflate(R.layout.list_talk_item_mine, parent, false) //내 메시지 레이아웃으로 초기화 MyMessageViewHolder(ListTalkItemMineBinding.bind(view)) } else -> { //메시지가 상대 메시지인 경우 val view = LayoutInflater.from(context) .inflate(R.layout.list_talk_item_others, parent, false) //상대 메시지 레이아웃으로 초기화 OtherMessageViewHolder(ListTalkItemOthersBinding.bind(view)) } } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (messages[position].senderUid.equals(myUid)) { //레이아웃 항목 초기화 (holder as MyMessageViewHolder).bind(position) } else { (holder as OtherMessageViewHolder).bind(position) } } override fun getItemCount(): Int { return messages.size } inner class OtherMessageViewHolder(itemView: ListTalkItemOthersBinding) : //상대 메시지 뷰홀더 RecyclerView.ViewHolder(itemView.root) { var background = itemView.background var txtMessage = itemView.txtMessage var txtDate = itemView.txtDate var txtIsShown = itemView.txtIsShown fun bind(position: Int) { //메시지 UI 항목 초기화 var message = messages[position] var sendDate = message.sended_date txtMessage.text = message.content txtDate.text = getDateText(sendDate) if (message.confirmed.equals(true)) //확인 여부 표시 txtIsShown.visibility = View.GONE else txtIsShown.visibility = View.VISIBLE setShown(position) //해당 메시지 확인하여 서버로 전송 } fun getDateText(sendDate: String): String { //메시지 전송 시각 생성 var dateText = "" var timeString = "" if (sendDate.isNotBlank()) { timeString = sendDate.substring(8, 12) var hour = timeString.substring(0, 2) var minute = timeString.substring(2, 4) var timeformat = "%02d:%02d" if (hour.toInt() > 11) { dateText += "오후 " dateText += timeformat.format(hour.toInt() - 12, minute.toInt()) } else { dateText += "오전 " dateText += timeformat.format(hour.toInt(), minute.toInt()) } } return dateText } fun setShown(position: Int) { //메시지 확인하여 서버로 전송 FirebaseDatabase.getInstance().getReference("ChatRoom") .child("chatRooms").child(chatRoomKey!!).child("messages") .child(messageKeys[position]).child("confirmed").setValue(true) .addOnSuccessListener { Log.i("checkShown", "성공") } } } inner class MyMessageViewHolder(itemView: ListTalkItemMineBinding) : // 내 메시지용 ViewHolder RecyclerView.ViewHolder(itemView.root) { var background = itemView.background var txtMessage = itemView.txtMessage var txtDate = itemView.txtDate var txtIsShown = itemView.txtIsShown fun bind(position: Int) { //메시지 UI 레이아웃 초기화 var message = messages[position] var sendDate = message.sended_date txtMessage.text = message.content txtDate.text = getDateText(sendDate) if (message.confirmed.equals(true)) txtIsShown.visibility = View.GONE else txtIsShown.visibility = View.VISIBLE } fun getDateText(sendDate: String): String { //메시지 전송 시각 생성 var dateText = "" var timeString = "" if (sendDate.isNotBlank()) { timeString = sendDate.substring(8, 12) var hour = timeString.substring(0, 2) var minute = timeString.substring(2, 4) var timeformat = "%02d:%02d" if (hour.toInt() > 11) { dateText += "오후 " dateText += timeformat.format(hour.toInt() - 12, minute.toInt()) } else { dateText += "오전 " dateText += timeformat.format(hour.toInt(), minute.toInt()) } } return dateText } } }
메시지를 표시하는 어댑터의 전체 코드이다.
class RecyclerMessagesAdapter( val context: Context, var chatRoomKey: String?, val opponentUid: String? ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { var messages: ArrayList<Message> = arrayListOf() //메시지 목록 var messageKeys: ArrayList<String> = arrayListOf() //메시지 키 목록 val myUid = FirebaseAuth.getInstance().currentUser?.uid.toString() val recyclerView = (context as ChatRoomActivity).recycler_talks //목록이 표시될 리사이클러 뷰 init { setupMessages() } fun setupMessages() { getMessages() } fun getMessages() { FirebaseDatabase.getInstance().getReference("ChatRoom") .child("chatRooms").child(chatRoomKey!!).child("messages") //전체 메시지 목록 가져오기 .addValueEventListener(object : ValueEventListener { override fun onCancelled(error: DatabaseError) {} override fun onDataChange(snapshot: DataSnapshot) { messages.clear() for (data in snapshot.children) { messages.add(data.getValue<Message>()!!) //메시지 목록에 추가 messageKeys.add(data.key!!) //메시지 키 목록에 추가 } notifyDataSetChanged() //화면 업데이트 recyclerView.scrollToPosition(messages.size - 1) //스크롤 최 하단으로 내리기 } }) }
생성자로부터 건네받은 채팅방의 키와 상대방의 Uid로 채팅방의 메시지 목록을 불러와 각 항목과 항목의 키들을 리스트에 추가한다.
그리고 메시지가 추가될 때마다 화면을 업데이트 한 뒤 스크롤을 맨 아래로 당기도록 한다.(초기화 될 때마다 위로 올라가므로)
inner class OtherMessageViewHolder(itemView: ListTalkItemOthersBinding) : //상대 메시지 뷰홀더 RecyclerView.ViewHolder(itemView.root) { var background = itemView.background var txtMessage = itemView.txtMessage var txtDate = itemView.txtDate var txtIsShown = itemView.txtIsShown fun bind(position: Int) { //메시지 UI 항목 초기화 var message = messages[position] var sendDate = message.sended_date txtMessage.text = message.content txtDate.text = getDateText(sendDate) if (message.confirmed.equals(true)) //확인 여부 표시 txtIsShown.visibility = View.GONE else txtIsShown.visibility = View.VISIBLE setShown(position) //해당 메시지 확인하여 서버로 전송 } fun getDateText(sendDate: String): String { //메시지 전송 시각 생성 var dateText = "" var timeString = "" if (sendDate.isNotBlank()) { timeString = sendDate.substring(8, 12) var hour = timeString.substring(0, 2) var minute = timeString.substring(2, 4) var timeformat = "%02d:%02d" if (hour.toInt() > 11) { dateText += "오후 " dateText += timeformat.format(hour.toInt() - 12, minute.toInt()) } else { dateText += "오전 " dateText += timeformat.format(hour.toInt(), minute.toInt()) } } return dateText } fun setShown(position: Int) { //메시지 확인하여 서버로 전송 FirebaseDatabase.getInstance().getReference("ChatRoom") .child("chatRooms").child(chatRoomKey!!).child("messages") .child(messageKeys[position]).child("confirmed").setValue(true) .addOnSuccessListener { Log.i("checkShown", "성공") } } } inner class MyMessageViewHolder(itemView: ListTalkItemMineBinding) : // 내 메시지용 ViewHolder RecyclerView.ViewHolder(itemView.root) { var background = itemView.background var txtMessage = itemView.txtMessage var txtDate = itemView.txtDate var txtIsShown = itemView.txtIsShown fun bind(position: Int) { //메시지 UI 레이아웃 초기화 var message = messages[position] var sendDate = message.sended_date txtMessage.text = message.content txtDate.text = getDateText(sendDate) if (message.confirmed.equals(true)) txtIsShown.visibility = View.GONE else txtIsShown.visibility = View.VISIBLE } fun getDateText(sendDate: String): String { //메시지 전송 시각 생성 var dateText = "" var timeString = "" if (sendDate.isNotBlank()) { timeString = sendDate.substring(8, 12) var hour = timeString.substring(0, 2) var minute = timeString.substring(2, 4) var timeformat = "%02d:%02d" if (hour.toInt() > 11) { dateText += "오후 " dateText += timeformat.format(hour.toInt() - 12, minute.toInt()) } else { dateText += "오전 " dateText += timeformat.format(hour.toInt(), minute.toInt()) } } return dateText } } }
그리고 상대방 메시지와 내 메시지를 구분할 ViewHolder를 따로 정의해준다.
override fun getItemViewType(position: Int): Int { //메시지의 id에 따라 내 메시지/상대 메시지 구분 return if (messages[position].senderUid.equals(myUid)) 1 else 0 } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { 1 -> { //메시지가 내 메시지인 경우 val view = LayoutInflater.from(context) .inflate(R.layout.list_talk_item_mine, parent, false) //내 메시지 레이아웃으로 초기화 MyMessageViewHolder(ListTalkItemMineBinding.bind(view)) } else -> { //메시지가 상대 메시지인 경우 val view = LayoutInflater.from(context) .inflate(R.layout.list_talk_item_others, parent, false) //상대 메시지 레이아웃으로 초기화 OtherMessageViewHolder(ListTalkItemOthersBinding.bind(view)) } } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (messages[position].senderUid.equals(myUid)) { //레이아웃 항목 초기화 (holder as MyMessageViewHolder).bind(position) } else { (holder as OtherMessageViewHolder).bind(position) } }
현재 포지션의 메시지의 송신자 id와 현재 로그인한 유저의 id를 비교하여,
일치하면 1,아니면 0으로 viewType을 반환하도록 하면,OnCreateViewHolder에서 받아
1이면 내 메시지를 표시하는 ViewHolder,0이면 상대 메시지를 표시하는 ViewHolder로 바인딩 한다.
fun bind(position: Int) { //메시지 UI 항목 초기화 var message = messages[position] var sendDate = message.sended_date txtMessage.text = message.content txtDate.text = getDateText(sendDate) if (message.confirmed.equals(true)) //확인 여부 표시 txtIsShown.visibility = View.GONE else txtIsShown.visibility = View.VISIBLE setShown(position) //해당 메시지 확인하여 서버로 전송 } fun getDateText(sendDate: String): String { //메시지 전송 시각 생성 var dateText = "" var timeString = "" if (sendDate.isNotBlank()) { timeString = sendDate.substring(8, 12) var hour = timeString.substring(0, 2) var minute = timeString.substring(2, 4) var timeformat = "%02d:%02d" if (hour.toInt() > 11) { dateText += "오후 " dateText += timeformat.format(hour.toInt() - 12, minute.toInt()) } else { dateText += "오전 " dateText += timeformat.format(hour.toInt(), minute.toInt()) } } return dateText } fun setShown(position: Int) { //메시지 확인하여 서버로 전송 FirebaseDatabase.getInstance().getReference("ChatRoom") .child("chatRooms").child(chatRoomKey!!).child("messages") .child(messageKeys[position]).child("confirmed").setValue(true) .addOnSuccessListener { Log.i("checkShown", "성공") } }
그리고 각 viewHolder에는 위와 같은 함수가 정의되어 있다.
뷰의 항목들을 초기화 하고, 메시지 옆에 표시될 시각을 계산하여 텍스트를 초기화한다.
그리고 메시지가 초기화되는 순간 상대방의 메시지를 확인했다는 것이므로,
서버에 상대 메시지를 읽음 처리하도록 했다.
정상적으로 작동되는 모습이다.
참조
https://m.blog.naver.com/zoown13/222094002924https://cionman.tistory.com/72https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=traeumen927&logNo=221493556497https://lasbe.tistory.com/19
우선 채팅방에 필요한 UI와, 메시지의 배경 레이아웃을 만들어준다.
위는 화면에 필요한 레이아웃에 사용되는 XML이다.
'Andorid' 카테고리의 다른 글
MVVM 적용기(2) (0) 2022.04.02 MVVM 적용기(1) (0) 2022.04.02 [Android]Firebase로 채팅 앱 만들기(5) (0) 2022.03.31 [Android]Firebase로 채팅 앱 만들기(4) (2) 2022.03.31 [Android]Firebase로 채팅 앱 만들기(3) (0) 2022.03.31