refactor: 캐릭터 탭 내부에서 사용하는 Adapter 코드를 ViewBinding 코드로 리팩토링

This commit is contained in:
2025-08-04 21:02:05 +09:00
parent b919691689
commit 33bdaa7dbd
6 changed files with 106 additions and 104 deletions

View File

@@ -4,6 +4,5 @@ data class Character(
val id: String,
val name: String,
val description: String,
val imageUrl: String,
val ranking: Int? = null // 인기 캐릭터에서만 사용
val imageUrl: String
)

View File

@@ -1,14 +1,13 @@
package kr.co.vividnext.sodalive.chat.character
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.chat.character.Character
import com.bumptech.glide.Glide
import kr.co.vividnext.sodalive.databinding.ItemCharacterBinding
class CharacterAdapter(
private var characters: List<Character> = emptyList(),
@@ -16,46 +15,47 @@ class CharacterAdapter(
private val onCharacterClick: (Character) -> Unit = {}
) : RecyclerView.Adapter<CharacterAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivCharacter: ImageView = itemView.findViewById(R.id.iv_character)
val tvCharacterName: TextView = itemView.findViewById(R.id.tv_character_name)
val tvCharacterDescription: TextView = itemView.findViewById(R.id.tv_character_description)
val llRanking: LinearLayout = itemView.findViewById(R.id.ll_ranking)
val tvRanking: TextView = itemView.findViewById(R.id.tv_ranking)
inner class ViewHolder(
private val context: Context,
private val binding: ItemCharacterBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(character: Character, index: Int) {
binding.tvCharacterName.text = character.name
binding.tvCharacterDescription.text = character.description
// 순위 표시 여부 결정
if (showRanking) {
binding.llRanking.visibility = View.VISIBLE
binding.tvRanking.text = (index + 1).toString()
} else {
binding.llRanking.visibility = View.GONE
}
Glide.with(context)
.load(character.imageUrl)
.into(binding.ivCharacter)
binding.root.setOnClickListener { onCharacterClick(character) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_character, parent, false)
return ViewHolder(view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemCharacterBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val character = characters[position]
holder.tvCharacterName.text = character.name
holder.tvCharacterDescription.text = character.description
// 순위 표시 여부 결정
if (showRanking && character.ranking != null) {
holder.llRanking.visibility = View.VISIBLE
holder.tvRanking.text = character.ranking.toString()
} else {
holder.llRanking.visibility = View.GONE
}
// TODO: 이미지 로딩 라이브러리 사용 (Glide, Picasso 등)
// Glide.with(holder.itemView.context)
// .load(character.imageUrl)
// .into(holder.ivCharacter)
holder.itemView.setOnClickListener {
onCharacterClick(character)
}
holder.bind(characters[position], index = position)
}
override fun getItemCount(): Int = characters.size
@SuppressLint("NotifyDataSetChanged")
fun updateCharacters(newCharacters: List<Character>) {
characters = newCharacters
notifyDataSetChanged()

View File

@@ -72,7 +72,7 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
}
// 전체보기 버튼 클릭 리스너
binding.tvFamousCharacterAll.setOnClickListener {
binding.tvPopularCharacterAll.setOnClickListener {
// 인기 캐릭터 전체보기 처리
}
@@ -106,11 +106,11 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
private fun loadPopularCharacters() {
// TODO: 서버에서 인기 캐릭터 데이터 로드
val popularCharacters = listOf(
Character("1", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", "", 1),
Character("2", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", "", 2),
Character("3", "캐릭터 이름", "#태그#태그#태그", "", 3),
Character("4", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", "", 4),
Character("5", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", "", 5)
Character("1", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
Character("2", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
Character("3", "캐릭터 이름", "#태그#태그#태그", ""),
Character("4", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
Character("5", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", "")
)
popularCharacterAdapter.updateCharacters(popularCharacters)

View File

@@ -1,52 +1,58 @@
package kr.co.vividnext.sodalive.chat.character.curation
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.chat.character.CharacterAdapter
import kr.co.vividnext.sodalive.chat.character.Character
import kr.co.vividnext.sodalive.chat.character.curation.CurationSection
import kr.co.vividnext.sodalive.chat.character.CharacterAdapter
import kr.co.vividnext.sodalive.databinding.ItemCurationSectionBinding
class CurationSectionAdapter(
private var sections: List<CurationSection> = emptyList(),
private val onCharacterClick: (Character) -> Unit = {}
) : RecyclerView.Adapter<CurationSectionAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvSectionTitle: TextView = itemView.findViewById(R.id.tv_section_title)
val rvCharacters: RecyclerView = itemView.findViewById(R.id.rv_characters)
inner class ViewHolder(
private val binding: ItemCurationSectionBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(section: CurationSection) {
binding.tvSectionTitle.text = section.title
// 캐릭터 리스트 설정
val characterAdapter = CharacterAdapter(
characters = section.characters,
showRanking = false,
onCharacterClick = onCharacterClick
)
binding.rvCharacters.apply {
layoutManager = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
adapter = characterAdapter
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_curation_section, parent, false)
return ViewHolder(view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemCurationSectionBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val section = sections[position]
holder.tvSectionTitle.text = section.title
// 캐릭터 리스트 설정
val characterAdapter = CharacterAdapter(
characters = section.characters,
showRanking = false,
onCharacterClick = onCharacterClick
)
holder.rvCharacters.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
adapter = characterAdapter
}
holder.bind(sections[position])
}
override fun getItemCount(): Int = sections.size
@SuppressLint("NotifyDataSetChanged")
fun updateSections(newSections: List<CurationSection>) {
sections = newSections
notifyDataSetChanged()

View File

@@ -1,43 +1,43 @@
package kr.co.vividnext.sodalive.chat.character.recent
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import com.bumptech.glide.Glide
import kr.co.vividnext.sodalive.databinding.ItemRecentCharacterBinding
class RecentCharacterAdapter(
private var characters: List<RecentCharacter> = emptyList(),
private val onCharacterClick: (RecentCharacter) -> Unit = {}
) : RecyclerView.Adapter<RecentCharacterAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivProfile: ImageView = itemView.findViewById(R.id.iv_profile)
val tvName: TextView = itemView.findViewById(R.id.tv_name)
inner class ViewHolder(
private val context: Context,
private val binding: ItemRecentCharacterBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(character: RecentCharacter) {
binding.tvName.text = character.name
Glide.with(context)
.load(character.profileImageUrl)
.into(binding.ivProfile)
binding.root.setOnClickListener { onCharacterClick(character) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_recent_character, parent, false)
return ViewHolder(view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemRecentCharacterBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val character = characters[position]
holder.tvName.text = character.name
// TODO: 이미지 로딩 라이브러리 사용 (Glide, Picasso 등)
// Glide.with(holder.itemView.context)
// .load(character.profileImageUrl)
// .into(holder.ivProfile)
holder.itemView.setOnClickListener {
onCharacterClick(character)
}
holder.bind(characters[position])
}
override fun getItemCount(): Int = characters.size

View File

@@ -105,7 +105,7 @@
android:textSize="24sp" />
<TextView
android:id="@+id/tv_famous_character_all"
android:id="@+id/tv_popular_character_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/pretendard_regular"
@@ -121,8 +121,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:clipToPadding="false"
android:paddingStart="24dp" />
android:paddingHorizontal="24dp" />
</LinearLayout>
<!-- 신규 캐릭터 섹션 -->
@@ -167,7 +166,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:clipToPadding="false"
android:paddingStart="24dp" />
android:paddingHorizontal="24dp" />
</LinearLayout>
@@ -177,7 +176,5 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>