Skip to content

Commit

Permalink
[Feat]: 媒体进度
Browse files Browse the repository at this point in the history
  • Loading branch information
why committed Dec 18, 2023
1 parent 16d7d4b commit cf2fe0b
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,27 @@ import com.blankj.utilcode.util.ScreenUtils
import com.blankj.utilcode.util.SnackbarUtils
import com.xiaoyv.bangumi.R
import com.xiaoyv.bangumi.databinding.FragmentMediaActionEpBinding
import com.xiaoyv.bangumi.ui.media.detail.overview.binder.OverviewEpBinder
import com.xiaoyv.bangumi.databinding.FragmentOverviewEpItemBinding
import com.xiaoyv.blueprint.constant.NavKey
import com.xiaoyv.blueprint.kts.launchUI
import com.xiaoyv.common.api.BgmApiManager
import com.xiaoyv.common.api.parser.entity.MediaDetailEntity
import com.xiaoyv.common.config.annotation.BgmPathType
import com.xiaoyv.common.config.bean.EpSaveProgress
import com.xiaoyv.common.helper.UserHelper
import com.xiaoyv.common.helper.callback.IdDiffItemCallback
import com.xiaoyv.common.helper.callback.IdEntity
import com.xiaoyv.common.kts.CommonColor
import com.xiaoyv.common.kts.GoogleAttr
import com.xiaoyv.common.kts.hideSnackBar
import com.xiaoyv.common.kts.setOnDebouncedChildClickListener
import com.xiaoyv.common.kts.showSnackBar
import com.xiaoyv.common.kts.tint
import com.xiaoyv.widget.binder.BaseQuickBindingHolder
import com.xiaoyv.widget.binder.BaseQuickDiffBindingAdapter
import com.xiaoyv.widget.kts.dpi
import com.xiaoyv.widget.kts.errorMsg
import com.xiaoyv.widget.kts.getParcelObjList
import com.xiaoyv.widget.kts.getAttrColor
import com.xiaoyv.widget.kts.getParcelObj
import com.xiaoyv.widget.kts.updateWindowParams
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand All @@ -48,34 +56,21 @@ class MediaEpActionDialog : DialogFragment() {
) = FragmentMediaActionEpBinding.inflate(inflater, container, false).root

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val mediaId = arguments?.getString(NavKey.KEY_STRING).orEmpty()
val mediaName = arguments?.getString(NavKey.KEY_STRING_SECOND).orEmpty()
val myProgress = arguments?.getInt(NavKey.KEY_INTEGER) ?: 0
val progresses =
arguments?.getParcelObjList<MediaDetailEntity.MediaProgress>(NavKey.KEY_PARCELABLE_LIST)
.orEmpty()

val binding = FragmentMediaActionEpBinding.bind(view)
initView(binding, mediaId, mediaName, progresses, myProgress)
val mediaProgress = arguments?.getParcelObj<EpSaveProgress>(NavKey.KEY_PARCELABLE)
if (mediaProgress != null) {
val binding = FragmentMediaActionEpBinding.bind(view)
initView(binding, mediaProgress)
}
}

