From 3136b47838031e9829b48d1a9e6c60b6604e4a96 Mon Sep 17 00:00:00 2001
From: klaus <klaus@vividnext.co.kr>
Date: Tue, 12 Nov 2024 00:32:21 +0900
Subject: [PATCH] =?UTF-8?q?=ED=95=98=ED=8A=B8=20=EB=9E=AD=ED=82=B9=20?=
 =?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../kr/co/vividnext/sodalive/live/LiveApi.kt  |   7 +
 .../vividnext/sodalive/live/LiveRepository.kt |   5 +
 .../sodalive/live/room/LiveRoomActivity.kt    |   8 +
 .../sodalive/live/room/LiveRoomViewModel.kt   |  29 ++++
 .../room/like/GetLiveRoomHeartListResponse.kt |  18 +++
 .../room/like/LiveRoomHeartRankingAdapter.kt  | 144 ++++++++++++++++++
 .../room/like/LiveRoomHeartRankingDialog.kt   |  85 +++++++++++
 .../layout/dialog_live_room_heart_ranking.xml | 120 +++++++++++++++
 .../layout/item_live_room_heart_ranking.xml   | 107 +++++++++++++
 9 files changed, 523 insertions(+)
 create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/live/room/like/GetLiveRoomHeartListResponse.kt
 create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingAdapter.kt
 create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingDialog.kt
 create mode 100644 app/src/main/res/layout/dialog_live_room_heart_ranking.xml
 create mode 100644 app/src/main/res/layout/item_live_room_heart_ranking.xml

diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt
index ef93d9b..4f1cfd4 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveApi.kt
@@ -24,6 +24,7 @@ import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
 import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
 import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
 import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
+import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartListResponse
 import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartTotalResponse
 import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest
 import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
@@ -226,4 +227,10 @@ interface LiveApi {
         @Path("id") id: Long,
         @Header("Authorization") authHeader: String
     ): Single<ApiResponse<GetLiveRoomHeartTotalResponse>>
+
+    @GET("/live/room/{id}/heart-list")
+    fun heartStatus(
+        @Path("id") id: Long,
+        @Header("Authorization") authHeader: String
+    ): Single<ApiResponse<GetLiveRoomHeartListResponse>>
 }
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt
index 5feab7c..2acea0a 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveRepository.kt
@@ -256,4 +256,9 @@ class LiveRepository(
         roomId,
         authHeader = token
     )
+
+    fun heartStatus(roomId: Long, token: String) = api.heartStatus(
+        roomId,
+        authHeader = token
+    )
 }
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt
index 62091cc..6004f3f 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt
@@ -87,6 +87,7 @@ import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewMo
 import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRankingDialog
 import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
 import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
+import kr.co.vividnext.sodalive.live.room.like.LiveRoomHeartRankingDialog
 import kr.co.vividnext.sodalive.live.room.profile.LiveRoomProfileDialog
 import kr.co.vividnext.sodalive.live.room.profile.LiveRoomProfileListAdapter
 import kr.co.vividnext.sodalive.live.room.profile.LiveRoomUserProfileDialog
@@ -538,6 +539,13 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
                 roomId = roomId
             ).show()
         }
+        binding.llHeart.setOnClickListener {
+            LiveRoomHeartRankingDialog(
+                activity = this,
+                layoutInflater = layoutInflater,
+                getHeartRanking = { viewModel.heartStatus(roomId, it) }
+            ).show()
+        }
 
         setupChatAdapter()
         setupSpeakerListAdapter()
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt
index c4e50e8..7312568 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt
@@ -21,6 +21,7 @@ import kr.co.vividnext.sodalive.live.LiveRepository
 import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResponse
 import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
 import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
+import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartListResponse
 import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
 import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
 import kr.co.vividnext.sodalive.live.room.update.EditLiveRoomInfoRequest
@@ -716,6 +717,34 @@ class LiveRoomViewModel(
         )
     }
 
