채팅 - 메시지 주고 받기

변현섭·2023년 9월 8일
0
post-thumbnail

채팅방도 생성했고, 채팅방에 유저도 초대했으니, 이제 마지막으로 메시지를 주고 받을 수 있는 기능을 추가해보도록 하겠습니다.

5. 메시지 주고 받기

① FirebaseRef에 아래의 내용을 입력한다.

val message = database.getReference("message")

② 메시지 전송에 사용될 data class를 정의하자. chat 디렉토리 하위로 MessageModel이라는 이름의 kotlin class를 추가한다.

  • 임시적으로 View Type을 Me로 지정하지만, 메시지를 가져올 때 동적으로 변경해줄 것이다.
  • 따라서 viewType은 반드시 var로 선언해야 한다.
data class MessageModel (
    val senderUid : String = "",
    val senderNickName : String = "",
    val senderProfileUrl : String = "",
    val contents : String = "",
    val sendTime : String = "",
    var viewType: Int = VIEW_TYPE_ME
) {
    companion object {
        const val VIEW_TYPE_YOU = 0
        const val VIEW_TYPE_ME = 1
    }
}

③ ChatRoomActivity에 send 버튼에 대한 클릭 이벤트 리스너를 등록하자.

  • push를 붙이지 않으면 가장 최근에 set된 value로 기존 값이 변경된다.
  • 기존 데이터에 무관하게 항상 DB에 추가되도록 하는 것이 목적이라면 반드시 push를 붙여야 한다.
class ChatRoomActivity : AppCompatActivity() {

    lateinit var count : String

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat_room)

        val roomName = findViewById<TextView>(R.id.chatRoomName)
        val nickNameList = findViewById<TextView>(R.id.nickNameList)
        val inviteBtn = findViewById<Button>(R.id.invite)
        val userCount = findViewById<TextView>(R.id.userCount)

        // Intent로부터 데이터를 가져옴
        val chatRoomName = intent.getStringExtra("chatRoomName")
        roomName.text = chatRoomName

        val roomId = intent.getStringExtra("chatRoomId")
        val chatRoomId = roomId

        val userList = intent.getStringExtra("userList")
        nickNameList.text = userList

        CoroutineScope(Dispatchers.IO).launch {
            val response = getUserCount(chatRoomId!!)
            Log.d("userCount", response.toString())
            if (response.isSuccess) {
                count = response.result.toString()
                userCount.text = count
            } else {
                Log.d("UserListFragment", "유저의 정보를 불러오지 못함")
            }
        }

        inviteBtn.setOnClickListener {
            val intent = Intent(this, InviteActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            intent.putExtra("chatRoomName", chatRoomName)
            intent.putExtra("nickNameList", userList)
            startActivity(intent)
        }

        val participantBtn = findViewById<ImageView>(R.id.participants)
        participantBtn.setOnClickListener {
            val intent = Intent(this, ParticipantsActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            startActivity(intent)
        }

        val message = findViewById<TextInputEditText>(R.id.message)
        val sendBtn = findViewById<ImageView>(R.id.send)

        lateinit var myNickName : String
        lateinit var myProfileUrl : String
        lateinit var messageModel: MessageModel
        val myUid = FirebaseAuthUtils.getUid()

        sendBtn.setOnClickListener {
            val contents = message.text.toString()
            val sendTime = getSendTime()
            CoroutineScope(Dispatchers.IO).launch {
                val response = getUserInfo(myUid)
                if (response.isSuccess) {
                    myNickName = response.result?.nickName.toString()
                    myProfileUrl = response.result?.imgUrl.toString()
                    messageModel = MessageModel(myUid, myNickName, myProfileUrl, contents, sendTime)
                    FirebaseRef.message.child(chatRoomId!!).push().setValue(messageModel)
                } else {
                    Log.d("UserListFragment", "유저의 정보를 불러오지 못함")
                }
            }
            message.text?.clear()
        }
    }

    private suspend fun getUserCount(roomId: String) : BaseResponse<String> {
        return RetrofitInstance.chatApi.getUserCount(roomId)
    }

    private suspend fun getUserInfo(uid: String): BaseResponse<GetUserRes> {
        return RetrofitInstance.myPageApi.getUserInfo(uid)
    }

    //메시지 보낸 시각 정보 반환
    @RequiresApi(Build.VERSION_CODES.O)
    private fun getSendTime(): String {
        try {
            val localDateTime = LocalDateTime.now()
            val dateTimeFormatter = DateTimeFormatter.ofPattern("a h:mm")
            return localDateTime.format(dateTimeFormatter)
        } catch (e: Exception) {
            e.printStackTrace()
            throw Exception("시간 정보를 불러오지 못함")
        }
    }
}