private fun initView(
binding: FragmentMediaActionEpBinding,
mediaId: String,
mediaName: String,
progresses: List<MediaDetailEntity.MediaProgress>,
myProgress: Int,
) {
binding.tvTitle.text = mediaName
private fun initView(binding: FragmentMediaActionEpBinding, mediaProgress: EpSaveProgress) {
binding.tvTitle.text = mediaProgress.mediaName
binding.ivCancel.setOnClickListener {
dismissAllowingStateLoss()
}

// 格子填充
val epAdapter = OverviewEpBinder.ItemEpAdapter(
selectedMode = true,
selectIndex = myProgress - 1
)
val epAdapter = ItemEpAdapter(mediaProgress.myProgress - 1)
binding.rvEp.updateLayoutParams<ConstraintLayout.LayoutParams> {
matchConstraintMaxHeight = (ScreenUtils.getAppScreenHeight() * 0.7f).roundToInt()
}
Expand All @@ -89,18 +84,21 @@ class MediaEpActionDialog : DialogFragment() {
epAdapter.notifyItemRangeChanged(
0,
epAdapter.itemCount,
OverviewEpBinder.ItemEpAdapter.PAYLOAD_REFRESH_COLOR
PAYLOAD_REFRESH_COLOR
)

finishEp(mediaId, it.number)
finishEp(mediaProgress.mediaId, it.number)
}

dismissAllowingStateLoss()
}

// 加载数据
launchUI {
epAdapter.submitList(withContext(Dispatchers.IO) {
progresses.filterNot { it.isNotEp }
val numberList = arrayListOf<EpNoEntity>()
repeat(mediaProgress.totalProgress.coerceAtLeast(0)) {
numberList.add(EpNoEntity(it.toString(), (it + 1).toString()))
}
numberList
})
}
}
Expand All @@ -112,10 +110,12 @@ class MediaEpActionDialog : DialogFragment() {
launchUI(
error = {
it.printStackTrace()

isCancelable = true
showSnackBar(it.errorMsg, error = true)
},
block = {
isCancelable = false

require(mediaId.isNotBlank()) { "条目信息丢失" }
require(number.isNotBlank()) { "章节信息丢失" }

Expand All @@ -129,6 +129,8 @@ class MediaEpActionDialog : DialogFragment() {
hideSnackBar()

onSaveListener?.invoke(number.toIntOrNull() ?: 0)

dismissAllowingStateLoss()
}
)
}
Expand All @@ -146,23 +148,67 @@ class MediaEpActionDialog : DialogFragment() {
}
}

data class EpNoEntity(override var id: String, var number: String) : IdEntity

internal class ItemEpAdapter(var selectIndex: Int = -1) :
BaseQuickDiffBindingAdapter<EpNoEntity, FragmentOverviewEpItemBinding>(IdDiffItemCallback()) {

override fun onBindViewHolder(
holder: BaseQuickBindingHolder<FragmentOverviewEpItemBinding>,
position: Int,
item: EpNoEntity?,
payloads: List<Any>,
) {
super.onBindViewHolder(holder, position, item, payloads)
payloads.forEach {
if (it == PAYLOAD_REFRESH_COLOR) {
refreshColor(position, holder)
}
}
}

override fun onBindViewHolder(
holder: BaseQuickBindingHolder<FragmentOverviewEpItemBinding>,
position: Int,
item: EpNoEntity?,
) {
super.onBindViewHolder(holder, position, item)

// 完成进度复用时的UI逻辑
refreshColor(position, holder)
}

override fun BaseQuickBindingHolder<FragmentOverviewEpItemBinding>.converted(item: EpNoEntity) {
binding.tvEp.text = item.number
}

private fun refreshColor(
position: Int,
holder: BaseQuickBindingHolder<FragmentOverviewEpItemBinding>,
) {
if (position <= selectIndex) {
holder.binding.tvEp.setTextColor(context.getAttrColor(GoogleAttr.colorOnPrimarySurface))
holder.binding.tvEp.backgroundTintList =
context.getColor(CommonColor.save_collect).tint
} else {
holder.binding.tvEp.setTextColor(context.getAttrColor(GoogleAttr.colorOnSurface))
holder.binding.tvEp.backgroundTintList =
context.getAttrColor(GoogleAttr.colorSurfaceContainer).tint
}
}
}