+    fun heartStatus(roomId: Long, onSuccess: (GetLiveRoomHeartListResponse) -> Unit) {
+        _isLoading.value = true
+        compositeDisposable.add(
+            repository.heartStatus(roomId, token = "Bearer ${SharedPreferenceManager.token}")
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(
+                    {
+                        _isLoading.value = false
+                        if (it.success && it.data != null) {
+                            onSuccess(it.data)
+                        } else {
+                            _toastLiveData.postValue(
+                                "하트 현황을 가져오지 못했습니다\n다시 시도해 주세요."
+                            )
+                        }
+                    },
+                    {
+                        _isLoading.value = false
+                        it.message?.let { message -> Logger.e(message) }
+                        _toastLiveData.postValue(
+                            "하트 현황을 가져오지 못했습니다\n다시 시도해 주세요."
+                        )
+                    }
+                )
+        )
+    }
+
     fun getUserRank(userId: Long): Int {
         // 방장 -> -2
         // 스탭 -> -3
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/GetLiveRoomHeartListResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/GetLiveRoomHeartListResponse.kt
new file mode 100644
index 0000000..884a168
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/GetLiveRoomHeartListResponse.kt
@@ -0,0 +1,18 @@
+package kr.co.vividnext.sodalive.live.room.like
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class GetLiveRoomHeartListResponse(
+    @SerializedName("heartList") val heartList: List<GetLiveRoomHeartListItem>,
+    @SerializedName("totalCount") val totalCount: Int,
+    @SerializedName("totalHeart") val totalHeart: Int
+)
+
+@Keep
+data class GetLiveRoomHeartListItem(
+    @SerializedName("profileImage") val profileImage: String,
+    @SerializedName("nickname") val nickname: String,
+    @SerializedName("heart") val heart: Int
+)
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingAdapter.kt
new file mode 100644
index 0000000..3788b2b
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingAdapter.kt
@@ -0,0 +1,144 @@
+package kr.co.vividnext.sodalive.live.room.like
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.RecyclerView
+import coil.load
+import coil.transform.CircleCropTransformation
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.databinding.ItemLiveRoomHeartRankingBinding
+import kr.co.vividnext.sodalive.extensions.dpToPx
+import kr.co.vividnext.sodalive.extensions.moneyFormat
+
+class LiveRoomHeartRankingAdapter :
+    RecyclerView.Adapter<LiveRoomHeartRankingAdapter.ViewHolder>() {
+    val items = mutableListOf<GetLiveRoomHeartListItem>()
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
+        parent.context,
+        ItemLiveRoomHeartRankingBinding.inflate(
+            LayoutInflater.from(parent.context),
+            parent,
+            false
+        )
+    )
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        holder.bind(items[position], position)
+    }
+
+    override fun getItemCount() = items.count()
+
+    inner class ViewHolder(
+        private val context: Context,
+        private val binding: ItemLiveRoomHeartRankingBinding
+    ) : RecyclerView.ViewHolder(binding.root) {
+        @SuppressLint("SetTextI18n")
+        fun bind(item: GetLiveRoomHeartListItem, position: Int) {
+            binding.tvRank.text = "${position + 1}"
+            binding.tvNickname.text = item.nickname
+            binding.tvHeart.text = item.heart.moneyFormat()
+            binding.ivProfile.load(item.profileImage) {
+                crossfade(true)
+                placeholder(R.drawable.ic_place_holder)
+                transformations(CircleCropTransformation())
+            }
+
+            val lp = binding.rlHeartRanking.layoutParams as FrameLayout.LayoutParams
+
+            when (position) {
+                0 -> {
+                    binding.ivBg.setImageResource(R.drawable.bg_circle_ffdc00_ffb600)
+                    binding.ivBg.visibility = View.VISIBLE
+
+                    binding.ivCrown.setImageResource(R.drawable.ic_crown_1)
+                    binding.ivCrown.visibility = View.VISIBLE
+                    binding.root.setBackgroundResource(
+                        if (items.size == 1) {
+                            R.drawable.bg_round_corner_4_7_13181b
+                        } else {
+                            R.drawable.bg_top_round_corner_4_7_13181b
+                        }
+                    )
+
+                    lp.setMargins(
+                        0,
+                        20.dpToPx().toInt(),
+                        0,
+                        if (items.size == 1) {
+                            20.dpToPx().toInt()
+                        } else {
+                            13.3f.dpToPx().toInt()
+                        }
+                    )
+                    binding.rlHeartRanking.layoutParams = lp
+                }
+
+                1 -> {
+                    binding.ivBg.setImageResource(R.drawable.bg_circle_ffffff_9f9f9f)
+                    binding.ivBg.visibility = View.VISIBLE
+
+                    binding.ivCrown.setImageResource(R.drawable.ic_crown_2)
+                    binding.ivCrown.visibility = View.VISIBLE
+
+                    if (items.size == 2) {
+                        binding.root.setBackgroundResource(
+                            R.drawable.bg_bottom_round_corner_4_7_13181b
+                        )
+                    } else {
+                        binding.root.setBackgroundColor(
+                            ContextCompat.getColor(context, R.color.color_13181b)
+                        )
+                    }
+
+                    lp.setMargins(
+                        0,
+                        0,
+                        0,
+                        if (items.size == 2) {
+                            20.dpToPx().toInt()
+                        } else {
+                            13.3f.dpToPx().toInt()
+                        }
+                    )
+                    binding.rlHeartRanking.layoutParams = lp
+                }
+
+                2 -> {
+                    binding.ivBg.setImageResource(R.drawable.bg_circle_e6a77a_c67e4a)
+                    binding.ivBg.visibility = View.VISIBLE
+
+                    binding.ivCrown.setImageResource(R.drawable.ic_crown_3)
+                    binding.ivCrown.visibility = View.VISIBLE
+                    binding.root.setBackgroundResource(
+                        R.drawable.bg_bottom_round_corner_4_7_13181b
+                    )
+
+                    lp.setMargins(
+                        0,
+                        0,
+                        0,
+                        20.dpToPx().toInt()
+                    )
+                    binding.rlHeartRanking.layoutParams = lp
+                }
+
+                else -> {
+                    binding.ivBg.setImageResource(0)
+                    binding.ivBg.visibility = View.GONE
+                    binding.ivCrown.visibility = View.GONE
+                    binding.root.setBackgroundResource(0)
+                    binding.root.background = null
+
+                    lp.setMargins(0, 0, 0, 0)
+                    binding.rlHeartRanking.layoutParams = lp
+                }
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingDialog.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingDialog.kt
new file mode 100644
index 0000000..0fd3a76
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/like/LiveRoomHeartRankingDialog.kt
@@ -0,0 +1,85 @@
+package kr.co.vividnext.sodalive.live.room.like
+
+import android.annotation.SuppressLint
+import android.graphics.Rect
+import android.view.LayoutInflater
+import android.view.View
+import androidx.fragment.app.FragmentActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import kr.co.vividnext.sodalive.databinding.DialogLiveRoomHeartRankingBinding
+import kr.co.vividnext.sodalive.extensions.dpToPx
+import kr.co.vividnext.sodalive.extensions.moneyFormat
+
+@SuppressLint("NotifyDataSetChanged")
+class LiveRoomHeartRankingDialog(
+    private val activity: FragmentActivity,
+    layoutInflater: LayoutInflater,
+    getHeartRanking: ((GetLiveRoomHeartListResponse) -> Unit) -> Unit
+) {
+    private val bottomSheetDialog: BottomSheetDialog = BottomSheetDialog(activity)
+    private val dialogView = DialogLiveRoomHeartRankingBinding.inflate(layoutInflater)
+    private val adapter = LiveRoomHeartRankingAdapter()
+
+    init {
+        bottomSheetDialog.setContentView(dialogView.root)
+        bottomSheetDialog.setCancelable(false)
+
+        dialogView.ivClose.setOnClickListener { bottomSheetDialog.dismiss() }
+        setupRecyclerView()
+
+        getHeartRanking {
+            adapter.items.clear()
+            adapter.items.addAll(it.heartList)
+            adapter.notifyDataSetChanged()
+            dialogView.tvTotalHeart.text = it.totalHeart.moneyFormat()
+            dialogView.tvTotalCount.text = it.totalCount.moneyFormat()
+        }
+    }
+
+    private fun setupRecyclerView() {
+        dialogView.rvHeartRanking.layoutManager = LinearLayoutManager(
+            activity,
+            LinearLayoutManager.VERTICAL,
+            false
+        )
+
+        dialogView.rvHeartRanking.addItemDecoration(object : RecyclerView.ItemDecoration() {
+            override fun getItemOffsets(
+                outRect: Rect,
+                view: View,
+                parent: RecyclerView,
+                state: RecyclerView.State
+            ) {
+                super.getItemOffsets(outRect, view, parent, state)
+
+                outRect.left = 13.3f.dpToPx().toInt()
+                outRect.right = 13.3f.dpToPx().toInt()
+
+                when (parent.getChildAdapterPosition(view)) {
+                    0, 1, 2 -> {
+                        outRect.top = 0
+                        outRect.bottom = 0
+                    }
+
+                    3 -> {
+                        outRect.top = 20.dpToPx().toInt()
+                        outRect.bottom = 6.7f.dpToPx().toInt()
+                    }
+
+                    else -> {
+                        outRect.top = 6.7f.dpToPx().toInt()
+                        outRect.bottom = 6.7f.dpToPx().toInt()
+                    }
+                }
+            }
+        })
+
+        dialogView.rvHeartRanking.adapter = adapter
+    }
+
+    fun show() {
+        bottomSheetDialog.show()
+    }
+}
diff --git a/app/src/main/res/layout/dialog_live_room_heart_ranking.xml b/app/src/main/res/layout/dialog_live_room_heart_ranking.xml
new file mode 100644
index 0000000..ea8288c
--- /dev/null
+++ b/app/src/main/res/layout/dialog_live_room_heart_ranking.xml
@@ -0,0 +1,120 @@
+<?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="match_parent"
+    android:background="@drawable/bg_top_round_corner_16_7_222222"
+    android:orientation="vertical">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="20dp"
+            android:layout_marginTop="20dp"
+            android:fontFamily="@font/gmarket_sans_bold"
+            android:text="현재 라이브 하트랭킹"
+            android:textColor="@color/color_eeeeee"
+            android:textSize="16.7sp" />
+
+        <ImageView
+            android:id="@+id/iv_close"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:contentDescription="@null"
+            android:paddingHorizontal="20dp"
+            android:paddingVertical="18.7dp"
+            android:src="@drawable/ic_close_white" />
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:id="@+id/rl_total_can"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="13.3dp"
+        android:layout_marginTop="7.7dp"
+        android:background="@drawable/bg_round_corner_8_13181b"
+        android:paddingHorizontal="18.7dp"
+        android:paddingVertical="10.7dp">
+
+        <TextView
+            android:id="@+id/tv_total_heart_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@font/gmarket_sans_bold"
+            android:text="합계"
+            android:textColor="@color/color_d2d2d2"
+            android:textSize="13.3sp" />
+
+        <TextView
+            android:id="@+id/tv_total_heart"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="2.7dp"
+            android:layout_toStartOf="@+id/tv_total_can_unit"
+            android:layout_toEndOf="@+id/tv_total_heart_title"
+            android:fontFamily="@font/gmarket_sans_medium"
+            android:gravity="end"
+            android:textColor="@color/color_80d8ff"
+            android:textSize="14sp"
+            tools:text="1,999,999" />
+
+        <TextView
+            android:id="@+id/tv_total_can_unit"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:fontFamily="@font/gmarket_sans_medium"
+            android:text="하트"
+            android:textColor="@color/color_bbbbbb"
+            android:textSize="10.7sp"
+            tools:ignore="SmallSp" />
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/ll_total_count"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="13.3dp"
+        android:layout_marginTop="13.3dp"
+        android:layout_marginBottom="8dp"
+        android:gravity="center_vertical">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@font/gmarket_sans_medium"
+            android:text="전체"
+            android:textColor="@color/color_eeeeee"
+            android:textSize="14.7sp" />
+
+        <TextView
+            android:id="@+id/tv_total_count"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="6.7dp"
+            android:fontFamily="@font/gmarket_sans_medium"
+            android:textColor="@color/color_80d8ff"
+            android:textSize="12sp"
+            tools:text="56" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@font/gmarket_sans_medium"
+            android:text=" 명"
+            android:textColor="@color/color_777777"
+            android:textSize="12sp" />
+    </LinearLayout>
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/rv_heart_ranking"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scrollbars="none" />
+</LinearLayout>
diff --git a/app/src/main/res/layout/item_live_room_heart_ranking.xml b/app/src/main/res/layout/item_live_room_heart_ranking.xml
new file mode 100644
index 0000000..1ec0d49
--- /dev/null
+++ b/app/src/main/res/layout/item_live_room_heart_ranking.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout 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"
+    tools:background="@color/black">
+
+    <RelativeLayout
+        android:id="@+id/rl_heart_ranking"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        tools:background="@color/black"
+        tools:ignore="UselessParent">
+
+        <TextView
+            android:id="@+id/tv_rank"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="13.3dp"
+            android:ellipsize="end"
+            android:fontFamily="@font/gmarket_sans_bold"
+            android:maxLength="10"
+            android:textColor="@color/color_eeeeee"
+            android:textSize="13.3sp"
+            tools:text="1" />
+
+        <RelativeLayout
+            android:id="@+id/rl_profile"
+            android:layout_width="65dp"
+            android:layout_height="65dp"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="13.3dp"
+            android:layout_toEndOf="@+id/tv_rank">
+
+            <ImageView
+                android:id="@+id/iv_bg"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:contentDescription="@null" />
+
+            <ImageView
+                android:id="@+id/iv_profile"
+                android:layout_width="60dp"
+                android:layout_height="60dp"
+                android:layout_centerInParent="true"
+                android:contentDescription="@null"
+                tools:src="@mipmap/ic_launcher" />
+
+            <ImageView
+                android:id="@+id/iv_crown"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentBottom="true"
+                android:contentDescription="@null" />
+        </RelativeLayout>
+
+        <TextView
+            android:id="@+id/tv_nickname"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="13.3dp"
+            android:layout_toEndOf="@+id/rl_profile"
+            android:ellipsize="end"
+            android:fontFamily="@font/gmarket_sans_medium"
+            android:lines="1"
+            android:maxLength="12"
+            android:textColor="@color/color_eeeeee"
+            android:textSize="13.3sp"
+            tools:text="Remix" />
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:layout_marginHorizontal="13.3dp"
+            android:layout_toEndOf="@+id/tv_nickname"
+            android:gravity="end|center_vertical"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/tv_heart"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="4dp"
+                android:fontFamily="@font/gmarket_sans_medium"
+                android:gravity="end"
+                android:textColor="@color/color_3bb9f1"
+                android:textSize="13.3sp"
+                tools:text="1000" />
+
+            <TextView
+                android:id="@+id/tv_donation_can_unit"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@font/gmarket_sans_medium"
+                android:gravity="end"
+                android:text="하트"
+                android:textColor="@color/color_eeeeee"
+                android:textSize="10.7sp"
+                tools:ignore="SmallSp" />
+        </LinearLayout>
+    </RelativeLayout>
+</FrameLayout>