④ drawable 디렉토리 하위로 message_box라는 이름의 리소스 파일을 추가한다.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/skyBlue"/>
    <corners android:radius="15dp"/>
</shape>

⑤ 메시지 목록을 Recycler View로 보여주기 위해 layout 디렉토리 하위로, message_recycler_view_item이라는 이름의 리소스 파일을 추가하자.

<?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="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/messageContentsArea"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="35dp"
            android:layout_marginEnd="8dp"
            android:background="@drawable/message_box"
            android:ellipsize="end"
            android:maxWidth="230dp"
            android:padding="10dp"
            android:text="my text message"
            android:textSize="15sp"
            app:layout_constraintEnd_toStartOf="@+id/messageProfileArea"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/dateTime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_marginEnd="15dp"
            android:text="9/7 7:01 AM"
            android:textSize="10sp"
            app:layout_constraintEnd_toStartOf="@+id/messageProfileArea"
            app:layout_constraintTop_toBottomOf="@+id/messageContentsArea" />

        <ImageView
            android:id="@+id/messageProfileArea"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginTop="15dp"
            android:layout_marginRight="15dp"
            android:src="@drawable/profile"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/messageNickName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="17dp"
            android:layout_marginBottom="2dp"
            android:gravity="center"
            android:text="nickname"
            android:textColor="@color/black"
            android:textSize="15sp"
            app:layout_constraintBottom_toTopOf="@+id/messageContentsArea"
            app:layout_constraintEnd_toStartOf="@+id/messageProfileArea" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>

⑥ 다른 사람의 메시지 박스에 적용할 your_message_box라는 이름의 리소스 파일을 추가한다.

  • 나의 메시지 박스와 상대방의 메시지 박스를 다르게 해주고 싶지 않으면 넘어가도 된다.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/skyBlue"/>
            <corners android:radius="15dp"/>
        </shape>
    </item>
    <item android:right="3dp" android:left="3dp" android:top="3dp" android:bottom="3dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/white"/>
            <corners android:radius="14dp"/>
        </shape>
    </item>
</layer-list>

⑦ 나의 채팅을 오른쪽에, 상대방의 채팅을 왼쪽에 표시하기 위해서 layout 디렉토리 하위로, message_recycler_view_item2를 추가한다.

<?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="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/messageContentsArea2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="35dp"
            android:background="@drawable/your_message_box"
            android:ellipsize="end"
            android:maxWidth="230dp"
            android:padding="10dp"
            android:text="my text message"
            android:textSize="15sp"
            app:layout_constraintStart_toEndOf="@+id/messageProfileArea2"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/dateTime2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="9/7 7:01 AM"
            android:textSize="10sp"
            android:layout_marginTop="5dp"
            android:layout_marginStart="15dp"
            app:layout_constraintStart_toEndOf="@+id/messageProfileArea2"
            app:layout_constraintTop_toBottomOf="@+id/messageContentsArea2" />

        <ImageView
            android:id="@+id/messageProfileArea2"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginTop="15dp"
            android:layout_marginLeft="15dp"
            android:src="@drawable/profile"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/messageNickName2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="17dp"
            android:gravity="center"
            android:text="nickname"
            android:textColor="@color/black"
            android:textSize="15sp"
            app:layout_constraintBottom_toTopOf="@+id/messageContentsArea2"
            app:layout_constraintStart_toEndOf="@+id/messageProfileArea2" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>
  • MessageModel을 사용하고 있는 ChatRoomActivity도 알맞게 수정해야 한다.
