파이어베이스-리얼타임 데이터베이스 구현(실시간 채팅 앱)
*파이어베이스-구현하기(실시간 채팅 앱, 리얼타임 데이터베이스 사용)
-리얼타임 데이터 베이스를 이용해 채팅 앱 만들기
-채팅 앱의 기본구조 : 회원가입/로그인 -> 채팅방목록/만들기 -> 입장 -> 채팅방
-데이터베이스 구조 : rooms라는 최상위 노드 안에 각 방의 id테이블이 있으며 id안 title, users, message목록(id:message)가 있음
*파이어베이스 리얼타임 데이터 베이스를 이용해 채팅 앱 만들기
-파이어베이스 프로젝트 및 리얼타임 데이터베이스 생성 : https://heeyjinny.tistory.com/121
-생성했던 프로젝트에 앱 추가할 수 있음 - 앱추가 - 다운로드 파일 추가 - SDK추가 - 완료
-build.gradle(Project)
--plugins{} 에 추가
id 'com.google.gms.google-services' version '4.3.13' apply false
-build.gradle(Module)
--plugins{} 에 추가
id 'com.google.gms.google-services'
-- android{} 에 뷰바인딩 추가
//뷰바인딩
buildFeatures{
viewBinding true
}
--dependencies{} 에 추가
implementation platform('com.google.firebase:firebase-bom:31.1.1')
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-database-ktx'
-MainActivity.kt
/** 실시간 채팅 앱 만들기(파이어베이스-리얼타임 데이터베이스) **/
//1
//3개 Activity 필요
//Main(로그인/회원가입)
//ChatList(채팅방 목록)
//ChatRoom(채팅방 화면)
//패키지명 우클릭 - New - Activity - 채티방목록 및 화면 생성
//2
//데이터 클래스 생성
//데이터 클래스를 생성할 model패키지 추가
//패키지명 우클릭 - New - Package - model
//model패키지에 메시지, 룸, 유저 클래스 추가
//3
//안드로이드 화면 만들기
//로그인/회원가입, 채팅방목록, 채팅방, 채팅방 메시지 화면
//activity_main.xml : 로그인/회원가입 화면
//activity_chat_list : 채팅방 목록 화면
//activity_chat_room : 채팅방 화면
//item_msg_list : 채팅방화면 목록의 메시지
//4
//User 클래스
//회원가입과 로그인 화면에서
//사용자 아이디, 비밀번호, 별명 입력받는 코드
//5
//Messages 클래스
//메시지를 주고받기 위해 사용되는 클래스
//채팅방에서 채팅 목록을 보여주기 위한 코드
//아이디, 메시지, 유저이름, 전송시간
//6
//Room 클래스
//채팅방생성, 생성된 채팅방 목록에 보여주는 클래스
//방 아이디, 방 이름
//7
//로그인/회원가입 구현하기
//MainActivity.kt
//12
//채팅방 목록/방 만들기 기능 구현하기
//ChatListActivity.kt
//13
//채팅방 구현
//ChatRoomActivity.kt
class MainActivity : AppCompatActivity() {
//뷰바인딩
val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
//7-1
//데이터베이스 프로퍼티에 파이어베이스 생성
val database = Firebase.database
//7-2
//현재 액티비티에서 사용할 users 노드 연결
val userRef = database.getReference("users")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//11
//로그인버튼 클릭시 signIn() 호출, 회원가입버튼 클릭 시 signUp() 호출
binding.btnSignin.setOnClickListener { signIn() }
binding.btnSignUp.setOnClickListener { signUp() }
}//onCreate
//8
//회원가입 버튼클릭 시 호출되는 함수생성
//signUp()
fun signUp(){
with(binding){
//8-1
//입력된 값 가져오기
val id = editId.text.toString()
val password = editPw.text.toString()
val name = editName.text.toString()
//8-2
//모두 값이 입력되어 있는지 검사***
if (id.isNotEmpty() && password.isNotEmpty() && name.isNotEmpty()){
//8-3
//화면에 모두 값이 입력되어 있다면...
//데이터베이스에 이미 아이디가 존재하는지 검사
userRef.child(id).get().addOnSuccessListener {
if (it.exists()){
Toast.makeText(baseContext, "아이디가 이미 존재합니다.", Toast.LENGTH_SHORT).show()
}else{
//8-4
//데이터베이스에 아이디가 없다면
//저장 후 자동으로 로그인
//유저데이터클래스에 id,pw,name 전달
val user = User(id, password, name)
//8-5
//데이터베이스 user노드안 id에 유저정보 추가...
userRef.child(id).setValue(user)
//8-6
//회원가입 완료 알림 메시지 후
//로그인...진행
Toast.makeText(baseContext, "회원가입이 완료되었습니다.", Toast.LENGTH_SHORT).show()
signIn()
}
}
}else{
//8-7
//입력창이 하나라도 비어있으면 메시지 출력
Toast.makeText(baseContext, "아이디, 비밀번호, 별명을 모두 입력해야 합니다.", Toast.LENGTH_SHORT).show()
}
}//with
}//signUp
//9
//로그인 버튼 클릭 시 호출되는 함수생성
//singIn()
fun signIn(){
with(binding){
//9-1
//화면에 입력된 아이디, 비밀번호 값 가져오기
val id = editId.text.toString()
val password = editPw.text.toString()
//9-2
//만약 아이디, 비밀번호 값이 모두 입력되었다면
if (id.isNotEmpty() && password.isNotEmpty()){
//9-3
//입력된 아이디로 User데이터 가져오기...
userRef.child(id).get().addOnSuccessListener {
//9-4
//id가 데이터 베이스에 있는지 확인
if (it.exists()){
//9-5
//비밀번호 비교
it.getValue(User::class.java)?.let {
//9-6
//비밀번호가 일치하다면
if (it.password == password){
//9-7
//유저 아이디와 별명을 전달하면서
//채팅방 목록 들어가기...(밑에 생성)
Toast.makeText(baseContext, "로그인 되었습니다.", Toast.LENGTH_SHORT).show()
goChatroomList(it.id, it.name)
}else{
//9-6-1
//비밀번호 불일치
Toast.makeText(baseContext, "비밀번호가 다릅니다.", Toast.LENGTH_SHORT).show()
}
}
}else{
//9-4-1
//아이디 없음
Toast.makeText(baseContext, "아이디가 없습니다.", Toast.LENGTH_SHORT).show()
}
}
}else{
//9-2-1
//아이디, 비밀번호 입력이 안되었다면
Toast.makeText(baseContext, "아이디, 비밀번호를 모두 입력해야 합니다.", Toast.LENGTH_SHORT).show()
}
}
}//signIn
//10
//로그인 성공 시 채팅방 목록으로 넘어가기
//사용자 정보를 파라미터로 전달
//아이디, 별명
fun goChatroomList(userId: String, userName: String){
//10-1
//채팅방 목록으로 넘어가는 인텐트 생성...
val intent = Intent(this, ChatListActivity::class.java)
//10-2
//방 생성 또는 입장시 사용
//인텐트 넘겨주면서 액티비티 시작
intent.putExtra("userId", userId)
intent.putExtra("userName", userName)
startActivity(intent)
}//goChatroomList
}//MainActivity
-User.kt
//회원가입과 로그인 화면에서
//사용자 아이디, 비밀번호, 별명 입력받는 코드
class User {
var id: String = ""
var password: String = ""
var name: String = ""
constructor()
constructor(id: String, password: String, name: String){
this.id = id
this.password = password
this.name = name
}
}
-User.kt
//회원가입과 로그인 화면에서
//사용자 아이디, 비밀번호, 별명 입력받는 코드
class User {
var id: String = ""
var password: String = ""
var name: String = ""
constructor()
constructor(id: String, password: String, name: String){
this.id = id
this.password = password
this.name = name
}
}
-ChatListActivity.kt
//채팅방 목록/방 만들기 기능 구현
class ChatListActivity : AppCompatActivity() {
//1
//뷰바인딩
val binding by lazy { ActivityChatListBinding.inflate(layoutInflater) }
//2
//데이터베이스와 연결하기
val database = Firebase.database
//3
//최상위 노드 rooms 연결
val roomsRef = database.getReference("rooms")
//4
//사용자 아이디와 이름을 저장하는 프로퍼티 선언
//채팅목록뿐만아니라 다른 액티비티에서 조회가 가능하도록
//compain object로 선언!
companion object{
var userId: String = ""
var userName: String = ""
}
//9
//방 목록을 저장한 roomList 프로퍼티 선언
val roomList = mutableListOf<Room>()
//10
//어댑터를 저장한 프로퍼티 선언
lateinit var adapter: ChatRoomListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//4-1
//위 선언한 프로퍼티에
//인텐트로 전달된 값 저장
//만약 값이 없을 때 엘비스 연산자 사용하여 기본 값 설정
userId = intent.getStringExtra("userId") ?: "none"
userName = intent.getStringExtra("userName") ?: "Anonymous"
//7
//방만들기 버튼 클릭 시
//만들기 다이얼로그 생성
binding.btnCreate.setOnClickListener { openCreateRoom() }
//11
//화면의 리사이클러뷰와 어탭터 연결
adapter = ChatRoomListAdapter(roomList)
binding.recyclerRooms.adapter = adapter
binding.recyclerRooms.layoutManager = LinearLayoutManager(baseContext)
//13
//목록을 갱신하는 메서드 호출
loadRooms()
}//onCreate
//5
//방 만들기 버튼 클릭 시
//AlertDialog를 사용해
//방 정보 입력 창 띄우기
fun openCreateRoom(){
//5-1
//방 이름을 입력할 EditText를 코드로 생성
val editText = EditText(this)
//5-2
//다이얼로그 생성
val dialog = AlertDialog.Builder(this)
.setTitle("방 이름")
.setView(editText).setNegativeButton("취소",null)
.setPositiveButton("만들기"){ dlg, id ->
//5-3
//방이름 입력 여부 체크...
if (editText.text.isNotEmpty()){
createRoom(editText.text.toString())
}else{
Toast.makeText(baseContext, "방 이름을 입력해야 합니다.", Toast.LENGTH_SHORT).show()
}
}
//5-4
//5-3의 속성을 가지고 있는 다이얼로그 띄우기
dialog.show()
}//openCreateRoom
//6
//실제 방을 만드는 creatRoom메서드 생성
//다이얼로그의 만들기버튼 클릭 시 호출
fun createRoom(title: String){
//6-1
//방 데이터 생성 Room클래스 데이터 저장
val room = Room(title, userName)
//6-2
//방 아이디 만들어서 입력
//.push().key!!를 이용해 데이터베이스에 자동아이디 생성
val roomId = roomsRef.push().key!!
room.id = roomId
//6-3
//파이어베이스에 전송
roomsRef.child(roomId).setValue(room)
}//createRoom
//12
//목록을 갱신하는 메서드 생성
//addValueEventListener사용...
//필수메서드가 2개이므로 object로 받아서 생성
fun loadRooms(){
roomsRef.addValueEventListener(object: ValueEventListener{
//12-1
//데이터가 정상적으로 변경되었으면
//파이어베이스 리얼타임 데이터베이스에서 rooms목록을 불러온 후
//roomList에 저장하고 목록갱신함
override fun onDataChange(snapshot: DataSnapshot) {
//12-1
//기존 방목록 삭제하고 방목록에 추가
roomList.clear()
for (item in snapshot.children){
item.getValue(Room::class.java)?.let {
roomList.add(it)
}
}
//12-2
//어댑터 갱신
adapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
print(error.message)
}
})
}//loadRooms
}//ChatListActivity
//8
//채팅방을 새로 만들면
//데이터베이스에 rooms 노드가 추가됨
//그 rooms의 목록을 리사이클러뷰에 보여주기
//리사이클러뷰 어댑터를 만들고 뷰와 연결하기
//새로 생성하지 않고 액티비티 밑에 클래스 생성...
class ChatRoomListAdapter(val roomList: MutableList<Room>):
RecyclerView.Adapter<ChatRoomListAdapter.Holder>() {//ChatRoomListAdapter
//8-3
//레이아웃을 생성하고 Holder에 담은 후 리턴...
//텍스트뷰가 1개인 간단한 목록이어서 아이템 레이아웃을 따로 생성하지 않음
//안드로이드 기본제공하는 아이템 simple_list_item_1 사용...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(parent.context)
.inflate(android.R.layout.simple_list_item_1,parent,false)
return Holder(view)
}
//8-4
//현재 포지션의 데이터를 꺼낸 후 홀더에 전달
override fun onBindViewHolder(holder: Holder, position: Int) {
val room = roomList.get(position)
holder.setRoom(room)
}
//8-2
//아이템 목록 리턴
override fun getItemCount(): Int {
return roomList.size
}
//8-1
//아이템 레이아웃을 따로 만들지 않았기 때문에 뷰바인딩 처리는 하지 않음...
//기본 아이템 뷰 사용예정이므로 바인딩말고
//View를 import함
inner class Holder(itemView: View): RecyclerView.ViewHolder(itemView){
//13
//목록 아이템 클릭 시 채팅방으로 이동
lateinit var mRoom: Room
init {
itemView.setOnClickListener {
val intent = Intent(itemView.context, ChatRoomActivity::class.java)
intent.putExtra("roomId", mRoom.id)
intent.putExtra("roomTitle", mRoom.title)
itemView.context.startActivity(intent)
}
}
//8-5
//setRoom()메서드 생성
//바인딩을 사용하지 않기 때문에 findViewById사용하여 위젯접근
//기본제공 아이템레이아웃의 텍스트뷰의 아이디는 text1 임...
fun setRoom(room: Room){
//14
//클릭 리스너를 init에서 구현하기때문에
//setRoom메서드에서 채팅방을 저장하여 init에서 사용...
this.mRoom = room
//8-5
itemView.findViewById<TextView>(android.R.id.text1).setText(room.title)
}
}
}//ChatRoomListAdapte
-Room.kt
//Room 클래스
//채팅방생성, 생성된 채팅방 목록에 보여주는 클래스
//방 아이디, 방 이름
class Room {
var id: String = ""
var title: String = ""
//users프로퍼티에는 하나의 이름만 입력되는데
//프로젝트가 끝난 후 여러개의 이름이 입력될 수 있도록 설계
var users: String = ""
constructor()
constructor(title: String, creatorName: String){
this.title = title
users = creatorName
}
}
-ChatRoomActivity.kt
//채팅방 구현
//데이터베이스의 rooms의 아이디노드 아래 방 정보와 함께 저장되는
//메시지 목록 사용
//rooms.child("방아이디").child("메시지들") 단계로 참조됨
class ChatRoomActivity : AppCompatActivity() {
//1
//뷰바인딩
val binding by lazy { ActivityChatRoomBinding.inflate(layoutInflater) }
//2
//파이어베이스 데이터베이스 연결
val database = Firebase.database
//3
//메시지노드를 참조하는 프로퍼티 생성
//메시지노드 참조 시 방 아이디가 필요하기 때문에
//lateinit 으로 먼저 선언
lateinit var msgRef: DatabaseReference
//4
//방 아이디, 방 제목 프로퍼티 미리 선언
var roomId: String = ""
var roomTitle: String = ""
//5
//메시지목록 프로퍼티 선언
val msgList = mutableListOf<Messages>()
//6
//어댑터를 저장할 프로퍼티 선언 후 어댑터 생성
lateinit var adapter: MsgListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//13
//인텐트로 전달된 방 정보와 사용자 정보꺼내기
roomId = intent.getStringExtra("roomId") ?: "none"
roomTitle = intent.getStringExtra("roomTitle") ?: "없음"
//14
//메시지 노드 레퍼런스 연결
//최상의노드 rooms안에 id에 들어가 messages생성 및 연결
msgRef = database.getReference("rooms").child(roomId).child("messages")
//15
//어댑터 생성
adapter = MsgListAdapter(msgList)
with(binding){
//16
//리사이클러뷰와 어댑터 연결
recyclerMsg.adapter = adapter
recyclerMsg.layoutManager = LinearLayoutManager(baseContext)
//20
//방제목 입력
textRoomName.text = roomTitle
//21
//뒤로가기버튼 방종료하기
//전송버튼 메시지 전송
btnBack.setOnClickListener { finish() }
btnSend.setOnClickListener { sendMsg() }
}
//18
//메시지 목록을 읽어와 갱신
//loadMsg()호출
loadMsg()
}//onCreate
//17
//메시지 목록을 읽어오는 ValueEventListener 를 호출하는
//loadMsg()메서드 생성
fun loadMsg(){
msgRef.addValueEventListener(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
//17-1
//메시지목록 삭제
msgList.clear()
for (item in snapshot.children){
//17-2
//메시지 목록에 추가
item.getValue(Messages::class.java)?.let {
msgList.add(it)
}
}
//17-3
//어댑터 갱신
adapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
print(error.message)
}
})
}//loadMsg
//19
//메시지를 파이어베이스에 전송하는
//메서드 sendMsg 작성
fun sendMsg(){
with(binding){
//19-1
//입력된 메시지가 있을때만 처리...
if (editMsg.text.isNotEmpty()){
val message = Messages(editMsg.text.toString(), ChatListActivity.userName)
val msgId = msgRef.push().key!!
message.id = msgId
msgRef.child(msgId).setValue(message)
//19-2
//메시지 입력 후 입력필드 삭제
editMsg.setText("")
}
}
}//sendMsg
}//ChatRoomActivity
//7
//어댑터클래스 생성
class MsgListAdapter(val msgList:MutableList<Messages>): RecyclerView.Adapter<MsgListAdapter.Holder>(){
//10
//뷰생성
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val binding = ItemMsgListBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return Holder(binding)
}
//11
//현재 위치 바인딩 처리
override fun onBindViewHolder(holder: Holder, position: Int) {
val msg = msgList.get(position)
holder.setMsg(msg)
}
//9
//목록의 개수
override fun getItemCount(): Int {
return msgList.size
}
//8
//홀더생성
inner class Holder(val binding: ItemMsgListBinding): RecyclerView.ViewHolder(binding.root){
//12
//바인딩처리
fun setMsg(msg:Messages){
with(binding){
textName.setText(msg.userName)
textMsg.setText(msg.msg)
//24시간 HH...
var sdf = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
textDate.setText("${sdf.format(msg.time)}")
}
}
}
}//MsgListAdapter
-Messages.kt
//메시지를 주고받기 위해 사용되는 클래스
//채팅방에서 채팅 목록을 보여주기 위한 코드
//아이디, 메시지, 유저이름, 전송시간
class Messages {
var id: String = ""
var msg: String = ""
var userName: String = ""
var time: Long = 0
constructor()
constructor(msg: String, userName: String){
this.msg = msg
this.userName = userName
this.time = System.currentTimeMillis()
}
}
-activity_main.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/key">
<!-- 로그인&회원가입 화면 -->
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:text="Firebase Chat"
android:textSize="36sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/editId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:ems="10"
android:hint="아이디"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text" />
<EditText
android:id="@+id/editPw"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:ems="10"
android:hint="비밀번호"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editId" />
<Button
android:id="@+id/btnSignin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:backgroundTint="@color/teal_200"
android:text="로그인"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editPw" />
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:text="회원가입일 경우 아이디와 비밀번호 \n 그리고 별명을 입력한 후 \n 회원가입 버튼을 터치합니다."
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnSignin" />
<EditText
android:id="@+id/editName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:ems="10"
android:hint="별명"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text2" />
<Button
android:id="@+id/btnSignUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:backgroundTint="@color/teal_700"
android:text="회원가입"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editName" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
-activity_chat_list.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ChatListActivity"
android:padding="16dp">
<!-- 채팅방 목록 -->
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="채팅방 목록"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="@+id/btnCreate"
app:layout_constraintEnd_toStartOf="@+id/btnCreate"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/btnCreate" />
<Button
android:id="@+id/btnCreate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:backgroundTint="@color/teal_700"
android:text="방 만들기"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerRooms"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnCreate" />
</androidx.constraintlayout.widget.ConstraintLayout>
-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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ChatRoomActivity"
android:padding="16dp">
<!-- 채팅방 -->
<ImageButton
android:id="@+id/btnBack"
android:layout_width="40dp"
android:layout_height="40dp"
android:backgroundTint="@color/teal_700"
android:scaleType="center"
android:src="@drawable/ic_baseline_arrow_back_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textRoomName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:singleLine="true"
android:text="방제목"
android:textAlignment="center"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="@+id/btnBack"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnBack"
app:layout_constraintTop_toTopOf="@+id/btnBack" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerMsg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/editMsg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnBack" />
<EditText
android:id="@+id/editMsg"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:hint="메시지 입력"
app:layout_constraintBottom_toBottomOf="@+id/btnSend"
app:layout_constraintEnd_toStartOf="@+id/btnSend"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/btnSend" />
<ImageButton
android:id="@+id/btnSend"
android:layout_width="50dp"
android:layout_height="50dp"
android:backgroundTint="@color/teal_200"
android:src="@drawable/ic_baseline_send_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- 메시지 목록에서 사용할 아이템 레이아웃 생성
layout우클릭 - New - 새레이아웃리소스파일 - item_msg_list.xml-->
</androidx.constraintlayout.widget.ConstraintLayout>
-item_msg_list
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:layout_weight="10"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- 채팅방 메시지 목록 아이템 -->
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="10"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="이름"
app:layout_constraintEnd_toStartOf="@+id/textMsg"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/textMsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="7"
android:text="메시지"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textName"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
<TextView
android:id="@+id/textDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2022/00/00 12:00:00"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
-결과
이 포스팅에 작성한 내용은 고돈호, ⌜이것이 안드로이드다⌟, 한빛미디어(주), 2022 에서 발췌하였습니다.