feat(ui): 톡 탭
- api, viewmodel, repository 연결 - 채팅방 리스트 UI 추가
This commit is contained in:
		@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.talk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.core.Single
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.ApiResponse
 | 
				
			||||||
 | 
					import retrofit2.http.GET
 | 
				
			||||||
 | 
					import retrofit2.http.Header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface TalkApi {
 | 
				
			||||||
 | 
					    @GET("/api/chat/talk/rooms")
 | 
				
			||||||
 | 
					    fun getTalkRooms(
 | 
				
			||||||
 | 
					        @Header("Authorization") authHeader: String
 | 
				
			||||||
 | 
					    ): Single<ApiResponse<TalkRoomResponse>>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.talk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data class TalkRoom(
 | 
				
			||||||
 | 
					    val id: Long,
 | 
				
			||||||
 | 
					    val profileImageUrl: String,
 | 
				
			||||||
 | 
					    val characterName: String,
 | 
				
			||||||
 | 
					    val characterType: String,
 | 
				
			||||||
 | 
					    val lastMessageTime: String,
 | 
				
			||||||
 | 
					    val lastMessage: String
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.talk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data class TalkRoomResponse(
 | 
				
			||||||
 | 
					    val totalCount: Int,
 | 
				
			||||||
 | 
					    val items: List<TalkRoom>
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.talk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.bumptech.glide.Glide
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.databinding.ItemTalkBinding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TalkTabAdapter(
 | 
				
			||||||
 | 
					    private val onItemClick: (TalkRoom) -> Unit
 | 
				
			||||||
 | 
					) : ListAdapter<TalkRoom, TalkTabAdapter.TalkViewHolder>(TalkDiffCallback()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TalkViewHolder {
 | 
				
			||||||
 | 
					        val binding = ItemTalkBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
				
			||||||
 | 
					        return TalkViewHolder(binding)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onBindViewHolder(holder: TalkViewHolder, position: Int) {
 | 
				
			||||||
 | 
					        holder.bind(getItem(position))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    inner class TalkViewHolder(
 | 
				
			||||||
 | 
					        private val binding: ItemTalkBinding
 | 
				
			||||||
 | 
					    ) : RecyclerView.ViewHolder(binding.root) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        init {
 | 
				
			||||||
 | 
					            binding.root.setOnClickListener {
 | 
				
			||||||
 | 
					                val position = bindingAdapterPosition
 | 
				
			||||||
 | 
					                if (position != RecyclerView.NO_POSITION) {
 | 
				
			||||||
 | 
					                    onItemClick(getItem(position))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun bind(talkRoom: TalkRoom) {
 | 
				
			||||||
 | 
					            binding.apply {
 | 
				
			||||||
 | 
					                // 프로필 이미지 로드
 | 
				
			||||||
 | 
					                Glide.with(ivProfile.context)
 | 
				
			||||||
 | 
					                    .load(talkRoom.profileImageUrl)
 | 
				
			||||||
 | 
					                    .into(ivProfile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 텍스트 설정
 | 
				
			||||||
 | 
					                tvCharacterName.text = talkRoom.characterName
 | 
				
			||||||
 | 
					                tvCharacterType.text = talkRoom.characterType
 | 
				
			||||||
 | 
					                tvLastTime.text = talkRoom.lastMessageTime
 | 
				
			||||||
 | 
					                tvLastMessage.text = talkRoom.lastMessage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 캐릭터 유형에 따른 배경 설정
 | 
				
			||||||
 | 
					                val backgroundResId = when (talkRoom.characterType.lowercase()) {
 | 
				
			||||||
 | 
					                    "character" -> kr.co.vividnext.sodalive.R.drawable.bg_character_type_badge_character
 | 
				
			||||||
 | 
					                    "clone" -> kr.co.vividnext.sodalive.R.drawable.bg_character_type_badge_clone
 | 
				
			||||||
 | 
					                    "creator" -> kr.co.vividnext.sodalive.R.drawable.bg_character_type_badge_creator
 | 
				
			||||||
 | 
					                    else -> kr.co.vividnext.sodalive.R.drawable.bg_character_type_badge_character
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                tvCharacterType.setBackgroundResource(backgroundResId)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private class TalkDiffCallback : DiffUtil.ItemCallback<TalkRoom>() {
 | 
				
			||||||
 | 
					        override fun areItemsTheSame(oldItem: TalkRoom, newItem: TalkRoom): Boolean {
 | 
				
			||||||
 | 
					            return oldItem.id == newItem.id
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        override fun areContentsTheSame(oldItem: TalkRoom, newItem: TalkRoom): Boolean {
 | 
				
			||||||
 | 
					            return oldItem == newItem
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,14 +2,70 @@ package kr.co.vividnext.sodalive.chat.talk
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import android.view.View
 | 
					import android.view.View
 | 
				
			||||||
 | 
					import android.widget.Toast
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.LinearLayoutManager
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseFragment
 | 
					import kr.co.vividnext.sodalive.base.BaseFragment
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
				
			||||||
import kr.co.vividnext.sodalive.databinding.FragmentTalkTabBinding
 | 
					import kr.co.vividnext.sodalive.databinding.FragmentTalkTabBinding
 | 
				
			||||||
 | 
					import org.koin.android.ext.android.inject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TalkTabFragment : BaseFragment<FragmentTalkTabBinding>(
 | 
					class TalkTabFragment : BaseFragment<FragmentTalkTabBinding>(
 | 
				
			||||||
    FragmentTalkTabBinding::inflate
 | 
					    FragmentTalkTabBinding::inflate
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    private val viewModel: TalkTabViewModel by inject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private lateinit var adapter: TalkTabAdapter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private lateinit var loadingDialog: LoadingDialog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
					    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
				
			||||||
        super.onViewCreated(view, savedInstanceState)
 | 
					        super.onViewCreated(view, savedInstanceState)
 | 
				
			||||||
        // 톡 탭 초기화 로직
 | 
					
 | 
				
			||||||
 | 
					        loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
 | 
				
			||||||
 | 
					        setupRecyclerView()
 | 
				
			||||||
 | 
					        observeViewModel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 데이터 로드
 | 
				
			||||||
 | 
					        viewModel.loadTalkRooms()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun setupRecyclerView() {
 | 
				
			||||||
 | 
					        adapter = TalkTabAdapter { talkRoom ->
 | 
				
			||||||
 | 
					            // 대화방 클릭 이벤트 처리
 | 
				
			||||||
 | 
					            // TODO: 대화방 상세 화면으로 이동
 | 
				
			||||||
 | 
					            Toast.makeText(
 | 
				
			||||||
 | 
					                requireContext(),
 | 
				
			||||||
 | 
					                "대화방 ${talkRoom.characterName} 클릭됨",
 | 
				
			||||||
 | 
					                Toast.LENGTH_SHORT
 | 
				
			||||||
 | 
					            ).show()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding.rvTalk.apply {
 | 
				
			||||||
 | 
					            layoutManager = LinearLayoutManager(requireContext())
 | 
				
			||||||
 | 
					            adapter = this@TalkTabFragment.adapter
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun observeViewModel() {
 | 
				
			||||||
 | 
					        // 대화방 목록 관찰
 | 
				
			||||||
 | 
					        viewModel.talkRooms.observe(viewLifecycleOwner) {
 | 
				
			||||||
 | 
					            if (it.isNotEmpty()) {
 | 
				
			||||||
 | 
					                adapter.submitList(it)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 로딩 상태 관찰
 | 
				
			||||||
 | 
					        viewModel.isLoading.observe(viewLifecycleOwner) {
 | 
				
			||||||
 | 
					            if (it) {
 | 
				
			||||||
 | 
					                loadingDialog.show(screenWidth)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                loadingDialog.dismiss()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 오류 관찰
 | 
				
			||||||
 | 
					        viewModel.toastLiveData.observe(viewLifecycleOwner) {
 | 
				
			||||||
 | 
					            Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.talk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TalkTabRepository(private val api: TalkApi) {
 | 
				
			||||||
 | 
					    fun getTalkRooms(
 | 
				
			||||||
 | 
					        token: String
 | 
				
			||||||
 | 
					    ) = api.getTalkRooms(authHeader = token)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.talk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.lifecycle.LiveData
 | 
				
			||||||
 | 
					import androidx.lifecycle.MutableLiveData
 | 
				
			||||||
 | 
					import com.orhanobut.logger.Logger
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.schedulers.Schedulers
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.base.BaseViewModel
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TalkTabViewModel(private val repository: TalkTabRepository) : BaseViewModel() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _talkRooms = MutableLiveData<List<TalkRoom>>()
 | 
				
			||||||
 | 
					    val talkRooms: LiveData<List<TalkRoom>> = _talkRooms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _isLoading = MutableLiveData<Boolean>()
 | 
				
			||||||
 | 
					    val isLoading: LiveData<Boolean> = _isLoading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _toastLiveData = MutableLiveData<String?>()
 | 
				
			||||||
 | 
					    val toastLiveData: LiveData<String?>
 | 
				
			||||||
 | 
					        get() = _toastLiveData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun loadTalkRooms() {
 | 
				
			||||||
 | 
					        _isLoading.value = true
 | 
				
			||||||
 | 
					        compositeDisposable.add(
 | 
				
			||||||
 | 
					            repository.getTalkRooms(token = "Bearer ${SharedPreferenceManager.token}")
 | 
				
			||||||
 | 
					                .subscribeOn(Schedulers.io())
 | 
				
			||||||
 | 
					                .observeOn(AndroidSchedulers.mainThread())
 | 
				
			||||||
 | 
					                .subscribe(
 | 
				
			||||||
 | 
					                    { response ->
 | 
				
			||||||
 | 
					                        _isLoading.value = false
 | 
				
			||||||
 | 
					                        if (response.success) {
 | 
				
			||||||
 | 
					                            _talkRooms.value = response.data?.items ?: emptyList()
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            _toastLiveData.value =
 | 
				
			||||||
 | 
					                                response.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        _isLoading.value = false
 | 
				
			||||||
 | 
					                        it.message?.let { message -> Logger.e(message) }
 | 
				
			||||||
 | 
					                        _toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -67,6 +67,9 @@ import kr.co.vividnext.sodalive.audition.role.AuditionRoleDetailViewModel
 | 
				
			|||||||
import kr.co.vividnext.sodalive.chat.character.CharacterApi
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterApi
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.CharacterTabRepository
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterTabRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.CharacterTabViewModel
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterTabViewModel
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkTabRepository
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkTabViewModel
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.ApiBuilder
 | 
					import kr.co.vividnext.sodalive.common.ApiBuilder
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.ObjectBox
 | 
					import kr.co.vividnext.sodalive.common.ObjectBox
 | 
				
			||||||
import kr.co.vividnext.sodalive.explorer.ExplorerApi
 | 
					import kr.co.vividnext.sodalive.explorer.ExplorerApi
 | 
				
			||||||
@@ -249,6 +252,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
 | 
				
			|||||||
        single { ApiBuilder().build(get(), PointStatusApi::class.java) }
 | 
					        single { ApiBuilder().build(get(), PointStatusApi::class.java) }
 | 
				
			||||||
        single { ApiBuilder().build(get(), HomeApi::class.java) }
 | 
					        single { ApiBuilder().build(get(), HomeApi::class.java) }
 | 
				
			||||||
        single { ApiBuilder().build(get(), CharacterApi::class.java) }
 | 
					        single { ApiBuilder().build(get(), CharacterApi::class.java) }
 | 
				
			||||||
 | 
					        single { ApiBuilder().build(get(), TalkApi::class.java) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val viewModelModule = module {
 | 
					    private val viewModelModule = module {
 | 
				
			||||||
@@ -347,6 +351,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
 | 
				
			|||||||
        viewModel { PointStatusViewModel(get()) }
 | 
					        viewModel { PointStatusViewModel(get()) }
 | 
				
			||||||
        viewModel { HomeViewModel(get(), get()) }
 | 
					        viewModel { HomeViewModel(get(), get()) }
 | 
				
			||||||
        viewModel { CharacterTabViewModel(get()) }
 | 
					        viewModel { CharacterTabViewModel(get()) }
 | 
				
			||||||
 | 
					        viewModel { TalkTabViewModel(get()) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val repositoryModule = module {
 | 
					    private val repositoryModule = module {
 | 
				
			||||||
@@ -392,6 +397,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
 | 
				
			|||||||
        factory { PointStatusRepository(get()) }
 | 
					        factory { PointStatusRepository(get()) }
 | 
				
			||||||
        factory { HomeRepository(get()) }
 | 
					        factory { HomeRepository(get()) }
 | 
				
			||||||
        factory { CharacterTabRepository(get()) }
 | 
					        factory { CharacterTabRepository(get()) }
 | 
				
			||||||
 | 
					        factory { TalkTabRepository(get()) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<shape xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:shape="rectangle">
 | 
				
			||||||
 | 
					    <solid android:color="#009D68" />
 | 
				
			||||||
 | 
					    <corners android:radius="6dp" />
 | 
				
			||||||
 | 
					</shape>
 | 
				
			||||||
@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<shape xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:shape="rectangle">
 | 
				
			||||||
 | 
					    <solid android:color="#0020C9" />
 | 
				
			||||||
 | 
					    <corners android:radius="6dp" />
 | 
				
			||||||
 | 
					</shape>
 | 
				
			||||||
@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<shape xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:shape="rectangle">
 | 
				
			||||||
 | 
					    <solid android:color="#F86660" />
 | 
				
			||||||
 | 
					    <corners android:radius="6dp" />
 | 
				
			||||||
 | 
					</shape>
 | 
				
			||||||
@@ -5,15 +5,14 @@
 | 
				
			|||||||
    android:layout_height="match_parent"
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
    android:background="@color/black">
 | 
					    android:background="@color/black">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <TextView
 | 
					    <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
        android:layout_width="wrap_content"
 | 
					        android:id="@+id/rv_talk"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
        android:text="톡 탭"
 | 
					        android:layout_height="0dp"
 | 
				
			||||||
        android:textColor="@color/white"
 | 
					        android:clipToPadding="false"
 | 
				
			||||||
        android:textSize="20sp"
 | 
					        android:padding="24dp"
 | 
				
			||||||
        app:layout_constraintBottom_toBottomOf="parent"
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
        app:layout_constraintEnd_toEndOf="parent"
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
        app:layout_constraintStart_toStartOf="parent"
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
        app:layout_constraintTop_toTopOf="parent" />
 | 
					        app:layout_constraintTop_toTopOf="parent" />
 | 
				
			||||||
 | 
					 | 
				
			||||||
</androidx.constraintlayout.widget.ConstraintLayout>
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										92
									
								
								app/src/main/res/layout/item_talk.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								app/src/main/res/layout/item_talk.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					    android:background="@color/black"
 | 
				
			||||||
 | 
					    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					    android:orientation="horizontal">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 프로필 이미지 -->
 | 
				
			||||||
 | 
					    <ImageView
 | 
				
			||||||
 | 
					        android:id="@+id/iv_profile"
 | 
				
			||||||
 | 
					        android:layout_width="76dp"
 | 
				
			||||||
 | 
					        android:layout_height="76dp"
 | 
				
			||||||
 | 
					        android:background="@color/color_777777"
 | 
				
			||||||
 | 
					        android:contentDescription="@null"
 | 
				
			||||||
 | 
					        android:scaleType="centerCrop" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 텍스트 영역 -->
 | 
				
			||||||
 | 
					    <LinearLayout
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_marginStart="13dp"
 | 
				
			||||||
 | 
					        android:layout_weight="1"
 | 
				
			||||||
 | 
					        android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 상단 영역: 캐릭터 이름 + 유형 배지 + 시간 -->
 | 
				
			||||||
 | 
					        <LinearLayout
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:gravity="center_vertical"
 | 
				
			||||||
 | 
					            android:orientation="horizontal">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 왼쪽 영역: 캐릭터 이름 + 유형 배지 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_weight="1"
 | 
				
			||||||
 | 
					                android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                android:orientation="horizontal">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 캐릭터 이름 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_character_name"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                    android:textColor="@color/white"
 | 
				
			||||||
 | 
					                    android:textSize="18sp"
 | 
				
			||||||
 | 
					                    tools:text="정인이" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 캐릭터 유형 배지 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/tv_character_type"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginStart="4dp"
 | 
				
			||||||
 | 
					                    android:background="@drawable/bg_character_type_badge_character"
 | 
				
			||||||
 | 
					                    android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                    android:paddingHorizontal="5dp"
 | 
				
			||||||
 | 
					                    android:paddingVertical="1dp"
 | 
				
			||||||
 | 
					                    android:textColor="#D9FCF4"
 | 
				
			||||||
 | 
					                    android:textSize="12sp"
 | 
				
			||||||
 | 
					                    tools:text="Character" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 시간 -->
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:id="@+id/tv_last_time"
 | 
				
			||||||
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                android:textColor="#78909C"
 | 
				
			||||||
 | 
					                android:textSize="12sp"
 | 
				
			||||||
 | 
					                tools:text="6월 15일" />
 | 
				
			||||||
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 마지막 대화 내용 -->
 | 
				
			||||||
 | 
					        <TextView
 | 
				
			||||||
 | 
					            android:id="@+id/tv_last_message"
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_marginTop="6dp"
 | 
				
			||||||
 | 
					            android:ellipsize="end"
 | 
				
			||||||
 | 
					            android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					            android:maxLines="2"
 | 
				
			||||||
 | 
					            android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					            android:textSize="14sp"
 | 
				
			||||||
 | 
					            tools:text="태풍온다잖아 ㅜㅜ\n조심해 어디 나가지 말고 집에만..." />
 | 
				
			||||||
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user