messageModel = MessageModel(myUid, myNickName, myProfileUrl, contents, sendTime, 1)

⑨ activity_chat_room.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:background="@drawable/main_border"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".chat.ChatRoomActivity">

    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="match_parent"
        android:layout_height="145dp"
        android:background="@drawable/main_border"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/userCount"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginTop="55dp"
            android:layout_marginLeft="10dp"
            android:text="1"
            android:gravity="right"
            android:textSize="20sp"
            app:layout_constraintEnd_toEndOf="@+id/roomName"
            app:layout_constraintHorizontal_bias="0.774"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:layout_width="35dp"
            android:layout_height="30dp"
            android:layout_marginTop="55dp"
            android:gravity="right"
            android:text="명 :"
            android:textSize="20sp"
            app:layout_constraintEnd_toEndOf="@+id/roomName"
            app:layout_constraintHorizontal_bias="0.774"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/nickNameList"
            android:layout_width="170dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="55dp"
            android:maxWidth="170dp"
            android:text="User List"
            android:textSize="20sp"
            app:layout_constraintEnd_toEndOf="@+id/roomName"
            app:layout_constraintHorizontal_bias="0.774"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/invite"
            android:layout_width="75dp"
            android:layout_height="40dp"
            android:layout_marginTop="90dp"
            android:background="@color/skyBlue"
            android:text="초대"
            android:textSize="20sp" />

    </LinearLayout>

    <ImageView
        android:id="@+id/participants"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="15dp"
        android:src="@drawable/participants"
        android:textSize="25sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/chatRoomName"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="ChatRoomName"
        android:textColor="#000000"
        android:textSize="30sp"
        android:layout_marginTop="10dp"
        android:layout_marginHorizontal="20dp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/linearLayout3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        tools:layout_editor_absoluteX="-19dp">

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/message"
                android:layout_width="280dp"
                android:layout_height="50dp"
                android:layout_marginLeft="13dp"
                android:layout_marginBottom="10dp"
                android:background="@drawable/main_border"
                android:hint="텍스트를 입력하세요"
                android:padding="5dp"
                android:textColorHint="#808080"
                android:textSize="25sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent" />

        </com.google.android.material.textfield.TextInputLayout>


        <ImageView
            android:id="@+id/send"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:layout_marginLeft="10dp"
            android:src="@drawable/send" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/linearLayout3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/linearLayout2"
        app:layout_constraintVertical_bias="1.0"
        android:orientation="vertical">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/messageRV"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toTopOf="@+id/linearLayout3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/linearLayout2"
            app:layout_constraintVertical_bias="1.0"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

⑩ chat 패키지 하위로 MessageAdapter 클래스를 추가한다.

