feat(chat-room): Coil 기반 프로필 이미지 로딩 유틸 도입 및 적용
채팅방의 프로필 이미지 로딩을 공용 유틸(loadProfileImage)로 통일하고 플레이스홀더/에러 처리 및 둥근 모서리 변환을 기본 적용했습니다. - ImageLoader.kt 추가: loadProfileImage(ImageView, url, cornerRadiusDp) - ChatMessageAdapter: AI 프로필 이미지 로딩에 유틸 적용 - ChatRoomActivity: 헤더 프로필 이미지 로딩에 유틸 적용 (배경 이미지는 기존 유지)
This commit is contained in:
@@ -16,7 +16,6 @@ import android.widget.TextView
|
|||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.load
|
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemChatAiMessageBinding
|
import kr.co.vividnext.sodalive.databinding.ItemChatAiMessageBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemChatTypingIndicatorBinding
|
import kr.co.vividnext.sodalive.databinding.ItemChatTypingIndicatorBinding
|
||||||
@@ -283,12 +282,9 @@ class ChatMessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|||||||
binding.tvName.text = displayName ?: ""
|
binding.tvName.text = displayName ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// 프로필 이미지 로딩 (Coil)
|
// 프로필 이미지 로딩 (공용 유틸 + 둥근 모서리 적용)
|
||||||
if (binding.ivProfile.isVisible) {
|
if (binding.ivProfile.isVisible) {
|
||||||
binding.ivProfile.load(data.profileImageUrl) {
|
loadProfileImage(binding.ivProfile, data.profileImageUrl)
|
||||||
placeholder(R.drawable.ic_placeholder_profile)
|
|
||||||
error(R.drawable.ic_placeholder_profile)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 그룹 내부 간격 최소화 (상단 패딩 축소)
|
// 그룹 내부 간격 최소화 (상단 패딩 축소)
|
||||||
|
|||||||
@@ -93,11 +93,8 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
|||||||
binding.tvName.text = info.name
|
binding.tvName.text = info.name
|
||||||
binding.tvName.isVisible = info.name.isNotBlank()
|
binding.tvName.isVisible = info.name.isNotBlank()
|
||||||
|
|
||||||
// 프로필 이미지 (Coil)
|
// 프로필 이미지 (공용 유틸 + 둥근 모서리 적용)
|
||||||
binding.ivProfile.load(info.profileImageUrl) {
|
loadProfileImage(binding.ivProfile, info.profileImageUrl)
|
||||||
placeholder(R.drawable.ic_placeholder_profile)
|
|
||||||
error(R.drawable.ic_placeholder_profile)
|
|
||||||
}
|
|
||||||
// 배경 프로필 이미지 (5.5)
|
// 배경 프로필 이미지 (5.5)
|
||||||
binding.ivBackgroundProfile.load(info.profileImageUrl) {
|
binding.ivBackgroundProfile.load(info.profileImageUrl) {
|
||||||
placeholder(R.drawable.ic_placeholder_profile)
|
placeholder(R.drawable.ic_placeholder_profile)
|
||||||
@@ -389,7 +386,7 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
|||||||
compositeDisposable.add(localDisposable)
|
compositeDisposable.add(localDisposable)
|
||||||
|
|
||||||
// 2) 서버 통합 API로 동기화 및 UI 갱신
|
// 2) 서버 통합 API로 동기화 및 UI 갱신
|
||||||
val token = "Bearer ${kr.co.vividnext.sodalive.common.SharedPreferenceManager.token}"
|
val token = "Bearer ${SharedPreferenceManager.token}"
|
||||||
val networkDisposable = chatRepository.enterChatRoom(token = token, roomId = roomId)
|
val networkDisposable = chatRepository.enterChatRoom(token = token, roomId = roomId)
|
||||||
.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
|
.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
|
||||||
.subscribe({ response ->
|
.subscribe({ response ->
|
||||||
@@ -491,7 +488,8 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
|||||||
}
|
}
|
||||||
val cursor: Long? = nextCursor ?: fallbackOldestCreatedAt
|
val cursor: Long? = nextCursor ?: fallbackOldestCreatedAt
|
||||||
|
|
||||||
val disposable = chatRepository.loadMoreMessages(token = token, roomId = roomId, cursor = cursor)
|
val disposable =
|
||||||
|
chatRepository.loadMoreMessages(token = token, roomId = roomId, cursor = cursor)
|
||||||
.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
|
.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
|
||||||
.subscribe({ response ->
|
.subscribe({ response ->
|
||||||
// 서버에서 받은 메시지(이전 것들)를 오래된 -> 최신 순으로 정렬
|
// 서버에서 받은 메시지(이전 것들)를 오래된 -> 최신 순으로 정렬
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 이미지 로딩 유틸리티
|
||||||
|
* - Coil을 사용하여 프로필 이미지를 로딩한다.
|
||||||
|
* - 플레이스홀더/에러 이미지는 ic_placeholder_profile을 사용한다.
|
||||||
|
* - 둥근 모서리 변환을 기본 적용한다.
|
||||||
|
*/
|
||||||
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import coil.load
|
||||||
|
import coil.transform.RoundedCornersTransformation
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dp 값을 픽셀로 변환
|
||||||
|
*/
|
||||||
|
private fun ImageView.dpToPx(dp: Float): Float {
|
||||||
|
return dp * this.resources.displayMetrics.density
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 프로필 이미지 로딩 공용 함수
|
||||||
|
*
|
||||||
|
* @param url 이미지 URL (null 또는 빈 값이면 플레이스홀더로 대체)
|
||||||
|
* @param cornerRadiusDp 둥근 모서리 반경(dp). 기본 12dp
|
||||||
|
* @param placeholderRes 플레이스홀더/에러 리소스
|
||||||
|
*/
|
||||||
|
fun loadProfileImage(
|
||||||
|
imageView: ImageView,
|
||||||
|
url: String?,
|
||||||
|
cornerRadiusDp: Float = 12f,
|
||||||
|
@DrawableRes placeholderRes: Int = R.drawable.ic_placeholder_profile
|
||||||
|
) {
|
||||||
|
val targetUrl = url?.takeIf { it.isNotBlank() }
|
||||||
|
val radiusPx = imageView.dpToPx(cornerRadiusDp)
|
||||||
|
|
||||||
|
if (targetUrl != null) {
|
||||||
|
imageView.load(targetUrl) {
|
||||||
|
placeholder(placeholderRes)
|
||||||
|
error(placeholderRes)
|
||||||
|
transformations(RoundedCornersTransformation(radiusPx))
|
||||||
|
crossfade(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageView.load(placeholderRes) {
|
||||||
|
placeholder(placeholderRes)
|
||||||
|
error(placeholderRes)
|
||||||
|
transformations(RoundedCornersTransformation(radiusPx))
|
||||||
|
crossfade(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user