feat(character): 캐릭터 탭 UI 및 기본 기능 구현
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
package kr.co.vividnext.sodalive.chat.character
|
||||
|
||||
data class Character(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val description: String,
|
||||
val imageUrl: String,
|
||||
val ranking: Int? = null // 인기 캐릭터에서만 사용
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
package kr.co.vividnext.sodalive.chat.character
|
||||
|
||||
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
|
||||
|
||||
class CharacterAdapter(
|
||||
private var characters: List<Character> = emptyList(),
|
||||
private val showRanking: Boolean = false,
|
||||
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)
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = characters.size
|
||||
|
||||
fun updateCharacters(newCharacters: List<Character>) {
|
||||
characters = newCharacters
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,162 @@ package kr.co.vividnext.sodalive.chat.character
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||
import kr.co.vividnext.sodalive.chat.character.curation.CurationSection
|
||||
import kr.co.vividnext.sodalive.chat.character.curation.CurationSectionAdapter
|
||||
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacter
|
||||
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacterAdapter
|
||||
import kr.co.vividnext.sodalive.databinding.FragmentCharacterTabBinding
|
||||
|
||||
// 캐릭터 탭 프래그먼트
|
||||
class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
||||
FragmentCharacterTabBinding::inflate
|
||||
) {
|
||||
|
||||
private lateinit var recentCharacterAdapter: RecentCharacterAdapter
|
||||
private lateinit var popularCharacterAdapter: CharacterAdapter
|
||||
private lateinit var newCharacterAdapter: CharacterAdapter
|
||||
private lateinit var curationSectionAdapter: CurationSectionAdapter
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// 캐릭터 탭 초기화 로직
|
||||
setupRecyclerViews()
|
||||
loadData()
|
||||
}
|
||||
|
||||
private fun setupRecyclerViews() {
|
||||
// 최근 대화한 캐릭터 RecyclerView 설정
|
||||
recentCharacterAdapter = RecentCharacterAdapter { character ->
|
||||
// 캐릭터 클릭 처리
|
||||
onRecentCharacterClick(character)
|
||||
}
|
||||
binding.rvRecentCharacters.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
adapter = recentCharacterAdapter
|
||||
}
|
||||
|
||||
// 인기 캐릭터 RecyclerView 설정 (순위 표시)
|
||||
popularCharacterAdapter = CharacterAdapter(
|
||||
showRanking = true
|
||||
) { character ->
|
||||
// 캐릭터 클릭 처리
|
||||
onCharacterClick(character)
|
||||
}
|
||||
binding.rvPopularCharacters.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
adapter = popularCharacterAdapter
|
||||
}
|
||||
|
||||
// 신규 캐릭터 RecyclerView 설정
|
||||
newCharacterAdapter = CharacterAdapter(
|
||||
showRanking = false
|
||||
) { character ->
|
||||
// 캐릭터 클릭 처리
|
||||
onCharacterClick(character)
|
||||
}
|
||||
binding.rvNewCharacters.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
adapter = newCharacterAdapter
|
||||
}
|
||||
|
||||
// 큐레이션 섹션 RecyclerView 설정
|
||||
curationSectionAdapter = CurationSectionAdapter { character ->
|
||||
// 캐릭터 클릭 처리
|
||||
onCharacterClick(character)
|
||||
}
|
||||
binding.rvCurationSections.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
adapter = curationSectionAdapter
|
||||
}
|
||||
|
||||
// 전체보기 버튼 클릭 리스너
|
||||
binding.tvFamousCharacterAll.setOnClickListener {
|
||||
// 인기 캐릭터 전체보기 처리
|
||||
}
|
||||
|
||||
binding.tvNewCharacterAll.setOnClickListener {
|
||||
// 신규 캐릭터 전체보기 처리
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
// TODO: 실제 데이터 로딩 로직 구현
|
||||
loadRecentCharacters()
|
||||
loadPopularCharacters()
|
||||
loadNewCharacters()
|
||||
loadCurationSections()
|
||||
}
|
||||
|
||||
private fun loadRecentCharacters() {
|
||||
// TODO: 서버에서 최근 대화한 캐릭터 데이터 로드
|
||||
val recentCharacters = listOf(
|
||||
RecentCharacter("1", "Yubin...", ""),
|
||||
RecentCharacter("2", "Yubin...", ""),
|
||||
RecentCharacter("3", "Yubin...", ""),
|
||||
RecentCharacter("4", "Yubin...", ""),
|
||||
RecentCharacter("5", "Yubin...", "")
|
||||
)
|
||||
|
||||
recentCharacterAdapter.updateCharacters(recentCharacters)
|
||||
binding.tvLatestCharacterCount.text = recentCharacters.size.toString()
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
popularCharacterAdapter.updateCharacters(popularCharacters)
|
||||
}
|
||||
|
||||
private fun loadNewCharacters() {
|
||||
// TODO: 서버에서 신규 캐릭터 데이터 로드
|
||||
val newCharacters = listOf(
|
||||
Character("1", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
||||
Character("2", "하이퍼나이프", "캐릭터 한줄 소개인데 2줄까", ""),
|
||||
Character("3", "내일", "#태그#태그", "")
|
||||
)
|
||||
|
||||
newCharacterAdapter.updateCharacters(newCharacters)
|
||||
}
|
||||
|
||||
private fun loadCurationSections() {
|
||||
// TODO: 서버에서 큐레이션 섹션 데이터 로드
|
||||
val curationSections = listOf(
|
||||
CurationSection(
|
||||
"1",
|
||||
"큐레이션",
|
||||
listOf(
|
||||
Character("1", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
||||
Character("2", "하이퍼나이프", "캐릭터 한줄 소개인데 2줄까", ""),
|
||||
Character("3", "내일", "#태그#태그", "")
|
||||
)
|
||||
),
|
||||
CurationSection(
|
||||
"2",
|
||||
"큐레이션",
|
||||
listOf(
|
||||
Character("4", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
||||
Character("5", "하이퍼나이프", "캐릭터 한줄 소개인데 2줄까", ""),
|
||||
Character("6", "내일", "#태그#태그", "")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
curationSectionAdapter.updateSections(curationSections)
|
||||
}
|
||||
|
||||
private fun onRecentCharacterClick(character: RecentCharacter) {
|
||||
// TODO: 최근 대화한 캐릭터 클릭 처리
|
||||
}
|
||||
|
||||
private fun onCharacterClick(character: Character) {
|
||||
// TODO: 캐릭터 클릭 처리
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package kr.co.vividnext.sodalive.chat.character.curation
|
||||
|
||||
import kr.co.vividnext.sodalive.chat.character.Character
|
||||
|
||||
data class CurationSection(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val characters: List<Character>
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
package kr.co.vividnext.sodalive.chat.character.curation
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = sections.size
|
||||
|
||||
fun updateSections(newSections: List<CurationSection>) {
|
||||
sections = newSections
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package kr.co.vividnext.sodalive.chat.character.recent
|
||||
|
||||
data class RecentCharacter(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val profileImageUrl: String
|
||||
)
|
||||
@@ -0,0 +1,50 @@
|
||||
package kr.co.vividnext.sodalive.chat.character.recent
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = characters.size
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateCharacters(newCharacters: List<RecentCharacter>) {
|
||||
characters = newCharacters
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user