class MessageAdapter(private val context: Context, val items: MutableList<MessageModel>)
    : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    class ItemViewHolder1(private val view: View) : RecyclerView.ViewHolder(view) {
        val contents : TextView = view.findViewById(R.id.messageContentsArea)
        val dateTime : TextView = view.findViewById(R.id.dateTime)
        val nickName : TextView = view.findViewById(R.id.messageNickName)
        val profile : ImageView = view.findViewById(R.id.messageProfileArea)
    }

    class ItemViewHolder2(private val view: View) : RecyclerView.ViewHolder(view) {
        val contents : TextView = view.findViewById(R.id.messageContentsArea2)
        val dateTime : TextView = view.findViewById(R.id.dateTime2)
        val nickName : TextView = view.findViewById(R.id.messageNickName2)
        val profile : ImageView = view.findViewById(R.id.messageProfileArea2)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val adapterLayout : View?
        return when(viewType) {
            MessageModel.VIEW_TYPE_ME -> {
                adapterLayout = LayoutInflater.from(parent.context).inflate(R.layout.message_recycler_view_item, parent, false)
                ItemViewHolder1(adapterLayout)
            }
            MessageModel.VIEW_TYPE_YOU -> {
                adapterLayout = LayoutInflater.from(parent.context).inflate(R.layout.message_recycler_view_item2, parent, false)
                ItemViewHolder2(adapterLayout)
            }
            else -> throw RuntimeException("Invalid View Type Error")
        }

    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item = items[position]
        when (item.viewType) {
            MessageModel.VIEW_TYPE_ME -> {
                (holder as ItemViewHolder1).contents.text = item.contents
                holder.dateTime.text = item.sendTime
                holder.nickName.text = item.senderNickName
                if (item.senderProfileUrl != "null") {
                    Glide.with(context)
                        .load(item.senderProfileUrl)
                        .into(holder.profile)
                } else {
                    holder.profile.setImageResource(R.drawable.profile)
                }
            }

            MessageModel.VIEW_TYPE_YOU -> {
                (holder as ItemViewHolder2).contents.text = item.contents
                holder.dateTime.text = item.sendTime
                holder.nickName.text = item.senderNickName
                if (item.senderProfileUrl != "null") {
                    Glide.with(context)
                        .load(item.senderProfileUrl)
                        .into(holder.profile)
                } else {
                    holder.profile.setImageResource(R.drawable.profile)
                }
            }
        }
    }

    override fun getItemCount(): Int {
        return items.size
    }

    override fun getItemViewType(position: Int): Int {
        return items[position].viewType
    }
}

⑪ ChatRoomActivity를 아래와 같이 수정한다.

  • getMessageList에서 viewType을 동적으로 변경해야 한다.
class ChatRoomActivity : AppCompatActivity() {

    lateinit var count : String
    lateinit var messageAdapter : MessageAdapter
    val messageList = mutableListOf<MessageModel>()

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat_room)

        val roomName = findViewById<TextView>(R.id.chatRoomName)
        val nickNameList = findViewById<TextView>(R.id.nickNameList)
        val inviteBtn = findViewById<Button>(R.id.invite)
        val userCount = findViewById<TextView>(R.id.userCount)

        // Intent로부터 데이터를 가져옴
        val chatRoomName = intent.getStringExtra("chatRoomName")
        roomName.text = chatRoomName

        val roomId = intent.getStringExtra("chatRoomId")
        val chatRoomId = roomId

        val userList = intent.getStringExtra("userList")
        nickNameList.text = userList

        CoroutineScope(Dispatchers.IO).launch {
            val response = getUserCount(chatRoomId!!)
            Log.d("userCount", response.toString())
            if (response.isSuccess) {
                count = response.result.toString()
                userCount.text = count
            } else {
                Log.d("UserListFragment", "유저의 정보를 불러오지 못함")
            }
        }

        val recyclerView = findViewById<RecyclerView>(R.id.messageRV)
        messageAdapter = MessageAdapter(this, messageList)
        recyclerView.adapter = messageAdapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        getMessageList(chatRoomId!!)
        Log.d("MessageList", messageList.toString())

        inviteBtn.setOnClickListener {
            val intent = Intent(this, InviteActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            intent.putExtra("chatRoomName", chatRoomName)
            intent.putExtra("nickNameList", userList)
            startActivity(intent)
        }

        val participantBtn = findViewById<ImageView>(R.id.participants)
        participantBtn.setOnClickListener {
            val intent = Intent(this, ParticipantsActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            startActivity(intent)
        }

        val message = findViewById<TextInputEditText>(R.id.message)
        val sendBtn = findViewById<ImageView>(R.id.send)

        lateinit var myNickName : String
        lateinit var myProfileUrl : String
        lateinit var messageModel: MessageModel
        val myUid = FirebaseAuthUtils.getUid()

        sendBtn.setOnClickListener {
            val contents = message.text.toString()
            val sendTime = getSendTime()
            CoroutineScope(Dispatchers.IO).launch {
                val response = getUserInfo(myUid)
                if (response.isSuccess) {
                    myNickName = response.result?.nickName.toString()
                    myProfileUrl = response.result?.imgUrl.toString()
                    messageModel = MessageModel(myUid, myNickName, myProfileUrl, contents, sendTime)
                    FirebaseRef.message.child(chatRoomId!!).push().setValue(messageModel)
                } else {
                    Log.d("ChatRoomActivity", "유저의 정보를 불러오지 못함")
                }
            }
            message.text?.clear()
        }
    }

    private suspend fun getUserCount(roomId: String) : BaseResponse<String> {
        return RetrofitInstance.chatApi.getUserCount(roomId)
    }

    private suspend fun getUserInfo(uid: String): BaseResponse<GetUserRes> {
        return RetrofitInstance.myPageApi.getUserInfo(uid)
    }

    //메시지 보낸 시각 정보 반환
    @RequiresApi(Build.VERSION_CODES.O)
    private fun getSendTime(): String {
        try {
            val localDateTime = LocalDateTime.now()
            val dateTimeFormatter = DateTimeFormatter.ofPattern("M/d  h:mm  a")
            return localDateTime.format(dateTimeFormatter)
        } catch (e: Exception) {
            e.printStackTrace()
            throw Exception("시간 정보를 불러오지 못함")
        }
    }

    private fun getMessageList(chatRoomId : String) {
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                messageList.clear()
                val newMessages = mutableListOf<MessageModel>() // 새로운 메시지를 저장할 리스트 생성
                for (datamModel in dataSnapshot.children) {
                    val messageModel = datamModel.getValue(MessageModel::class.java)
                    if(messageModel!!.senderUid != FirebaseAuthUtils.getUid()) {
                        messageModel.viewType = MessageModel.VIEW_TYPE_YOU
                    }
                    newMessages.add(messageModel!!)
                }
                messageList.addAll(newMessages)
                messageAdapter.notifyDataSetChanged()
                Log.d("MessageList", messageList.toString())
            }

            override fun onCancelled(databaseError: DatabaseError) {
                Log.w("MyMessage", "onCancelled", databaseError.toException())
            }
        }
        FirebaseRef.message.child(chatRoomId).addValueEventListener(postListener)
    }
}

