diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterAdapter.kt index a2053b3b..bc381474 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterAdapter.kt @@ -6,8 +6,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import coil.transform.RoundedCornersTransformation import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions import kr.co.vividnext.sodalive.databinding.ItemCharacterBinding +import kr.co.vividnext.sodalive.extensions.dpToPx class CharacterAdapter( private var characters: List = emptyList(), @@ -34,6 +38,13 @@ class CharacterAdapter( Glide.with(context) .load(character.imageUrl) + .apply( + RequestOptions().transform( + RoundedCorners( + 16f.dpToPx().toInt() + ) + ) + ) .into(binding.ivCharacter) binding.root.setOnClickListener { onCharacterClick(character) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterTabFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterTabFragment.kt index 3048829d..ada1dc94 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterTabFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterTabFragment.kt @@ -1,20 +1,22 @@ package kr.co.vividnext.sodalive.chat.character +import android.graphics.Rect import android.os.Bundle import android.view.View import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView 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 +import kr.co.vividnext.sodalive.extensions.dpToPx // 캐릭터 탭 프래그먼트 class CharacterTabFragment : BaseFragment( FragmentCharacterTabBinding::inflate ) { - private lateinit var recentCharacterAdapter: RecentCharacterAdapter private lateinit var popularCharacterAdapter: CharacterAdapter private lateinit var newCharacterAdapter: CharacterAdapter @@ -22,65 +24,208 @@ class CharacterTabFragment : BaseFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupRecyclerViews() + setupView() loadData() } - private fun setupRecyclerViews() { + private fun setupView() { + setupRecentCharactersRecyclerView() + setupPopularCharactersRecyclerView() + setupNewCharactersRecyclerView() + setupCurationSectionsRecyclerView() + } + + private fun setupRecentCharactersRecyclerView() { // 최근 대화한 캐릭터 RecyclerView 설정 - recentCharacterAdapter = RecentCharacterAdapter { character -> - // 캐릭터 클릭 처리 - onRecentCharacterClick(character) - } - binding.rvRecentCharacters.apply { - layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - adapter = recentCharacterAdapter + recentCharacterAdapter = RecentCharacterAdapter { + onRecentCharacterClick(it) } + val recyclerView = binding.rvRecentCharacters + + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 8f.dpToPx().toInt() + } + + recentCharacterAdapter.itemCount - 1 -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = recentCharacterAdapter + } + + private fun setupPopularCharactersRecyclerView() { // 인기 캐릭터 RecyclerView 설정 (순위 표시) popularCharacterAdapter = CharacterAdapter( showRanking = true - ) { character -> - // 캐릭터 클릭 처리 - onCharacterClick(character) - } - binding.rvPopularCharacters.apply { - layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - adapter = popularCharacterAdapter + ) { + onCharacterClick(it) } + val recyclerView = binding.rvPopularCharacters + + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 8f.dpToPx().toInt() + } + + popularCharacterAdapter.itemCount - 1 -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = popularCharacterAdapter + + binding.tvPopularCharacterAll.setOnClickListener { + } + } + + private fun setupNewCharactersRecyclerView() { // 신규 캐릭터 RecyclerView 설정 newCharacterAdapter = CharacterAdapter( showRanking = false - ) { character -> - // 캐릭터 클릭 처리 - onCharacterClick(character) - } - binding.rvNewCharacters.apply { - layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - adapter = newCharacterAdapter + ) { + onCharacterClick(it) } - // 큐레이션 섹션 RecyclerView 설정 - curationSectionAdapter = CurationSectionAdapter { character -> - // 캐릭터 클릭 처리 - onCharacterClick(character) - } - binding.rvCurationSections.apply { - layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) - adapter = curationSectionAdapter - } + val recyclerView = binding.rvNewCharacters - // 전체보기 버튼 클릭 리스너 - binding.tvPopularCharacterAll.setOnClickListener { - // 인기 캐릭터 전체보기 처리 - } + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 8f.dpToPx().toInt() + } + + newCharacterAdapter.itemCount - 1 -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = newCharacterAdapter binding.tvNewCharacterAll.setOnClickListener { - // 신규 캐릭터 전체보기 처리 } } + private fun setupCurationSectionsRecyclerView() { + // 큐레이션 섹션 RecyclerView 설정 + curationSectionAdapter = CurationSectionAdapter { + onCharacterClick(it) + } + + + val recyclerView = binding.rvCurationSections + + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.VERTICAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.top = 0 + outRect.bottom = 24f.dpToPx().toInt() + } + + curationSectionAdapter.itemCount - 1 -> { + outRect.top = 24f.dpToPx().toInt() + outRect.bottom = 0 + } + + else -> { + outRect.top = 24f.dpToPx().toInt() + outRect.bottom = 24f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = curationSectionAdapter + } + private fun loadData() { // TODO: 실제 데이터 로딩 로직 구현 loadRecentCharacters() diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/curation/CurationSectionAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/curation/CurationSectionAdapter.kt index c879aeac..66f8925b 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/curation/CurationSectionAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/curation/CurationSectionAdapter.kt @@ -1,13 +1,17 @@ package kr.co.vividnext.sodalive.chat.character.curation import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Rect import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import kr.co.vividnext.sodalive.chat.character.Character import kr.co.vividnext.sodalive.chat.character.CharacterAdapter import kr.co.vividnext.sodalive.databinding.ItemCurationSectionBinding +import kr.co.vividnext.sodalive.extensions.dpToPx class CurationSectionAdapter( private var sections: List = emptyList(), @@ -15,6 +19,7 @@ class CurationSectionAdapter( ) : RecyclerView.Adapter() { inner class ViewHolder( + private val context: Context, private val binding: ItemCurationSectionBinding ) : RecyclerView.ViewHolder(binding.root) { fun bind(section: CurationSection) { @@ -27,18 +32,48 @@ class CurationSectionAdapter( onCharacterClick = onCharacterClick ) - binding.rvCharacters.apply { - layoutManager = LinearLayoutManager( - context, - LinearLayoutManager.HORIZONTAL, - false - ) - adapter = characterAdapter - } + val recyclerView = binding.rvCharacters + + recyclerView.layoutManager = LinearLayoutManager( + context, + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 8f.dpToPx().toInt() + } + + characterAdapter.itemCount - 1 -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = characterAdapter } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, ItemCurationSectionBinding.inflate( LayoutInflater.from(parent.context), parent, diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/recent/RecentCharacterAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/recent/RecentCharacterAdapter.kt index 77994b5a..e4babdf4 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/character/recent/RecentCharacterAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/character/recent/RecentCharacterAdapter.kt @@ -6,7 +6,10 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions import kr.co.vividnext.sodalive.databinding.ItemRecentCharacterBinding +import kr.co.vividnext.sodalive.extensions.dpToPx class RecentCharacterAdapter( private var characters: List = emptyList(), @@ -21,6 +24,13 @@ class RecentCharacterAdapter( binding.tvName.text = character.name Glide.with(context) .load(character.profileImageUrl) + .apply( + RequestOptions().transform( + RoundedCorners( + 16f.dpToPx().toInt() + ) + ) + ) .into(binding.ivProfile) binding.root.setOnClickListener { onCharacterClick(character) } diff --git a/app/src/main/res/drawable/gradient_ranking_bg.xml b/app/src/main/res/drawable/gradient_ranking_bg.xml deleted file mode 100644 index 1c8a1573..00000000 --- a/app/src/main/res/drawable/gradient_ranking_bg.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_character_tab.xml b/app/src/main/res/layout/fragment_character_tab.xml index 1388e7fe..fab3cba3 100644 --- a/app/src/main/res/layout/fragment_character_tab.xml +++ b/app/src/main/res/layout/fragment_character_tab.xml @@ -9,7 +9,8 @@ + android:orientation="vertical" + android:paddingVertical="24dp"> + android:clipToPadding="false" /> diff --git a/app/src/main/res/layout/item_character.xml b/app/src/main/res/layout/item_character.xml index 180436de..e776c8f3 100644 --- a/app/src/main/res/layout/item_character.xml +++ b/app/src/main/res/layout/item_character.xml @@ -1,71 +1,64 @@ - + android:orientation="vertical"> - + + - - - - - - - - - - - - - + android:layout_gravity="bottom" + android:layout_marginStart="14dp" + android:gravity="center" + android:orientation="horizontal"> + + + + + + + + + + - - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_curation_section.xml b/app/src/main/res/layout/item_curation_section.xml index 7f4a21c0..7dd1b55f 100644 --- a/app/src/main/res/layout/item_curation_section.xml +++ b/app/src/main/res/layout/item_curation_section.xml @@ -2,7 +2,6 @@ @@ -21,7 +20,6 @@ android:fontFamily="@font/pretendard_bold" android:textColor="@color/white" android:textSize="24sp" /> - @@ -31,6 +29,5 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:clipToPadding="false" - android:paddingStart="24dp" /> - - \ No newline at end of file + android:paddingHorizontal="24dp" /> +