companion object {
private const val PAYLOAD_REFRESH_COLOR = 1

fun show(
fragmentManager: FragmentManager,
mediaId: String,
mediaName: String,
totalEp: ArrayList<MediaDetailEntity.MediaProgress>?,
myProgress: Int = 0,
mediaProgress: EpSaveProgress,
onSaveListener: ((Int) -> Unit)? = null,
) {
MediaEpActionDialog().apply {
this.onSaveListener = onSaveListener
this.arguments = bundleOf(
NavKey.KEY_STRING to mediaId,
NavKey.KEY_STRING_SECOND to mediaName,
NavKey.KEY_PARCELABLE_LIST to totalEp,
NavKey.KEY_INTEGER to myProgress
)
this.arguments = bundleOf(NavKey.KEY_PARCELABLE to mediaProgress)
}.show(fragmentManager, "MediaEpActionDialog")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,25 +92,19 @@ class OverviewFragment : BaseViewModelFragment<FragmentOverviewBinding, Overview

override fun initListener() {
// 章节完成度点击
overviewAdapter.setOnDebouncedChildClickListener(R.id.tv_ep_my_progress) {
val position = overviewAdapter.itemIndexOfFirst(it)
overviewAdapter.setOnDebouncedChildClickListener(R.id.tv_ep_my_progress) { item ->
val position = overviewAdapter.itemIndexOfFirst(item)
val entity = viewModel.mediaDetailLiveData.value
?: return@setOnDebouncedChildClickListener

// 更新我的进度
if (entity.progressList.isEmpty().not()) {
MediaEpActionDialog.show(
childFragmentManager,
viewModel.mediaId,
viewModel.requireMediaName,
entity.progressList,
entity.myProgress
) { myProgress ->
entity.myProgress = myProgress
val saveProgress = viewModel.requireProgress

// 修改进度面板
if (entity != null) {
MediaEpActionDialog.show(childFragmentManager, saveProgress) { progress ->
// 刷新章节的进度 Item
it.entity = entity
overviewAdapter[position] = it
item.entity = entity.apply {
myProgress = progress
}
overviewAdapter[position] = item
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.xiaoyv.common.api.parser.impl.parserMediaDetail
import com.xiaoyv.common.api.response.douban.DouBanPhotoEntity
import com.xiaoyv.common.config.annotation.MediaDetailType
import com.xiaoyv.common.config.annotation.SampleImageGridClickType
import com.xiaoyv.common.config.bean.EpSaveProgress
import com.xiaoyv.common.config.bean.SampleAvatar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand All @@ -35,6 +36,17 @@ class OverviewViewModel : BaseViewModel() {
return entity.titleCn.ifBlank { entity.titleNative }
}

/**
* 用户的进度信息
*/
internal val requireProgress: EpSaveProgress
get() = EpSaveProgress(
mediaId = mediaId,
mediaName = requireMediaName,
myProgress = mediaDetailLiveData.value?.myProgress ?: 0,
totalProgress = mediaDetailLiveData.value?.totalProgress ?: 0
)

private val defaultImage by lazy {
DouBanPhotoEntity.Photo(
image = DouBanPhotoEntity.Image(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ import com.xiaoyv.common.api.parser.entity.MediaDetailEntity
import com.xiaoyv.common.config.annotation.InterestType
import com.xiaoyv.common.helper.callback.IdDiffItemCallback
import com.xiaoyv.common.helper.callback.RecyclerItemTouchedListener
import com.xiaoyv.common.kts.CommonColor
import com.xiaoyv.common.kts.GoogleAttr
import com.xiaoyv.common.kts.forceCast
import com.xiaoyv.common.kts.inflater
import com.xiaoyv.common.kts.setOnDebouncedChildClickListener
import com.xiaoyv.common.kts.tint
import com.xiaoyv.widget.binder.BaseQuickBindingHolder
import com.xiaoyv.widget.binder.BaseQuickDiffBindingAdapter
import com.xiaoyv.widget.kts.getAttrColor
Expand All @@ -41,7 +39,7 @@ class OverviewEpBinder(
private val subSize = 24

private val itemAdapter by lazy {
ItemEpAdapter().apply {
ItemAdapter().apply {
setOnDebouncedChildClickListener(R.id.item_ep, block = clickItemListener)
}
}
Expand All @@ -56,9 +54,22 @@ class OverviewEpBinder(
holder.binding.tvTitleEp.title = item.title

item.entity.forceCast<MediaDetailEntity>().apply {
holder.binding.pbMedia.max = totalProgress
holder.binding.pbMedia.progress = myProgress

// 进度文字颜色
if (totalProgress == 0 || myProgress < totalProgress / 2) {
holder.binding.tvEpMyProgress.setTextColor(
holder.binding.pbMedia.context.getAttrColor(GoogleAttr.colorOnSurfaceVariant)
)
} else {
holder.binding.tvEpMyProgress.setTextColor(
holder.binding.pbMedia.context.getAttrColor(GoogleAttr.colorOnPrimary)
)
}

holder.binding.tvEpMyProgress.isVisible =
(collectState.interest != InterestType.TYPE_UNKNOWN && collectState.interest != InterestType.TYPE_WISH)

holder.binding.tvEpMyProgress.text =
String.format("我的完成度:%d/%d", myProgress, totalProgress)
itemAdapter.submitList(progressList.subListLimit(subSize))
Expand All @@ -81,42 +92,12 @@ class OverviewEpBinder(
*
* @param selectedMode 是否为完成格子时的选取模式
*/
internal class ItemEpAdapter(
val selectedMode: Boolean = false,
var selectIndex: Int = -1,
) : BaseQuickDiffBindingAdapter<MediaDetailEntity.MediaProgress,
class ItemAdapter : BaseQuickDiffBindingAdapter<MediaDetailEntity.MediaProgress,
FragmentOverviewEpItemBinding>(IdDiffItemCallback()) {

override fun onBindViewHolder(
holder: BaseQuickBindingHolder<FragmentOverviewEpItemBinding>,
position: Int,
item: MediaDetailEntity.MediaProgress?,
payloads: List<Any>,
) {
super.onBindViewHolder(holder, position, item, payloads)
payloads.forEach {
if (it == PAYLOAD_REFRESH_COLOR) {
refreshColor(position, holder)
}
}
}

override fun onBindViewHolder(
holder: BaseQuickBindingHolder<FragmentOverviewEpItemBinding>,
position: Int,
item: MediaDetailEntity.MediaProgress?,
) {
super.onBindViewHolder(holder, position, item)

// 完成进度复用时的UI逻辑
refreshColor(position, holder)
}

override fun BaseQuickBindingHolder<FragmentOverviewEpItemBinding>.converted(item: MediaDetailEntity.MediaProgress) {
binding.tvEp.text = item.number

// 完成进度复用时,不执行这里逻辑
if (selectedMode.not()) when {
when {
item.isRelease -> {
binding.tvEp.setTextColor(context.getAttrColor(GoogleAttr.colorOnPrimaryContainer))
binding.tvEp.backgroundTintList = ColorStateList.valueOf(
Expand All @@ -132,26 +113,5 @@ class OverviewEpBinder(
}
}
}

private fun refreshColor(
position: Int,
holder: BaseQuickBindingHolder<FragmentOverviewEpItemBinding>,
) {
if (selectedMode) {
if (position <= selectIndex) {
holder.binding.tvEp.setTextColor(context.getAttrColor(GoogleAttr.colorOnPrimarySurface))
holder.binding.tvEp.backgroundTintList =
context.getColor(CommonColor.save_collect).tint
} else {
holder.binding.tvEp.setTextColor(context.getAttrColor(GoogleAttr.colorOnSurface))
holder.binding.tvEp.backgroundTintList =
context.getAttrColor(GoogleAttr.colorSurfaceContainer).tint
}
}
}

companion object {
const val PAYLOAD_REFRESH_COLOR = 1
}
}
}
Loading

0 comments on commit cf2fe0b

Please sign in to comment.