이제 코드를 실행시켜보자. AVD 또는 실제 본인의 휴대폰을 이용해 두 개 이상의 디바이스에서 테스트해보기 바란다. (만약 본인의 휴대폰으로 테스트할거라면, 먼저 서버에 배포해야 한다. 서버에 배포하는 방법을 모르거나 귀찮다면 AVD 두 개로 테스트해야 한다.) 실시간으로 채팅 메시지가 업데이트 되어야하고, 상대방의 메시지와 나의 메시지가 다른 레이아웃으로 구성되어야 한다.

※ 새로운 메시지로 포커스 이동시키기

지금의 코드는 새로운 메시지를 보내거나 받아도 포커스가 이동하지 않는다. 어플리케이션의 동작에는 전혀 무리가 없으나, UX 측면에서의 불편함이 있을 수는 있다. 그러한 경우 아래와 같이 ChatRoomAcitvity의 getMessageList 메서드를 수정하면 된다.

class ChatRoomActivity : AppCompatActivity() {
	lateinit var recyclerView : RecyclerView // 리사이클러뷰를 lateinit으로 선언
    ...
    private fun getMessageList(chatRoomId : String) {
    	val postListener = object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
        	...
    		messageList.addAll(newMessages)
    		messageAdapter.notifyDataSetChanged()
    		Log.d("MessageList", messageList.toString())
            
    		recyclerView.post {
        		recyclerView.scrollToPosition(recyclerView.adapter?.itemCount?.minus(1) ?: 0)
   			}
}

이제 새로운 메시지로 포커스가 이동하는 것을 확인할 수 있을 것이다. 또한 채팅방에 나갔다가 다시 들어갈 때에도 가장 최근 메시지에 포커싱된다.

profile
Java Spring, Android Kotlin, Node.js, ML/DL 개발을 공부하는 인하대학교 정보통신공학과 학생입니다.

0개의 댓글