feat(home): 인기 캐릭터 색션 추가
This commit is contained in:
		@@ -20,6 +20,7 @@ data class GetHomeResponse(
 | 
				
			|||||||
    @SerializedName("eventBannerList") val eventBannerList: GetEventResponse,
 | 
					    @SerializedName("eventBannerList") val eventBannerList: GetEventResponse,
 | 
				
			||||||
    @SerializedName("originalAudioDramaList") val originalAudioDramaList: List<GetSeriesListResponse.SeriesListItem>,
 | 
					    @SerializedName("originalAudioDramaList") val originalAudioDramaList: List<GetSeriesListResponse.SeriesListItem>,
 | 
				
			||||||
    @SerializedName("dayOfWeekSeriesList") val dayOfWeekSeriesList: List<GetSeriesListResponse.SeriesListItem>,
 | 
					    @SerializedName("dayOfWeekSeriesList") val dayOfWeekSeriesList: List<GetSeriesListResponse.SeriesListItem>,
 | 
				
			||||||
 | 
					    @SerializedName("popularCharacters") val popularCharacters: List<Character>,
 | 
				
			||||||
    @SerializedName("contentRanking") val contentRanking: List<GetAudioContentRankingItem>,
 | 
					    @SerializedName("contentRanking") val contentRanking: List<GetAudioContentRankingItem>,
 | 
				
			||||||
    @SerializedName("recommendChannelList") val recommendChannelList: List<RecommendChannelResponse>,
 | 
					    @SerializedName("recommendChannelList") val recommendChannelList: List<RecommendChannelResponse>,
 | 
				
			||||||
    @SerializedName("freeContentList") val freeContentList: List<AudioContentMainItem>,
 | 
					    @SerializedName("freeContentList") val freeContentList: List<AudioContentMainItem>,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import android.os.Looper
 | 
				
			|||||||
import android.text.SpannableString
 | 
					import android.text.SpannableString
 | 
				
			||||||
import android.text.Spanned
 | 
					import android.text.Spanned
 | 
				
			||||||
import android.text.style.ForegroundColorSpan
 | 
					import android.text.style.ForegroundColorSpan
 | 
				
			||||||
 | 
					import android.view.Gravity
 | 
				
			||||||
import android.view.View
 | 
					import android.view.View
 | 
				
			||||||
import android.widget.LinearLayout
 | 
					import android.widget.LinearLayout
 | 
				
			||||||
import android.widget.Toast
 | 
					import android.widget.Toast
 | 
				
			||||||
@@ -19,6 +20,7 @@ import androidx.media3.common.util.UnstableApi
 | 
				
			|||||||
import androidx.recyclerview.widget.GridLayoutManager
 | 
					import androidx.recyclerview.widget.GridLayoutManager
 | 
				
			||||||
import androidx.recyclerview.widget.LinearLayoutManager
 | 
					import androidx.recyclerview.widget.LinearLayoutManager
 | 
				
			||||||
import androidx.recyclerview.widget.RecyclerView
 | 
					import androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					import com.google.gson.Gson
 | 
				
			||||||
import com.zhpan.bannerview.BaseBannerAdapter
 | 
					import com.zhpan.bannerview.BaseBannerAdapter
 | 
				
			||||||
import com.zhpan.indicator.enums.IndicatorSlideMode
 | 
					import com.zhpan.indicator.enums.IndicatorSlideMode
 | 
				
			||||||
import com.zhpan.indicator.enums.IndicatorStyle
 | 
					import com.zhpan.indicator.enums.IndicatorStyle
 | 
				
			||||||
@@ -34,6 +36,10 @@ import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
 | 
				
			|||||||
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
 | 
					import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.audition.AuditionActivity
 | 
					import kr.co.vividnext.sodalive.audition.AuditionActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseFragment
 | 
					import kr.co.vividnext.sodalive.base.BaseFragment
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.base.SodaDialog
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterAdapter
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity.Companion.EXTRA_CHARACTER_ID
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.Constants
 | 
					import kr.co.vividnext.sodalive.common.Constants
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
					import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
@@ -46,10 +52,15 @@ import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
 | 
				
			|||||||
import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog
 | 
					import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog
 | 
				
			||||||
import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog
 | 
					import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog
 | 
				
			||||||
import kr.co.vividnext.sodalive.main.MainActivity
 | 
					import kr.co.vividnext.sodalive.main.MainActivity
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.mypage.MyPageViewModel
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.mypage.auth.Auth
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
 | 
				
			||||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
 | 
					import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.search.SearchActivity
 | 
					import kr.co.vividnext.sodalive.search.SearchActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
 | 
					import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.settings.notification.MemberRole
 | 
					import kr.co.vividnext.sodalive.settings.notification.MemberRole
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.splash.SplashActivity
 | 
				
			||||||
import org.koin.android.ext.android.inject
 | 
					import org.koin.android.ext.android.inject
 | 
				
			||||||
import java.text.SimpleDateFormat
 | 
					import java.text.SimpleDateFormat
 | 
				
			||||||
import java.util.Date
 | 
					import java.util.Date
 | 
				
			||||||
@@ -59,6 +70,7 @@ import java.util.Locale
 | 
				
			|||||||
class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
 | 
					class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
 | 
				
			||||||
    private val viewModel: HomeViewModel by inject()
 | 
					    private val viewModel: HomeViewModel by inject()
 | 
				
			||||||
    private val liveViewModel: LiveViewModel by inject()
 | 
					    private val liveViewModel: LiveViewModel by inject()
 | 
				
			||||||
 | 
					    private val myPageViewModel: MyPageViewModel by inject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private lateinit var loadingDialog: LoadingDialog
 | 
					    private lateinit var loadingDialog: LoadingDialog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,6 +82,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
 | 
				
			|||||||
    private lateinit var contentBannerAdapter: AudioContentMainBannerAdapter
 | 
					    private lateinit var contentBannerAdapter: AudioContentMainBannerAdapter
 | 
				
			||||||
    private lateinit var originalSeriesAdapter: HomeSeriesAdapter
 | 
					    private lateinit var originalSeriesAdapter: HomeSeriesAdapter
 | 
				
			||||||
    private lateinit var seriesDayOfWeekAdapter: HomeSeriesAdapter
 | 
					    private lateinit var seriesDayOfWeekAdapter: HomeSeriesAdapter
 | 
				
			||||||
 | 
					    private lateinit var popularCharacterAdapter: CharacterAdapter
 | 
				
			||||||
    private lateinit var weelyChartAdapter: HomeWeeklyChartAdapter
 | 
					    private lateinit var weelyChartAdapter: HomeWeeklyChartAdapter
 | 
				
			||||||
    private lateinit var recommendChannelAdapter: HomeRecommendChannelAdapter
 | 
					    private lateinit var recommendChannelAdapter: HomeRecommendChannelAdapter
 | 
				
			||||||
    private lateinit var homeFreeContentAdapter: HomeContentAdapter
 | 
					    private lateinit var homeFreeContentAdapter: HomeContentAdapter
 | 
				
			||||||
@@ -173,6 +186,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
 | 
				
			|||||||
        setupOriginalSeries()
 | 
					        setupOriginalSeries()
 | 
				
			||||||
        setupAudition()
 | 
					        setupAudition()
 | 
				
			||||||
        setupSeriesDayOfWeek()
 | 
					        setupSeriesDayOfWeek()
 | 
				
			||||||
 | 
					        setupPopularCharacters()
 | 
				
			||||||
        setupWeelyChart()
 | 
					        setupWeelyChart()
 | 
				
			||||||
        setupRecommendChannel()
 | 
					        setupRecommendChannel()
 | 
				
			||||||
        setupFreeContent()
 | 
					        setupFreeContent()
 | 
				
			||||||
@@ -777,6 +791,67 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
 | 
				
			|||||||
        rvDayOfWeek.adapter = dayOfWeekAdapter
 | 
					        rvDayOfWeek.adapter = dayOfWeekAdapter
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun setupPopularCharacters() {
 | 
				
			||||||
 | 
					        // 인기 캐릭터 RecyclerView 설정 (순위 표시)
 | 
				
			||||||
 | 
					        popularCharacterAdapter = CharacterAdapter(
 | 
				
			||||||
 | 
					            showRanking = true
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            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 {
 | 
				
			||||||
 | 
					            (requireActivity() as MainActivity).openChatTab()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 인기 캐릭터 LiveData 구독
 | 
				
			||||||
 | 
					        viewModel.popularCharacters.observe(viewLifecycleOwner) {
 | 
				
			||||||
 | 
					            if (it.isNotEmpty()) {
 | 
				
			||||||
 | 
					                binding.llPopularCharacters.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					                popularCharacterAdapter.updateCharacters(it)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                binding.llPopularCharacters.visibility = View.GONE
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun setupWeelyChart() {
 | 
					    private fun setupWeelyChart() {
 | 
				
			||||||
        val spSectionTitle = SpannableString(binding.tvWeeklyChart.text)
 | 
					        val spSectionTitle = SpannableString(binding.tvWeeklyChart.text)
 | 
				
			||||||
        spSectionTitle.setSpan(
 | 
					        spSectionTitle.setSpan(
 | 
				
			||||||
@@ -1196,4 +1271,65 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun ensureLoginAndAuth(onAuthed: () -> Unit) {
 | 
				
			||||||
 | 
					        if (SharedPreferenceManager.token.isBlank()) {
 | 
				
			||||||
 | 
					            (requireActivity() as MainActivity).showLoginActivity()
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!SharedPreferenceManager.isAuth) {
 | 
				
			||||||
 | 
					            SodaDialog(
 | 
				
			||||||
 | 
					                activity = requireActivity(),
 | 
				
			||||||
 | 
					                layoutInflater = layoutInflater,
 | 
				
			||||||
 | 
					                title = "본인인증",
 | 
				
			||||||
 | 
					                desc = "보이스온의 오픈월드 캐릭터톡은\n청소년 보호를 위해 본인인증한\n성인만 이용이 가능합니다.\n" +
 | 
				
			||||||
 | 
					                    "캐릭터톡 서비스를 이용하시려면\n본인인증을 하고 이용해주세요.",
 | 
				
			||||||
 | 
					                confirmButtonTitle = "본인인증 하러가기",
 | 
				
			||||||
 | 
					                confirmButtonClick = { startAuthFlow() },
 | 
				
			||||||
 | 
					                cancelButtonTitle = "취소",
 | 
				
			||||||
 | 
					                cancelButtonClick = {},
 | 
				
			||||||
 | 
					                descGravity = Gravity.CENTER
 | 
				
			||||||
 | 
					            ).show(screenWidth)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onAuthed()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun startAuthFlow() {
 | 
				
			||||||
 | 
					        Auth.auth(requireActivity(), requireContext()) { json ->
 | 
				
			||||||
 | 
					            val bootpayResponse = Gson().fromJson(
 | 
				
			||||||
 | 
					                json,
 | 
				
			||||||
 | 
					                BootpayResponse::class.java
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            val request = AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)
 | 
				
			||||||
 | 
					            requireActivity().runOnUiThread {
 | 
				
			||||||
 | 
					                myPageViewModel.authVerify(request) {
 | 
				
			||||||
 | 
					                    startActivity(
 | 
				
			||||||
 | 
					                        Intent(
 | 
				
			||||||
 | 
					                            requireContext(),
 | 
				
			||||||
 | 
					                            SplashActivity::class.java
 | 
				
			||||||
 | 
					                        ).apply {
 | 
				
			||||||
 | 
					                            addFlags(
 | 
				
			||||||
 | 
					                                Intent.FLAG_ACTIVITY_CLEAR_TASK or
 | 
				
			||||||
 | 
					                                    Intent.FLAG_ACTIVITY_NEW_TASK
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    requireActivity().finish()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun onCharacterClick(characterId: Long) {
 | 
				
			||||||
 | 
					        ensureLoginAndAuth {
 | 
				
			||||||
 | 
					            startActivity(
 | 
				
			||||||
 | 
					                Intent(requireContext(), CharacterDetailActivity::class.java).apply {
 | 
				
			||||||
 | 
					                    putExtra(EXTRA_CHARACTER_ID, characterId)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
 | 
				
			|||||||
import kr.co.vividnext.sodalive.audio_content.main.v2.GetContentCurationResponse
 | 
					import kr.co.vividnext.sodalive.audio_content.main.v2.GetContentCurationResponse
 | 
				
			||||||
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
 | 
					import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
 | 
					import kr.co.vividnext.sodalive.base.BaseViewModel
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.character.Character
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
import kr.co.vividnext.sodalive.explorer.GetExplorerSectionCreatorResponse
 | 
					import kr.co.vividnext.sodalive.explorer.GetExplorerSectionCreatorResponse
 | 
				
			||||||
import kr.co.vividnext.sodalive.live.GetRoomListResponse
 | 
					import kr.co.vividnext.sodalive.live.GetRoomListResponse
 | 
				
			||||||
@@ -58,6 +59,11 @@ class HomeViewModel(
 | 
				
			|||||||
    val dayOfWeekSeriesListLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
 | 
					    val dayOfWeekSeriesListLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
 | 
				
			||||||
        get() = _dayOfWeekSeriesListLiveData
 | 
					        get() = _dayOfWeekSeriesListLiveData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 인기 캐릭터 LiveData
 | 
				
			||||||
 | 
					    private val _popularCharacters = MutableLiveData<List<Character>>(emptyList())
 | 
				
			||||||
 | 
					    val popularCharacters: LiveData<List<Character>>
 | 
				
			||||||
 | 
					        get() = _popularCharacters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var _contentRankingLiveData = MutableLiveData<List<GetAudioContentRankingItem>>()
 | 
					    private var _contentRankingLiveData = MutableLiveData<List<GetAudioContentRankingItem>>()
 | 
				
			||||||
    val contentRankingLiveData: LiveData<List<GetAudioContentRankingItem>>
 | 
					    val contentRankingLiveData: LiveData<List<GetAudioContentRankingItem>>
 | 
				
			||||||
        get() = _contentRankingLiveData
 | 
					        get() = _contentRankingLiveData
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -596,6 +596,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun openChatTab() {
 | 
				
			||||||
 | 
					        viewModel.clickTab(MainViewModel.CurrentTab.CHAT)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inner class AudioContentReceiver : BroadcastReceiver() {
 | 
					    inner class AudioContentReceiver : BroadcastReceiver() {
 | 
				
			||||||
        override fun onReceive(context: Context?, intent: Intent?) {
 | 
					        override fun onReceive(context: Context?, intent: Intent?) {
 | 
				
			||||||
            val contentId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
 | 
					            val contentId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -263,6 +263,51 @@
 | 
				
			|||||||
                    android:paddingHorizontal="24dp" />
 | 
					                    android:paddingHorizontal="24dp" />
 | 
				
			||||||
            </LinearLayout>
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- 인기 캐릭터 섹션 -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/ll_popular_characters"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					                android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 제목과 전체보기 -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                    android:orientation="horizontal"
 | 
				
			||||||
 | 
					                    android:paddingHorizontal="24dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:layout_width="0dp"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_weight="1"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_bold"
 | 
				
			||||||
 | 
					                        android:text="인기 캐릭터"
 | 
				
			||||||
 | 
					                        android:textColor="@color/white"
 | 
				
			||||||
 | 
					                        android:textSize="24sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/tv_popular_character_all"
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/pretendard_regular"
 | 
				
			||||||
 | 
					                        android:text="전체보기"
 | 
				
			||||||
 | 
					                        android:textColor="#90A4AE"
 | 
				
			||||||
 | 
					                        android:textSize="14sp" />
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- 캐릭터 카드 리스트 -->
 | 
				
			||||||
 | 
					                <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					                    android:id="@+id/rv_popular_characters"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="16dp"
 | 
				
			||||||
 | 
					                    android:clipToPadding="false"
 | 
				
			||||||
 | 
					                    android:paddingHorizontal="24dp" />
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <LinearLayout
 | 
					            <LinearLayout
 | 
				
			||||||
                android:id="@+id/ll_weekly_chart"
 | 
					                android:id="@+id/ll_weekly_chart"
 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user