feat:分类新增搜索功能—客户端 https://jira.shanqu.cc/browse/GHZSCY-7512
This commit is contained in:
@ -1534,8 +1534,8 @@ object DirectUtils {
|
||||
if (categoryId.isEmpty()) return
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, CategoryV2Activity::class.java.name)
|
||||
bundle.putString(KEY_CATEGORY_ID, categoryId)
|
||||
bundle.putString(KEY_CATEGORY_TITLE, categoryTitle)
|
||||
bundle.putString(KEY_PAGE_ID, categoryId)
|
||||
bundle.putString(KEY_PAGE_NAME, categoryTitle)
|
||||
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
if (exposureEvent != null) bundle.putParcelableArrayList(
|
||||
KEY_EXPOSURE_SOURCE_LIST,
|
||||
|
||||
@ -199,8 +199,8 @@ object ViewPagerFragmentHelper {
|
||||
// 分类2.0
|
||||
TYPE_CATEGORY_V2 -> {
|
||||
className = CategoryV2Fragment::class.java.name
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_ID, entity.link)
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, entity.text)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_ID, entity.link)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_NAME, entity.text)
|
||||
bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper)
|
||||
}
|
||||
// 通用内容合集详情页
|
||||
|
||||
@ -1,38 +1,48 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import android.widget.RadioButton
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.forEach
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.setDrawableEnd
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.LayoutCategoryFilterBinding
|
||||
import com.gh.gamecenter.databinding.LayoutCategoryFilterSizeBinding
|
||||
import com.gh.gamecenter.databinding.PopCategoryFilterSizeBinding
|
||||
import com.gh.gamecenter.databinding.PopCategoryFilterTypeBinding
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
|
||||
class CategoryFilterView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var mTypeTv: TextView
|
||||
private var mCatalogTv: TextView
|
||||
private var mSizeTv: TextView
|
||||
private var mTypeContainer: View
|
||||
private var mCatalogContainer: View
|
||||
private var mSizeContainer: View
|
||||
private val binding: LayoutCategoryFilterBinding
|
||||
|
||||
private var mTypeFilterArray = arrayOf(SortType.RECOMMENDED, SortType.NEWEST, SortType.RATING)
|
||||
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
|
||||
private val mTypeFilterArray = arrayOf(SortType.RECOMMENDED, SortType.NEWEST, SortType.RATING)
|
||||
private val sizeFilterArray by lazy {
|
||||
listOf(
|
||||
SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"),
|
||||
SubjectSettingEntity.Size(min = -1, max = 100, text = "100M以下"),
|
||||
SubjectSettingEntity.Size(min = 100, max = 300, text = "100-300M"),
|
||||
SubjectSettingEntity.Size(min = 300, max = 500, text = "300-500M"),
|
||||
SubjectSettingEntity.Size(min = 500, max = 1000, text = "500M-1G"),
|
||||
SubjectSettingEntity.Size(min = 1000, max = -1, text = "1G以上")
|
||||
)
|
||||
}
|
||||
|
||||
private var mOnCategoryFilterSetupListener: OnCategoryFilterSetupListener? = null
|
||||
private var mOnFilterClickListener: OnFilterClickListener? = null
|
||||
@ -41,29 +51,28 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
private var mSizePopupWindow: PopupWindow? = null
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.layout_category_filter, this)
|
||||
|
||||
mTypeTv = findViewById(R.id.type_tv)
|
||||
mCatalogTv = findViewById(R.id.catalog_tv)
|
||||
mSizeTv = findViewById(R.id.size_tv)
|
||||
mTypeContainer = findViewById(R.id.container_type)
|
||||
mCatalogContainer = findViewById(R.id.container_category)
|
||||
mSizeContainer = findViewById(R.id.container_size)
|
||||
mTypeTv.text = mTypeFilterArray[0].value
|
||||
val inflater = LayoutInflater.from(context)
|
||||
binding = LayoutCategoryFilterBinding.inflate(inflater, this, true)
|
||||
|
||||
mTypeContainer.setOnClickListener {
|
||||
binding.ivTypeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivTypeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
|
||||
binding.ivSizeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivSizeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
|
||||
binding.tvType.text = mTypeFilterArray[0].value
|
||||
|
||||
binding.llTypeContainer.setOnClickListener {
|
||||
mOnFilterClickListener?.onTypeClick()
|
||||
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
|
||||
showSelectTypePopupWindow()
|
||||
}
|
||||
|
||||
mCatalogContainer.setOnClickListener {
|
||||
mOnFilterClickListener?.onCategoryClick()
|
||||
mOnCategoryFilterSetupListener?.onSetupSortCategory()
|
||||
}
|
||||
|
||||
mSizeContainer.setOnClickListener {
|
||||
binding.llSizeContainer.setOnClickListener {
|
||||
mOnFilterClickListener?.onSizeClick()
|
||||
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
|
||||
showSelectSizePopupWindow()
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,154 +85,133 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun resetSortSize() {
|
||||
mSizeTv.text = "全部大小"
|
||||
binding.tvType.text = R.string.size.toResString()
|
||||
}
|
||||
|
||||
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
|
||||
if (highlightIt) {
|
||||
targetTextView.background = ContextCompat.getDrawable(targetTextView.context, R.drawable.bg_tag_text)
|
||||
targetTextView.setTextColor(Color.WHITE)
|
||||
} else {
|
||||
targetTextView.background = null
|
||||
targetTextView.setTextColor(ContextCompat.getColor(targetTextView.context, com.gh.gamecenter.common.R.color.text_757575))
|
||||
}
|
||||
}
|
||||
private fun showSelectTypePopupWindow() {
|
||||
binding.tvType.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
|
||||
private fun showSelectTypePopupWindow(containerView: View, typeTv: TextView, typeText: String) {
|
||||
typeTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
binding.ivTypeArrow.setImageResource(R.drawable.ic_arrow_up)
|
||||
binding.ivTypeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
|
||||
val inflater = LayoutInflater.from(typeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
val windowWidth = typeTv.context.resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val typeBinding = PopCategoryFilterTypeBinding.inflate(inflater, null, false)
|
||||
val windowWidth = resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val windowHeight = mOnCategoryFilterSetupListener?.getPopHeight() ?: 0
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
typeBinding.root,
|
||||
windowWidth,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
if (windowHeight == 0) ViewGroup.LayoutParams.WRAP_CONTENT else (windowHeight - height)
|
||||
).apply { mTypePopupWindow = this }
|
||||
|
||||
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
|
||||
val backgroundView = layout.findViewById<View>(R.id.background)
|
||||
|
||||
backgroundView.setOnClickListener {
|
||||
typeBinding.root.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (type in mTypeFilterArray) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
val checkedId = when (binding.tvType.text.toString()) {
|
||||
"最新" -> R.id.rb_newest
|
||||
"评分" -> R.id.rb_score
|
||||
else -> R.id.rb_popular
|
||||
}
|
||||
|
||||
// 单列 3 个,强行设置宽度为屏幕的 1/3
|
||||
val width = windowWidth / 3
|
||||
val height = item.layoutParams.height
|
||||
val checkButton = typeBinding.root.findViewById<RadioButton>(checkedId)
|
||||
checkButton.setTypeface(checkButton.typeface, Typeface.BOLD)
|
||||
checkButton.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
checkButton.setDrawableEnd(R.drawable.ic_basic_checkmark_circle_fill)
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
tv.text = type.value
|
||||
|
||||
toggleHighlightedTextView(tv, typeText == type.value)
|
||||
|
||||
tv.tag = type.value
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
popupWindow.dismiss()
|
||||
typeTv.text = type.value
|
||||
val onCheckedChangeListener = CompoundButton.OnCheckedChangeListener { button, isChecked ->
|
||||
if (isChecked) {
|
||||
button.setTypeface(button.typeface, Typeface.BOLD)
|
||||
button.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
button.setDrawableEnd(R.drawable.ic_basic_checkmark_circle_fill)
|
||||
|
||||
val type = when (button.id) {
|
||||
R.id.rb_newest -> SortType.NEWEST
|
||||
R.id.rb_score -> SortType.RATING
|
||||
else -> SortType.RECOMMENDED
|
||||
}
|
||||
binding.tvType.text = type.value
|
||||
mOnCategoryFilterSetupListener?.onSetupSortType(type)
|
||||
popupWindow.dismiss()
|
||||
} else {
|
||||
button.setTypeface(button.typeface, Typeface.NORMAL)
|
||||
button.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
|
||||
button.setDrawableEnd(null)
|
||||
}
|
||||
}
|
||||
typeBinding.rgFilter.forEach {
|
||||
if (it is RadioButton) {
|
||||
it.setOnCheckedChangeListener(onCheckedChangeListener)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
typeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
|
||||
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
binding.tvType.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
binding.ivTypeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivTypeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
mTypePopupWindow = null
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.animationStyle = 0
|
||||
popupWindow.showAsDropDown(containerView, 0, 0)
|
||||
popupWindow.showAsDropDown(this, 0, 0)
|
||||
}
|
||||
|
||||
private fun showSelectSizePopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
private fun showSelectSizePopupWindow() {
|
||||
binding.tvSize.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
binding.ivSizeArrow.setImageResource(R.drawable.ic_arrow_up)
|
||||
binding.ivSizeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
|
||||
val inflater = LayoutInflater.from(sizeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
val windowWidth = sizeTv.context.resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val sizeBinding = PopCategoryFilterSizeBinding.inflate(inflater, null, false)
|
||||
val windowWidth = context.resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val windowHeight = mOnCategoryFilterSetupListener?.getPopHeight() ?: 0
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
sizeBinding.root,
|
||||
windowWidth,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
if (windowHeight == 0) ViewGroup.LayoutParams.WRAP_CONTENT else (windowHeight - height)
|
||||
).apply { mSizePopupWindow = this }
|
||||
|
||||
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
|
||||
val backgroundView = layout.findViewById<View>(R.id.background)
|
||||
|
||||
sizeFilterArray = if (sizeFilterArray == null) {
|
||||
getDefaultSizeFilterArray()
|
||||
} else {
|
||||
sizeFilterArray?.apply {
|
||||
if (firstOrNull()?.text != "全部大小") {
|
||||
add(0, SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backgroundView.setOnClickListener {
|
||||
sizeBinding.root.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (size in sizeFilterArray!!) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
|
||||
// 单列 3 个,强行设置宽度为屏幕的 1/3
|
||||
val width = windowWidth / 3
|
||||
val height = item.layoutParams.height
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
tv.text = size.text
|
||||
|
||||
toggleHighlightedTextView(tv, sizeText == size.text)
|
||||
|
||||
tv.tag = size.text
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
popupWindow.dismiss()
|
||||
sizeTv.text = size.text
|
||||
|
||||
mOnCategoryFilterSetupListener?.onSetupSortSize(size)
|
||||
}
|
||||
sizeBinding.rvSize.setOnClickListener {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
val sizeAdapter = CategoryFilterSizeAdapter {
|
||||
if (it.min == INVALID_SIZE && it.max == INVALID_SIZE) {
|
||||
binding.tvSize.text = R.string.size.toResString()
|
||||
} else {
|
||||
binding.tvSize.text = it.text
|
||||
}
|
||||
popupWindow.dismiss()
|
||||
mOnCategoryFilterSetupListener?.onSetupSortSize(it)
|
||||
}
|
||||
sizeBinding.rvSize.layoutManager = GridLayoutManager(context, 3, RecyclerView.VERTICAL, false)
|
||||
sizeBinding.rvSize.adapter = sizeAdapter
|
||||
val selectedPosition =
|
||||
sizeFilterArray.indexOfFirst { it.text?.contains(binding.tvSize.text.toString()) == true }
|
||||
|
||||
sizeAdapter.setData(sizeFilterArray, selectedPosition)
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
binding.tvSize.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
binding.ivSizeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivSizeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
mSizePopupWindow = null
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.animationStyle = 0
|
||||
popupWindow.showAsDropDown(containerView, 0, 0)
|
||||
}
|
||||
|
||||
private fun getDefaultSizeFilterArray(): ArrayList<SubjectSettingEntity.Size> {
|
||||
return arrayListOf<SubjectSettingEntity.Size>().apply {
|
||||
add(SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
|
||||
add(SubjectSettingEntity.Size(min = -1, max = 100, text = "100M以下"))
|
||||
add(SubjectSettingEntity.Size(min = 100, max = 300, text = "100-300M"))
|
||||
add(SubjectSettingEntity.Size(min = 300, max = 500, text = "300-500M"))
|
||||
add(SubjectSettingEntity.Size(min = 500, max = 1000, text = "500M-1G"))
|
||||
add(SubjectSettingEntity.Size(min = 1000, max = -1, text = "1G以上"))
|
||||
}
|
||||
popupWindow.showAsDropDown(this, 0, 0)
|
||||
}
|
||||
|
||||
fun setRootBackgroundColor(@ColorInt color: Int) {
|
||||
@ -231,21 +219,23 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun setItemTextColor(@ColorInt color: Int) {
|
||||
mTypeTv.setTextColor(color)
|
||||
mCatalogTv.setTextColor(color)
|
||||
mSizeTv.setTextColor(color)
|
||||
val colorInt = com.gh.gamecenter.common.R.color.text_neutral.toColor(context)
|
||||
binding.tvType.setTextColor(color)
|
||||
binding.tvSize.setTextColor(color)
|
||||
binding.ivTypeArrow.imageTintList = ColorStateList.valueOf(colorInt)
|
||||
binding.ivSizeArrow.imageTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
|
||||
fun updatePopupWindow() {
|
||||
when {
|
||||
mTypePopupWindow != null && mTypePopupWindow!!.isShowing -> {
|
||||
mTypePopupWindow?.dismiss()
|
||||
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
|
||||
showSelectTypePopupWindow()
|
||||
}
|
||||
|
||||
mSizePopupWindow != null && mSizePopupWindow!!.isShowing -> {
|
||||
mSizePopupWindow?.dismiss()
|
||||
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
|
||||
showSelectSizePopupWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -253,18 +243,69 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
interface OnCategoryFilterSetupListener {
|
||||
fun onSetupSortSize(sortSize: SubjectSettingEntity.Size)
|
||||
fun onSetupSortType(sortType: SortType)
|
||||
fun onSetupSortCategory()
|
||||
fun getPopHeight(): Int
|
||||
}
|
||||
|
||||
interface OnFilterClickListener {
|
||||
fun onCategoryClick()
|
||||
fun onTypeClick()
|
||||
fun onSizeClick()
|
||||
}
|
||||
|
||||
enum class SortType(val value: String) {
|
||||
RECOMMENDED("热门推荐"),
|
||||
NEWEST("最新上线"),
|
||||
RATING("最高评分")
|
||||
RECOMMENDED("热门"),
|
||||
NEWEST("最新"),
|
||||
RATING("评分")
|
||||
}
|
||||
|
||||
class CategoryFilterSizeAdapter(private val itemClick: (SubjectSettingEntity.Size) -> Unit) :
|
||||
RecyclerView.Adapter<CategoryFilterSizeAdapter.SizeViewHolder>() {
|
||||
|
||||
private val dataList = arrayListOf<SubjectSettingEntity.Size>()
|
||||
private var selectedPosition = 0
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setData(data: List<SubjectSettingEntity.Size>, position: Int) {
|
||||
dataList.clear()
|
||||
dataList.addAll(data)
|
||||
selectedPosition = position
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SizeViewHolder {
|
||||
return SizeViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataList.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SizeViewHolder, position: Int) {
|
||||
val item = dataList[position]
|
||||
val context = holder.binding.root.context
|
||||
if (selectedPosition == position) {
|
||||
holder.binding.root.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
holder.binding.root.setBackgroundResource(R.drawable.shape_category_filter_size_item_selected)
|
||||
} else {
|
||||
holder.binding.root.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
|
||||
holder.binding.root.setBackgroundResource(R.drawable.shape_category_filter_size_item_normal)
|
||||
}
|
||||
holder.binding.root.text = item.text
|
||||
holder.binding.root.setOnClickListener {
|
||||
if (selectedPosition == holder.bindingAdapterPosition) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
val lastPosition = selectedPosition
|
||||
selectedPosition = holder.bindingAdapterPosition
|
||||
notifyItemChanged(lastPosition)
|
||||
notifyItemChanged(selectedPosition)
|
||||
itemClick(item)
|
||||
}
|
||||
}
|
||||
|
||||
class SizeViewHolder(val binding: LayoutCategoryFilterSizeBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INVALID_SIZE = -1
|
||||
}
|
||||
}
|
||||
@ -1,82 +1,89 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.CategoryDirectoryItemBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class CategoryDirectoryAdapter(
|
||||
context: Context,
|
||||
private val mViewModel: CategoryV2ViewModel,
|
||||
private var mList: List<CategoryEntity>
|
||||
) : BaseRecyclerAdapter<CategoryDirectoryAdapter.CategoryDirectoryItemViewHolder>(context) {
|
||||
private val listener: SearchCategoryPop.OnSearchCategoryListener
|
||||
) : RecyclerView.Adapter<CategoryDirectoryAdapter.CategoryDirectoryItemViewHolder>() {
|
||||
|
||||
val width = mContext.resources.displayMetrics.widthPixels * 260 / 360
|
||||
private val data = arrayListOf<CategoryEntity>()
|
||||
|
||||
fun setListData(list: List<CategoryEntity>) {
|
||||
mList = list
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setListData(newData: List<CategoryEntity>) {
|
||||
data.clear()
|
||||
data.addAll(newData)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
override fun getItemCount() = data.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
CategoryDirectoryItemViewHolder(CategoryDirectoryItemBinding.inflate(mLayoutInflater))
|
||||
CategoryDirectoryItemViewHolder(listener, parent.toBinding())
|
||||
|
||||
override fun onBindViewHolder(holder: CategoryDirectoryItemViewHolder, position: Int) {
|
||||
holder.binding.run {
|
||||
root.layoutParams = root.layoutParams?.apply {
|
||||
width = mContext.resources.displayMetrics.widthPixels * 260 / 360
|
||||
} ?: RecyclerView.LayoutParams(width, RecyclerView.LayoutParams.WRAP_CONTENT)
|
||||
holder.onBind(position, data[position])
|
||||
}
|
||||
|
||||
val padTop = if (position == 0) 16F.dip2px() else 24F.dip2px()
|
||||
root.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 0)
|
||||
override fun onBindViewHolder(holder: CategoryDirectoryItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
if (payloads.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
} else {
|
||||
holder.notifyItemSelectedChanged()
|
||||
}
|
||||
|
||||
val entity = mList[position]
|
||||
title.text = entity.name
|
||||
title.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
}
|
||||
|
||||
subCategoryRv.run {
|
||||
if (adapter is SubCategoryAdapter) {
|
||||
layoutManager = GridLayoutManager(mContext, 3)
|
||||
adapter = entity.data?.let {
|
||||
SubCategoryAdapter(
|
||||
mContext,
|
||||
mViewModel,
|
||||
it,
|
||||
position
|
||||
)
|
||||
}
|
||||
} else {
|
||||
layoutManager = GridLayoutManager(mContext, 3)
|
||||
adapter = entity.data?.let {
|
||||
SubCategoryAdapter(
|
||||
mContext,
|
||||
mViewModel,
|
||||
it,
|
||||
position
|
||||
)
|
||||
}
|
||||
addItemDecoration(
|
||||
GridSpacingItemColorDecoration(
|
||||
mContext,
|
||||
6,
|
||||
6,
|
||||
com.gh.gamecenter.common.R.color.transparent
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
fun notifyItemSelectedChanged(parentId: String) {
|
||||
val position = data.indexOfFirst { it.id == parentId }
|
||||
if (position != -1) {
|
||||
notifyItemChanged(position, "")
|
||||
}
|
||||
}
|
||||
|
||||
class CategoryDirectoryItemViewHolder(val binding: CategoryDirectoryItemBinding) :
|
||||
BaseRecyclerViewHolder<Any>(binding.root)
|
||||
class CategoryDirectoryItemViewHolder(
|
||||
private val listener: SearchCategoryPop.OnSearchCategoryListener,
|
||||
val binding: CategoryDirectoryItemBinding
|
||||
) :
|
||||
ViewHolder(binding.root) {
|
||||
|
||||
private val childAdapter by lazy {
|
||||
SubCategoryAdapter(listener)
|
||||
}
|
||||
|
||||
fun onBind(position: Int, item: CategoryEntity) {
|
||||
|
||||
val context = binding.root.context
|
||||
binding.title.text = item.name
|
||||
|
||||
if (binding.subCategoryRv.adapter == null) {
|
||||
binding.subCategoryRv.layoutManager = object : GridLayoutManager(context, 4) {
|
||||
override fun canScrollVertically(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
binding.subCategoryRv.adapter = childAdapter
|
||||
binding.subCategoryRv.addItemDecoration(
|
||||
GridSpacingItemColorDecoration(
|
||||
context,
|
||||
8,
|
||||
8,
|
||||
com.gh.gamecenter.common.R.color.transparent
|
||||
)
|
||||
)
|
||||
}
|
||||
childAdapter.setData(position, item)
|
||||
}
|
||||
|
||||
fun notifyItemSelectedChanged() {
|
||||
childAdapter.notifyItemRangeChanged(0, childAdapter.itemCount, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,8 +37,8 @@ class CategoryV2Activity : DownloadToolbarActivity() {
|
||||
companion object {
|
||||
fun getIntent(context: Context, catalogId: String, catalogTitle: String, entrance: String): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_ID, catalogId)
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, catalogTitle)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_ID, catalogId)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_NAME, catalogTitle)
|
||||
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
|
||||
return getTargetIntent(context, CategoryV2Activity::class.java, CategoryV2Fragment::class.java, bundle)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
@ -13,11 +14,13 @@ import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class CategoryV2Adapter(
|
||||
context: Context,
|
||||
private val mFragment: CategoryV2Fragment,
|
||||
private val mViewModel: CategoryV2ViewModel,
|
||||
private val mList: List<SidebarsEntity.SidebarEntity>
|
||||
) : BaseRecyclerAdapter<CategoryV2Adapter.CategoryV2ItemViewHolder>(context) {
|
||||
|
||||
var selectedPosition = 0
|
||||
private set
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
@ -28,25 +31,35 @@ class CategoryV2Adapter(
|
||||
val catalogEntity = mList[position]
|
||||
catalogName.text = catalogEntity.name
|
||||
recommendTag.goneIf(!catalogEntity.recommended)
|
||||
if (catalogEntity.name == mViewModel.selectedCategoryName) {
|
||||
if (position == selectedPosition) {
|
||||
selectedTag.visibility = View.VISIBLE
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext))
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
catalogName.setTypeface(null, Typeface.BOLD)
|
||||
root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(mContext))
|
||||
} else {
|
||||
selectedTag.visibility = View.GONE
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(mContext))
|
||||
catalogName.setTypeface(null, Typeface.NORMAL)
|
||||
root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_background.toColor(mContext))
|
||||
}
|
||||
root.setOnClickListener {
|
||||
if (catalogEntity.name != mViewModel.selectedCategoryName) {
|
||||
mViewModel.selectedCategoryName = catalogEntity.name
|
||||
mFragment.changeCategory(position)
|
||||
if (position != selectedPosition) {
|
||||
mViewModel.logClickSide()
|
||||
notifyDataSetChanged()
|
||||
mViewModel.selectSidebarsPosition(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectPosition(newPosition: Int) {
|
||||
if (selectedPosition == newPosition) {
|
||||
return
|
||||
}
|
||||
val oldSelection = selectedPosition
|
||||
selectedPosition = newPosition
|
||||
notifyItemChanged(oldSelection)
|
||||
notifyItemChanged(newPosition)
|
||||
}
|
||||
|
||||
class CategoryV2ItemViewHolder(val binding: CategoryV2ItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.SearchActivity
|
||||
@ -22,35 +22,49 @@ import com.gh.gamecenter.common.view.FixLinearLayoutManager
|
||||
import com.gh.gamecenter.core.utils.PageSwitchDataHelper
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.databinding.FragmentCategoryBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.entity.SidebarsEntity
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperViewModel
|
||||
|
||||
class CategoryV2Fragment : LazyFragment() {
|
||||
|
||||
private var mBinding: FragmentCategoryBinding? = null
|
||||
private var mViewModel: CategoryV2ViewModel? = null
|
||||
|
||||
private val viewModel by viewModels<CategoryV2ViewModel>()
|
||||
|
||||
private var mHomeViewModel: SearchToolbarTabWrapperViewModel? = null
|
||||
private var mEntity: SidebarsEntity? = null
|
||||
private var mSpecialCatalogFragment: SpecialCatalogFragment? = null
|
||||
private var mCategoryV2ListFragment: CategoryV2ListFragment? = null
|
||||
private var mLastPageDataMap: HashMap<String, String>? = null
|
||||
|
||||
private var mCategoryId: String = ""
|
||||
private var mCategoryTitle: String = ""
|
||||
private var pageId: String = ""
|
||||
private var pageName: String = ""
|
||||
private var mLastSelectedPosition = -1
|
||||
|
||||
private var searchCategoryPop: SearchCategoryPop? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pageId = arguments?.getString(EntranceConsts.KEY_PAGE_ID) ?: ""
|
||||
pageName = arguments?.getString(EntranceConsts.KEY_PAGE_NAME) ?: ""
|
||||
mLastPageDataMap = PageSwitchDataHelper.popLastPageData()
|
||||
savedInstanceState?.run {
|
||||
mLastSelectedPosition = getInt(EntranceConsts.KEY_LAST_SELECTED_POSITION)
|
||||
}
|
||||
|
||||
// 除了这里以外,下面还有一个判断是否为首页 tab 栏的赋值
|
||||
var entrance = if (mEntrance.contains("首页")) "首页" else "板块"
|
||||
|
||||
val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: ""
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true && multiTabNavId.isNotEmpty()) {
|
||||
mHomeViewModel =
|
||||
viewModelProviderFromParent(SearchToolbarTabWrapperViewModel.Factory(multiTabNavId, ""), multiTabNavId)
|
||||
entrance = "首页Tab栏"
|
||||
}
|
||||
viewModel.init(entrance)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
mViewModel?.run {
|
||||
outState.putInt(EntranceConsts.KEY_LAST_SELECTED_POSITION, selectedCategoryPosition)
|
||||
}
|
||||
outState.putInt(EntranceConsts.KEY_LAST_SELECTED_POSITION, viewModel.selectedSidebarsPosition.value ?: 0)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
@ -61,100 +75,40 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
mCategoryId = arguments?.getString(EntranceConsts.KEY_CATEGORY_ID) ?: ""
|
||||
mCategoryTitle = arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: ""
|
||||
mLastPageDataMap = PageSwitchDataHelper.popLastPageData()
|
||||
mViewModel = viewModelProviderFromParent(CategoryV2ViewModel.Factory(mCategoryId, mCategoryTitle), mCategoryId)
|
||||
|
||||
// 除了这里以外,下面还有一个判断是否为首页 tab 栏的赋值
|
||||
mViewModel?.entrance = if (mEntrance.contains("首页")) "首页" else "板块"
|
||||
|
||||
val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: ""
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true && multiTabNavId.isNotEmpty()) {
|
||||
mHomeViewModel = viewModelProviderFromParent(SearchToolbarTabWrapperViewModel.Factory(multiTabNavId, ""), multiTabNavId)
|
||||
mViewModel?.entrance = "首页Tab栏"
|
||||
}
|
||||
mViewModel?.logAppearance()
|
||||
viewModel.logAppearance()
|
||||
|
||||
super.onFragmentFirstVisible()
|
||||
|
||||
viewModel.loadData(pageId, pageName)
|
||||
}
|
||||
|
||||
|
||||
override fun initRealView() {
|
||||
super.initRealView()
|
||||
initMenu(R.menu.menu_search)
|
||||
setNavigationTitle(mCategoryTitle)
|
||||
setNavigationTitle(pageName)
|
||||
|
||||
mBinding?.run {
|
||||
val width = resources.displayMetrics.widthPixels * 260 / 360
|
||||
drawerLayout.setScrimColor(com.gh.gamecenter.common.R.color.black_alpha_30.toColor())
|
||||
// 关闭手势滑动
|
||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
|
||||
override fun onDrawerStateChanged(newState: Int) {
|
||||
}
|
||||
|
||||
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
|
||||
}
|
||||
|
||||
override fun onDrawerClosed(drawerView: View) {
|
||||
showGuide()
|
||||
}
|
||||
|
||||
override fun onDrawerOpened(drawerView: View) {
|
||||
}
|
||||
|
||||
})
|
||||
directoryContainer.layoutParams.width = width
|
||||
directoryRv.layoutParams.width = width
|
||||
|
||||
// 嵌入在首页时特殊处理
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
|
||||
root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
|
||||
root.setPadding(0, 8F.dip2px(), 0, 0)
|
||||
directoryRv.isNestedScrollingEnabled = false
|
||||
categoryRv.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_background.toColor(requireContext()))
|
||||
}
|
||||
tvMoreCategory.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
|
||||
tvTagNumber.typeface =
|
||||
Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH)
|
||||
}
|
||||
|
||||
mViewModel?.directoriesLiveData?.observeNonNull(viewLifecycleOwner) {
|
||||
initDirectoryView(it)
|
||||
}
|
||||
|
||||
mViewModel?.selectedCountLiveData?.observeNonNull(viewLifecycleOwner) {
|
||||
mBinding?.run {
|
||||
if (it == 0) {
|
||||
confirmTv.text = "确定"
|
||||
} else {
|
||||
mViewModel?.run {
|
||||
if (selectedCategoryName != "全部") {
|
||||
selectedCategoryName = "全部"
|
||||
selectedCategoryPosition = if (mEntity?.hasSpecial == true) 1 else 0
|
||||
categoryRv.adapter?.notifyDataSetChanged()
|
||||
mCategoryV2ListFragment?.updateSubCategoryId("all")
|
||||
}
|
||||
}
|
||||
|
||||
confirmTv.text = "确定(已选${it})"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mViewModel?.sidebarsLiveData?.observe(viewLifecycleOwner, Observer {
|
||||
viewModel.sidebarsLiveData.observe(viewLifecycleOwner) { sidebars ->
|
||||
mBinding?.run {
|
||||
reuseLoading.root.visibility = View.GONE
|
||||
if (it != null) {
|
||||
if (sidebars != null) {
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
categoryContainer.visibility = View.VISIBLE
|
||||
reuseNoneData.root.visibility = View.GONE
|
||||
mEntity = it
|
||||
(mEntity!!.sidebars as ArrayList).run {
|
||||
mEntity = sidebars
|
||||
(sidebars.sidebars as ArrayList).run {
|
||||
val allEntity = SidebarsEntity.SidebarEntity(name = "全部", categoryId = "all")
|
||||
if (mEntity!!.hasSpecial) {
|
||||
add(0, allEntity)
|
||||
if (sidebars.hasSpecial) {
|
||||
val specialEntity = SidebarsEntity.SidebarEntity(name = "精选")
|
||||
add(0, specialEntity)
|
||||
add(1, allEntity)
|
||||
} else {
|
||||
add(0, allEntity)
|
||||
add(1, specialEntity)
|
||||
}
|
||||
}
|
||||
initView()
|
||||
@ -165,12 +119,70 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
reuseNoConnection.root.setOnClickListener {
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
reuseLoading.root.visibility = View.VISIBLE
|
||||
mViewModel?.getSidebars()
|
||||
mViewModel?.getCategoryDirectories()
|
||||
viewModel.loadData(pageId, pageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
viewModel.directoriesLiveData.observe(viewLifecycleOwner) {
|
||||
searchCategoryPop?.setData(it)
|
||||
}
|
||||
|
||||
viewModel.selectedSubCategories.observe(viewLifecycleOwner) { selectedList ->
|
||||
searchCategoryPop?.updateCategorySelected(selectedList)
|
||||
updateMoreCategory(selectedList.size)
|
||||
}
|
||||
|
||||
viewModel.selectedSidebarsPosition.observe(viewLifecycleOwner) {
|
||||
mLastSelectedPosition = it
|
||||
onSelectedPositionChanged(it)
|
||||
|
||||
val adapter = mBinding?.categoryRv?.adapter
|
||||
if (adapter is CategoryV2Adapter) {
|
||||
adapter.selectPosition(it)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.notifySubCategorySelected.observe(
|
||||
viewLifecycleOwner,
|
||||
EventObserver {
|
||||
searchCategoryPop?.notifyItemSelectedChanged(it)
|
||||
})
|
||||
}
|
||||
|
||||
private fun createSearchPop(isAutoRequestFocus: Boolean): SearchCategoryPop {
|
||||
val height = mBinding!!.root.height
|
||||
return SearchCategoryPop.newInstance(requireContext(), height, isAutoRequestFocus, pageId, pageName).apply {
|
||||
val data = viewModel.directoriesLiveData.value ?: listOf()
|
||||
setData(data)
|
||||
val selectedList = viewModel.selectedSubCategories.value
|
||||
updateCategorySelected(selectedList)
|
||||
setOnSearchCategoryListener(object : SearchCategoryPop.OnSearchCategoryListener {
|
||||
override fun isEnableSelected(): Boolean {
|
||||
val size = viewModel.selectedSubCategories.value?.size ?: 0
|
||||
return size < 5
|
||||
}
|
||||
|
||||
override fun onItemSelected(selected: CategoryV2ViewModel.SelectedTags) {
|
||||
viewModel.addSubCategorySelected(selected)
|
||||
}
|
||||
|
||||
override fun onItemRemoved(parentId: String, subCategoryId: String) {
|
||||
viewModel.removeSubCategorySelected(parentId, subCategoryId, "全部游戏")
|
||||
}
|
||||
|
||||
override fun onResetSelected() {
|
||||
viewModel.clearSelectedTag()
|
||||
viewModel.updateGameFiltered()
|
||||
}
|
||||
|
||||
override fun onSubmit() {
|
||||
viewModel.logClickDetermine()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun removeGuide() {
|
||||
@ -181,15 +193,7 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
|
||||
private fun showGuide() {
|
||||
if (!isAdded) return
|
||||
mBinding?.run {
|
||||
val isShow = SPUtils.getBoolean(Constants.SP_SHOW_CATEGORY_GUIDE)
|
||||
if (isShow) return
|
||||
|
||||
guideContainer.layoutParams = (guideContainer.layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||
val screenWidth = resources.displayMetrics.widthPixels
|
||||
leftMargin = screenWidth * 66F.dip2px() / 360F.dip2px()
|
||||
}
|
||||
guideContainer.visibility = View.VISIBLE
|
||||
|
||||
postDelayedRunnable({
|
||||
@ -204,7 +208,7 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
override fun onMenuItemClick(menuItem: MenuItem?) {
|
||||
menuItem?.run {
|
||||
if (itemId == R.id.menu_search) {
|
||||
LogUtils.uploadSearchGame("access_to_search", mCategoryTitle, "", "")
|
||||
LogUtils.uploadSearchGame("access_to_search", pageName, "", "")
|
||||
val intent = SearchActivity.getIntent(
|
||||
requireContext(),
|
||||
false,
|
||||
@ -217,63 +221,64 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDirectoryView(list: List<CategoryEntity>) {
|
||||
mBinding?.run {
|
||||
mViewModel?.run {
|
||||
if (directoryRv.adapter != null) {
|
||||
(directoryRv.adapter as? CategoryDirectoryAdapter)?.setListData(list)
|
||||
} else {
|
||||
directoryRv.layoutManager = FixLinearLayoutManager(requireContext())
|
||||
directoryRv.adapter = CategoryDirectoryAdapter(
|
||||
requireContext(),
|
||||
this,
|
||||
list
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
resetTv.setOnClickListener {
|
||||
mViewModel?.logClickReset("全部类别")
|
||||
confirmTv.text = "确定"
|
||||
mViewModel?.resetDirectoryList()
|
||||
mCategoryV2ListFragment?.changeCategoryTab()
|
||||
}
|
||||
|
||||
confirmTv.setOnClickListener {
|
||||
mViewModel?.logClickDetermine()
|
||||
drawerLayout.closeDrawer(GravityCompat.START)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
if (mEntity?.sidebars?.isNullOrEmpty() == true || mViewModel == null) return
|
||||
if (mEntity?.sidebars.isNullOrEmpty()) return
|
||||
initSelectedCategory()
|
||||
initCategoryRv()
|
||||
initContentFragment()
|
||||
|
||||
mBinding?.run {
|
||||
|
||||
vSearchCategory.setOnClickListener {
|
||||
SensorsBridge.logClassificationSearch(pageId, pageName)
|
||||
removeGuide()
|
||||
showSearchPop(true)
|
||||
}
|
||||
|
||||
vMoreCategory.setOnClickListener {
|
||||
removeGuide()
|
||||
showSearchPop(false)
|
||||
}
|
||||
}
|
||||
|
||||
val isShow = SPUtils.getBoolean(Constants.SP_SHOW_CATEGORY_GUIDE)
|
||||
if (!isShow) {
|
||||
postDelayedRunnable({
|
||||
showSearchPop(false)
|
||||
searchCategoryPop?.setOnDismissListener {
|
||||
showGuide()
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun showSearchPop(isAutoRequestFocus: Boolean) {
|
||||
mBinding?.run {
|
||||
val location = IntArray(2)
|
||||
vSearchCategory.getLocationOnScreen(location)
|
||||
|
||||
val popTop = location[1] - 8F.dip2px()
|
||||
searchCategoryPop = createSearchPop(isAutoRequestFocus)
|
||||
searchCategoryPop?.showAtLocation(vSearchCategory, Gravity.TOP, 0, popTop)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSelectedCategory() {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
if (mLastSelectedPosition != -1) {
|
||||
selectedCategoryPosition = mLastSelectedPosition
|
||||
selectedCategoryName = sidebars[mLastSelectedPosition].name
|
||||
} else {
|
||||
selectedCategoryPosition = 0
|
||||
selectedCategoryName = sidebars[0].name
|
||||
}
|
||||
}
|
||||
if (mLastSelectedPosition != -1) {
|
||||
viewModel.selectSidebarsPosition(mLastSelectedPosition)
|
||||
} else {
|
||||
// 默认选中第 0 个 位置
|
||||
viewModel.selectSidebarsPosition(0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initCategoryRv() {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
viewModel.run {
|
||||
mBinding?.categoryRv?.layoutManager = FixLinearLayoutManager(requireContext())
|
||||
mBinding?.categoryRv?.adapter = CategoryV2Adapter(
|
||||
requireContext(),
|
||||
this@CategoryV2Fragment,
|
||||
this,
|
||||
sidebars
|
||||
)
|
||||
@ -281,226 +286,93 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun initContentFragment() {
|
||||
mEntity?.apply {
|
||||
mViewModel?.apply {
|
||||
if (hasSpecial && selectedCategoryPosition == 0) {
|
||||
initSpecialCatalogFragment()
|
||||
} else {
|
||||
initCategoryV2ListFragment()
|
||||
private fun onSelectedPositionChanged(position: Int) {
|
||||
mEntity?.run {
|
||||
viewModel.run {
|
||||
clearSelectedTag()
|
||||
childFragmentManager.fragments.find { it.isAdded }
|
||||
val targetFragment =
|
||||
if (hasSpecial && position == 1) {
|
||||
val fragment = childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.name)
|
||||
?: SpecialCatalogFragment()
|
||||
fragment.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to true,
|
||||
EntranceConsts.KEY_CATALOG_ID to pageId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to pageName,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
fragment
|
||||
|
||||
// 第一次点"全部"tab展开全部类别选择框
|
||||
// 加延迟是为了防止卡顿
|
||||
if (SPUtils.getBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, true)) {
|
||||
SPUtils.setBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, false)
|
||||
mBinding?.drawerLayout?.postDelayed({
|
||||
tryCatchInRelease { openDrawer() }
|
||||
}, 500L)
|
||||
} else {
|
||||
val fragment = (childFragmentManager.findFragmentByTag(CategoryV2ListFragment::class.java.name)
|
||||
?: CategoryV2ListFragment())
|
||||
fragment.arguments = bundleOf(
|
||||
EntranceConsts.KEY_PAGE_ID to id,
|
||||
EntranceConsts.KEY_PAGE_NAME to pageName,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSpecialCatalogFragment() {
|
||||
mEntity?.run {
|
||||
mSpecialCatalogFragment = childFragmentManager
|
||||
.findFragmentByTag(SpecialCatalogFragment::class.java.name)
|
||||
as? SpecialCatalogFragment ?: SpecialCatalogFragment()
|
||||
mSpecialCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to true,
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mSpecialCatalogFragment!!,
|
||||
SpecialCatalogFragment::class.java.name
|
||||
)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCategoryV2ListFragment() {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
mCategoryV2ListFragment = childFragmentManager
|
||||
.findFragmentByTag(CategoryV2ListFragment::class.java.name)
|
||||
as? CategoryV2ListFragment ?: CategoryV2ListFragment()
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[selectedCategoryPosition].categoryId,
|
||||
EntranceConsts.KEY_CATEGORY_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mCategoryV2ListFragment!!,
|
||||
CategoryV2ListFragment::class.java.name
|
||||
)
|
||||
.replace(R.id.gamesContainer, targetFragment, targetFragment::class.java.name)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCategory(position: Int) {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
resetDirectoryList()
|
||||
|
||||
if (hasSpecial) {
|
||||
if (selectedCategoryPosition == 0) {
|
||||
mCategoryV2ListFragment = childFragmentManager
|
||||
.findFragmentByTag(CategoryV2ListFragment::class.java.name)
|
||||
as? CategoryV2ListFragment ?: CategoryV2ListFragment()
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mCategoryV2ListFragment!!,
|
||||
CategoryV2ListFragment::class.java.name
|
||||
)
|
||||
.commitAllowingStateLoss()
|
||||
} else {
|
||||
if (position == 0) {
|
||||
removeGuide()
|
||||
|
||||
mSpecialCatalogFragment = childFragmentManager
|
||||
.findFragmentByTag(SpecialCatalogFragment::class.java.name)
|
||||
as? SpecialCatalogFragment ?: SpecialCatalogFragment()
|
||||
mSpecialCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to true,
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mSpecialCatalogFragment!!,
|
||||
SpecialCatalogFragment::class.java.name
|
||||
)
|
||||
.commitAllowingStateLoss()
|
||||
} else {
|
||||
if (mCategoryV2ListFragment?.isStateSaved == false) {
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
}
|
||||
mCategoryV2ListFragment?.changeCategoryTab(sidebars[position].categoryId)
|
||||
}
|
||||
}
|
||||
|
||||
// 第一次点"全部"tab展开全部类别选择框
|
||||
// 加延迟是为了防止卡顿
|
||||
if (position == 1 && SPUtils.getBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, true)) {
|
||||
SPUtils.setBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, false)
|
||||
mBinding?.drawerLayout?.postDelayed({
|
||||
tryCatchInRelease { openDrawer() }
|
||||
}, 200L)
|
||||
}
|
||||
} else {
|
||||
if (mCategoryV2ListFragment?.isStateSaved == false) {
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
}
|
||||
mCategoryV2ListFragment?.changeCategoryTab(sidebars[position].categoryId)
|
||||
}
|
||||
selectedCategoryPosition = position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun openDirectoryLayout() {
|
||||
private fun updateMoreCategory(size: Int) {
|
||||
mBinding?.run {
|
||||
var i = 0
|
||||
mViewModel?.run {
|
||||
mEntity?.run {
|
||||
val sidebar = sidebars[selectedCategoryPosition]
|
||||
if (sidebar.name != "全部") {
|
||||
directories.forEachIndexed { index, entity ->
|
||||
if (sidebar.type == "level_one") {
|
||||
if (sidebar.categoryId == entity.id) {
|
||||
i = index
|
||||
return@run
|
||||
}
|
||||
} else {
|
||||
if (sidebar.parentId == entity.id) {
|
||||
i = index
|
||||
return@run
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sidebar.name == "全部" && selectedCategoryList.isNotEmpty()) {
|
||||
i = selectedCategoryList[0].primaryIndex
|
||||
}
|
||||
if (size > 0) {
|
||||
vMoreCategory.setBackgroundResource(R.drawable.bg_more_category_filtered)
|
||||
TextViewCompat.setCompoundDrawableTintList(
|
||||
tvMoreCategory,
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_theme.toColor(requireContext()))
|
||||
)
|
||||
tvMoreCategory.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(requireContext()))
|
||||
tvTagNumber.goneIf(false) {
|
||||
tvTagNumber.text = "$size"
|
||||
}
|
||||
} else {
|
||||
vMoreCategory.setBackgroundResource(R.drawable.bg_more_category_default)
|
||||
TextViewCompat.setCompoundDrawableTintList(
|
||||
tvMoreCategory,
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
|
||||
)
|
||||
tvMoreCategory.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
|
||||
tvTagNumber.goneIf(true)
|
||||
}
|
||||
openDrawer()
|
||||
(directoryRv.layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(i, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openDrawer() {
|
||||
mBinding?.drawerLayout?.openDrawer(GravityCompat.START)
|
||||
|
||||
mHomeViewModel?.let {
|
||||
mBinding?.directoryContainer?.setPadding(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
requireContext().resources.getDimension(com.gh.gamecenter.common.R.dimen.main_bottom_tab_height).toInt() - it.appBarOffset
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
getItemMenu(R.id.menu_search)?.setIcon(R.drawable.ic_column_search)
|
||||
mBinding?.categoryRv?.adapter?.run {
|
||||
mBinding?.categoryRv?.recycledViewPool?.clear()
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
mBinding?.directoryRv?.adapter?.run {
|
||||
mBinding?.directoryRv?.recycledViewPool?.clear()
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
mBinding?.run {
|
||||
categoryRv.adapter?.run {
|
||||
categoryRv.recycledViewPool.clear()
|
||||
notifyItemRangeChanged(0, itemCount, "")
|
||||
}
|
||||
|
||||
val selectedTagsSize = viewModel.selectedSubCategories.value?.size ?: 0
|
||||
updateMoreCategory(selectedTagsSize)
|
||||
|
||||
context?.let {
|
||||
ivSearchCategory.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_instance.toColor(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val SPECIAL_CATEGORY_POSITION = 1
|
||||
}
|
||||
}
|
||||
@ -76,8 +76,15 @@ class CategoryV2ListAdapter(
|
||||
ItemViewType.GAME_NORMAL -> {
|
||||
CategoryGameItemViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
else -> {
|
||||
FooterViewHolder(mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false))
|
||||
FooterViewHolder(
|
||||
mLayoutInflater.inflate(
|
||||
com.gh.gamecenter.common.R.layout.refresh_footerview,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,20 +101,12 @@ class CategoryV2ListAdapter(
|
||||
holder.bindGameItem(gameEntity)
|
||||
holder.initServerType(gameEntity)
|
||||
|
||||
val categoryTitle = mCategoryViewModel.categoryTitle
|
||||
val selectedCategoryName = mCategoryViewModel.selectedCategoryName
|
||||
val builder = StringBuilder()
|
||||
mCategoryViewModel.selectedCategoryList.run {
|
||||
forEachIndexed { index, entity ->
|
||||
builder.append(entity.name)
|
||||
if (index != size - 1) {
|
||||
builder.append("_")
|
||||
}
|
||||
}
|
||||
}
|
||||
val selectedSubCatalogName = builder.toString()
|
||||
val sortType = mViewModel.sortType.value
|
||||
val sortSize = mViewModel.sortSize.text
|
||||
val categoryTitle = mCategoryViewModel.pageName
|
||||
val selectedCategoryName = mCategoryViewModel.selectedSidebarsName
|
||||
val selectedSubCatalogName =
|
||||
mCategoryViewModel.selectedSubCategories.value?.joinToString("-") { it.category.name ?: "" }
|
||||
val sortType = mViewModel.gameFiltered.sortType.value
|
||||
val sortSize = mViewModel.gameFiltered.size.text
|
||||
|
||||
val exposureSources = ArrayList<ExposureSource>()
|
||||
if (!mViewModel.exposureSourceList.isNullOrEmpty()) {
|
||||
@ -172,8 +171,13 @@ class CategoryV2ListAdapter(
|
||||
) {
|
||||
val trackEvent = JSONObject()
|
||||
try {
|
||||
trackEvent.put("navigation_bar_name", mCategoryViewModel.selectedCategoryName)
|
||||
trackEvent.put("game_tag", mCategoryViewModel.selectedCategoryList.map { it.name })
|
||||
trackEvent.put("navigation_bar_name", mCategoryViewModel.selectedSidebarsName)
|
||||
trackEvent.put(
|
||||
"game_tag",
|
||||
(mCategoryViewModel.selectedSubCategories.value ?: listOf())
|
||||
.map {
|
||||
it.category.name
|
||||
})
|
||||
trackEvent.put("game_status", gameEntity.category)
|
||||
trackEvent.put(
|
||||
"inclusion_size",
|
||||
@ -266,18 +270,24 @@ class CategoryV2ListAdapter(
|
||||
binding.gameKaifuType.visibility = View.GONE
|
||||
binding.gameKaifuType.text = ""
|
||||
}
|
||||
|
||||
serverLabel != null -> {
|
||||
binding.gameKaifuType.visibility = View.VISIBLE
|
||||
binding.gameKaifuType.text = serverLabel.value
|
||||
if (gameEntity.isUseDefaultServerStyle()) {
|
||||
binding.gameKaifuType.background =
|
||||
com.gh.gamecenter.feature.R.drawable.server_label_default_bg.toDrawable(binding.root.context)
|
||||
binding.gameKaifuType.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(binding.root.context))
|
||||
binding.gameKaifuType.setTextColor(
|
||||
com.gh.gamecenter.common.R.color.text_secondary.toColor(
|
||||
binding.root.context
|
||||
)
|
||||
)
|
||||
} else {
|
||||
binding.gameKaifuType.background = DrawableView.getServerDrawable(serverLabel.color)
|
||||
binding.gameKaifuType.setTextColor(com.gh.gamecenter.common.R.color.white.toColor(binding.root.context))
|
||||
}
|
||||
}
|
||||
|
||||
else -> binding.gameKaifuType.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
@ -2,9 +2,10 @@ package com.gh.gamecenter.category2
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.view.CategoryFilterView
|
||||
@ -13,16 +14,17 @@ import com.gh.common.xapk.XapkUnzipStatus
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.observeNonNull
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.databinding.FragmentCategoryListBinding
|
||||
import com.gh.gamecenter.databinding.LayoutSelectedCategoryBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@ -30,13 +32,18 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>() {
|
||||
|
||||
private var mCategoryId: String = ""
|
||||
private var mSubCategoryId: String = ""
|
||||
private var mCategoryTitle: String = ""
|
||||
private var pageId: String = ""
|
||||
private var pageName: String = ""
|
||||
private val parentViewModel by viewModels<CategoryV2ViewModel>(
|
||||
ownerProducer = { parentFragment ?: this }
|
||||
)
|
||||
|
||||
override fun isAutomaticLoad(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private var mAdapter: CategoryV2ListAdapter? = null
|
||||
private var mSelectedViewList = ArrayList<View>()
|
||||
private var mBinding: FragmentCategoryListBinding? = null
|
||||
private var mCategoryViewModel: CategoryV2ViewModel? = null
|
||||
private var mLastPageDataMap: HashMap<String, String>? = null
|
||||
private val mDataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
@ -52,6 +59,12 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
private val selectedTagsAdapter by lazy {
|
||||
SelectedTagsAdapter {
|
||||
parentViewModel.removeSubCategorySelected(it.parentId, it.category.id, "游戏列表")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() =
|
||||
@ -60,8 +73,7 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
override fun provideListViewModel() =
|
||||
viewModelProvider<CategoryV2ListViewModel>(
|
||||
CategoryV2ListViewModel.Factory(
|
||||
mCategoryId,
|
||||
mSubCategoryId,
|
||||
pageId,
|
||||
arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST)
|
||||
)
|
||||
)
|
||||
@ -70,12 +82,7 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
?: CategoryV2ListAdapter(
|
||||
requireContext(),
|
||||
mListViewModel ?: provideListViewModel(),
|
||||
mCategoryViewModel ?: viewModelProviderFromParent(
|
||||
CategoryV2ViewModel.Factory(
|
||||
mCategoryId,
|
||||
mCategoryTitle
|
||||
), mCategoryId
|
||||
),
|
||||
parentViewModel,
|
||||
mEntrance,
|
||||
mLastPageDataMap
|
||||
).apply { mAdapter = this }
|
||||
@ -83,12 +90,10 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
override fun getItemDecoration() = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mCategoryId = arguments?.getString(EntranceConsts.KEY_CATEGORY_ID) ?: ""
|
||||
mSubCategoryId = arguments?.getString(EntranceConsts.KEY_SUB_CATEGORY_ID) ?: ""
|
||||
mCategoryTitle = arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: ""
|
||||
pageId = arguments?.getString(EntranceConsts.KEY_PAGE_ID) ?: ""
|
||||
pageName = arguments?.getString(EntranceConsts.KEY_PAGE_NAME) ?: ""
|
||||
mLastPageDataMap = arguments?.getSerializable(EntranceConsts.KEY_LAST_PAGE_DATA) as? HashMap<String, String>
|
||||
mCategoryViewModel =
|
||||
viewModelProviderFromParent(CategoryV2ViewModel.Factory(mCategoryId, mCategoryTitle), mCategoryId)
|
||||
|
||||
mEntrance = arguments?.getString(EntranceConsts.KEY_ENTRANCE) ?: Constants.ENTRANCE_UNKNOWN
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -104,19 +109,8 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
|
||||
initFilterView()
|
||||
|
||||
mListViewModel?.refresh?.observeNonNull(viewLifecycleOwner) { onRefresh() }
|
||||
mCategoryViewModel?.run {
|
||||
categoryPositionLiveData.observeNonNull(viewLifecycleOwner) {
|
||||
directories[it.first].data?.get(it.second)?.run {
|
||||
mBinding?.selectedCategoryContainer?.visibility = View.VISIBLE
|
||||
|
||||
if (selected) {
|
||||
addCategory(this)
|
||||
} else {
|
||||
removeCategory(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
mListViewModel?.refresh?.observeNonNull(viewLifecycleOwner) {
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
mListRv.addOnScrollListener(ExposureListener(this, provideListAdapter()))
|
||||
@ -132,19 +126,40 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
.show()
|
||||
|
||||
mBinding?.reuseNoneData?.reuseResetLoadTv?.setOnClickListener {
|
||||
mCategoryViewModel?.run {
|
||||
// 重试时,
|
||||
// 清空所有筛选条件
|
||||
with(parentViewModel) {
|
||||
logClickReset("游戏列表")
|
||||
resetDirectoryList()
|
||||
clearSelectedTag()
|
||||
// 移除大小限制
|
||||
mBinding?.filterContainer?.resetSortSize()
|
||||
val size = SubjectSettingEntity.Size()
|
||||
updateGameFiltered(size)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
with(parentViewModel) {
|
||||
gameFiltered.observe(viewLifecycleOwner) {
|
||||
mListViewModel.updateSortConfig(it)
|
||||
}
|
||||
|
||||
selectedSubCategories.observe(viewLifecycleOwner) {
|
||||
updateSelectedTags(it)
|
||||
}
|
||||
resetSortSize()
|
||||
changeCategoryTab()
|
||||
// openDirectoryLayout()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetSortSize() {
|
||||
mBinding?.filterContainer?.resetSortSize()
|
||||
mListViewModel?.sortSize = SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小")
|
||||
private fun updateSelectedTags(selectedTags: List<CategoryV2ViewModel.SelectedTags>) {
|
||||
mBinding?.run {
|
||||
flTagsContainer.goneIf(selectedTags.isEmpty()) {
|
||||
if (rvTags.adapter == null) {
|
||||
rvTags.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
|
||||
rvTags.adapter = selectedTagsAdapter
|
||||
}
|
||||
selectedTagsAdapter.submitList(selectedTags)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -154,22 +169,19 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
|
||||
setOnConfigSetupListener(object : CategoryFilterView.OnCategoryFilterSetupListener {
|
||||
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
|
||||
mListViewModel?.updateSortConfig(sortSize = sortSize)
|
||||
parentViewModel.updateGameFiltered(size = sortSize)
|
||||
}
|
||||
|
||||
override fun onSetupSortType(sortType: CategoryFilterView.SortType) {
|
||||
mListViewModel?.updateSortConfig(sortType = sortType)
|
||||
parentViewModel.updateGameFiltered(sortType = sortType)
|
||||
}
|
||||
|
||||
override fun onSetupSortCategory() {
|
||||
openDirectoryLayout()
|
||||
override fun getPopHeight(): Int {
|
||||
return (mBinding?.root?.height ?: 0) - (mBinding?.flTagsContainer?.height ?: 0)
|
||||
}
|
||||
})
|
||||
|
||||
setOnFilterClickListener(object : CategoryFilterView.OnFilterClickListener {
|
||||
override fun onCategoryClick() {
|
||||
removeGuide()
|
||||
}
|
||||
|
||||
override fun onTypeClick() {
|
||||
removeGuide()
|
||||
@ -183,101 +195,6 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSubCategoryId(id: String) {
|
||||
mSubCategoryId = id
|
||||
}
|
||||
|
||||
fun changeCategoryTab(categoryId: String? = null) {
|
||||
mSelectedViewList.clear()
|
||||
mBinding?.selectedCategoryContainer?.run {
|
||||
removeAllViews()
|
||||
visibility = View.GONE
|
||||
}
|
||||
if (categoryId != null) mSubCategoryId = categoryId
|
||||
mListViewModel?.updateSortConfig(categoryIds = mSubCategoryId)
|
||||
}
|
||||
|
||||
private fun addCategory(entity: CategoryEntity) {
|
||||
addCategoryView(entity)
|
||||
mCategoryViewModel?.selectedCategoryList?.add(entity)
|
||||
updateCategoryGame()
|
||||
}
|
||||
|
||||
private fun removeCategory(entity: CategoryEntity) {
|
||||
mCategoryViewModel?.selectedCategoryList?.run {
|
||||
if (isEmpty()) return
|
||||
|
||||
removeCategoryView(entity.name ?: "")
|
||||
remove(entity)
|
||||
if (size == 0) {
|
||||
mBinding?.selectedCategoryContainer?.visibility = View.GONE
|
||||
mListViewModel?.updateSortConfig(categoryIds = mSubCategoryId)
|
||||
} else {
|
||||
updateCategoryGame()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCategoryView(entity: CategoryEntity) {
|
||||
val binding = LayoutSelectedCategoryBinding.inflate(layoutInflater).apply {
|
||||
val params =
|
||||
FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
params.setMargins(0, 8F.dip2px(), 8F.dip2px(), 0)
|
||||
params.height = 24F.dip2px()
|
||||
root.layoutParams = params
|
||||
|
||||
name.text = entity.name
|
||||
root.setOnClickListener {
|
||||
removeCategoryAndNotify(entity)
|
||||
}
|
||||
}
|
||||
|
||||
mSelectedViewList.add(binding.root)
|
||||
mBinding?.selectedCategoryContainer?.addView(binding.root)
|
||||
}
|
||||
|
||||
private fun removeCategoryView(categoryName: String) {
|
||||
mCategoryViewModel?.selectedCategoryList?.run {
|
||||
var i = 0
|
||||
forEachIndexed { index, categoryEntity ->
|
||||
if (categoryName == categoryEntity.name) {
|
||||
i = index
|
||||
}
|
||||
}
|
||||
|
||||
if (i < mSelectedViewList.size) {
|
||||
mBinding?.selectedCategoryContainer?.removeView(mSelectedViewList[i])
|
||||
mSelectedViewList.removeAt(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCategoryAndNotify(entity: CategoryEntity) {
|
||||
removeCategory(entity)
|
||||
entity.selected = false
|
||||
mCategoryViewModel?.run {
|
||||
if (selectedCount > 0) {
|
||||
selectedCount--
|
||||
postSelectedCount()
|
||||
postCategoryDirectoryList()
|
||||
logClickClassificationDelete(entity.primaryIndex, entity.name ?: "", "游戏列表")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCategoryGame() {
|
||||
mCategoryViewModel?.selectedCategoryList?.run {
|
||||
val categoryIds = StringBuilder()
|
||||
forEachIndexed { index, s ->
|
||||
categoryIds.append(s.id)
|
||||
if (index != size - 1) {
|
||||
categoryIds.append("-")
|
||||
}
|
||||
}
|
||||
mListViewModel?.updateSortConfig(categoryIds = categoryIds.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeGuide() {
|
||||
(parentFragment as? CategoryV2Fragment)?.removeGuide()
|
||||
}
|
||||
@ -328,10 +245,6 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
fun openDirectoryLayout() {
|
||||
(parentFragment as? CategoryV2Fragment)?.openDirectoryLayout()
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mBinding?.filterContainer?.run {
|
||||
|
||||
@ -4,14 +4,13 @@ import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.gamecenter.common.entity.ExposureEntity
|
||||
import com.gh.common.exposure.ExposureUtils
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.common.view.CategoryFilterView
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.entity.ExposureEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Observable
|
||||
@ -20,14 +19,13 @@ import io.reactivex.Single
|
||||
class CategoryV2ListViewModel(
|
||||
application: Application,
|
||||
val categoryId: String,
|
||||
var categoryIds: String,
|
||||
var exposureSourceList: List<ExposureSource>?
|
||||
) : ListViewModel<GameEntity, GameEntity>(application) {
|
||||
|
||||
val refresh = MutableLiveData<Boolean>()
|
||||
|
||||
var sortType = CategoryFilterView.SortType.RECOMMENDED
|
||||
var sortSize = SubjectSettingEntity.Size()
|
||||
var gameFiltered = CategoryV2ViewModel.GameFiltered()
|
||||
private set
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? = null
|
||||
|
||||
@ -51,52 +49,36 @@ class CategoryV2ListViewModel(
|
||||
}
|
||||
|
||||
fun updateSortConfig(
|
||||
sortSize: SubjectSettingEntity.Size? = null,
|
||||
sortType: CategoryFilterView.SortType? = null,
|
||||
categoryIds: String? = null
|
||||
gameFiltered: CategoryV2ViewModel.GameFiltered
|
||||
) {
|
||||
when {
|
||||
sortSize != null && sortSize != this.sortSize -> {
|
||||
this.sortSize = sortSize
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
sortType != null && sortType != this.sortType -> {
|
||||
this.sortType = sortType
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
categoryIds != null && categoryIds != this.categoryIds -> {
|
||||
this.categoryIds = categoryIds
|
||||
refresh.postValue(true)
|
||||
}
|
||||
}
|
||||
this.gameFiltered = gameFiltered
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
private fun getFilter(): String? {
|
||||
|
||||
return UrlFilterUtils.getFilterQuery(
|
||||
"category_ids", categoryIds,
|
||||
"min_size", sortSize.min.toString(),
|
||||
"max_size", sortSize.max.toString()
|
||||
"category_ids", gameFiltered.categoryIds.ifBlank { gameFiltered.sidebarCategoryId },
|
||||
"min_size", gameFiltered.size.min.toString(),
|
||||
"max_size", gameFiltered.size.max.toString()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private fun getSortType(): String? {
|
||||
return when (sortType) {
|
||||
return when (gameFiltered.sortType) {
|
||||
CategoryFilterView.SortType.RECOMMENDED -> "download:-1"
|
||||
CategoryFilterView.SortType.NEWEST -> "publish:-1"
|
||||
CategoryFilterView.SortType.RATING -> "star:-1"
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(val categoryId: String, val categoryIds: String, val exposureSourceList: List<ExposureSource>?) :
|
||||
class Factory(val categoryId: String, val exposureSourceList: List<ExposureSource>?) :
|
||||
ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CategoryV2ListViewModel(
|
||||
HaloApp.getInstance().application,
|
||||
categoryId,
|
||||
categoryIds,
|
||||
exposureSourceList
|
||||
) as T
|
||||
}
|
||||
|
||||
@ -1,160 +1,214 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.common.view.CategoryFilterView
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.entity.SidebarsEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
|
||||
class CategoryV2ViewModel(
|
||||
application: Application,
|
||||
private val mCategoryId: String,
|
||||
val categoryTitle: String
|
||||
) : AndroidViewModel(application) {
|
||||
class CategoryV2ViewModel : ViewModel() {
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
var sidebarsLiveData = MutableLiveData<SidebarsEntity>()
|
||||
var directories = ArrayList<CategoryEntity>()
|
||||
var sidebarsLiveData = MutableLiveData<SidebarsEntity?>()
|
||||
var directoriesLiveData = MutableLiveData<List<CategoryEntity>>()
|
||||
var selectedCount = 0
|
||||
var selectedCountLiveData = MutableLiveData<Int>()
|
||||
var categoryPositionLiveData = MutableLiveData<Pair<Int, Int>>()
|
||||
|
||||
var selectedCategoryName: String = ""
|
||||
var selectedCategoryPosition: Int = 0
|
||||
var selectedCategoryList = ArrayList<CategoryEntity>()
|
||||
var entrance: String = ""
|
||||
private var pageId = ""
|
||||
var pageName = ""
|
||||
private set
|
||||
|
||||
init {
|
||||
getSidebars()
|
||||
getCategoryDirectories()
|
||||
fun init(entrance: String) {
|
||||
this.entrance = entrance
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getSidebars() {
|
||||
api.getSidebars(mCategoryId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<SidebarsEntity>() {
|
||||
override fun onSuccess(data: SidebarsEntity) {
|
||||
sidebarsLiveData.postValue(data)
|
||||
fun loadData(pageId: String, pageName: String) {
|
||||
this.pageId = pageId
|
||||
this.pageName = pageName
|
||||
|
||||
val sidebarsObservable = api.getSidebars(pageId)
|
||||
val directoriesObservable = api.getCategoryDirectories(pageId)
|
||||
.onErrorReturnItem(listOf())
|
||||
sidebarsObservable.zipWith(directoriesObservable) { t1, t2 ->
|
||||
t1 to t2
|
||||
}.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<Pair<SidebarsEntity, List<CategoryEntity>>>() {
|
||||
override fun onSuccess(data: Pair<SidebarsEntity, List<CategoryEntity>>) {
|
||||
val (sidebarsData, directories) = data
|
||||
directoriesLiveData.value = directories
|
||||
sidebarsLiveData.value = sidebarsData
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
sidebarsLiveData.postValue(null)
|
||||
}
|
||||
})
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getCategoryDirectories() {
|
||||
api.getCategoryDirectories(mCategoryId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<List<CategoryEntity>>() {
|
||||
override fun onSuccess(data: List<CategoryEntity>) {
|
||||
directories = ArrayList(data)
|
||||
postCategoryDirectoryList()
|
||||
fun clearSelectedTag() {
|
||||
logClickReset("全部类别")
|
||||
val filteredList = _selectedSubCategories.value
|
||||
if (!filteredList.isNullOrEmpty()) {
|
||||
_selectedSubCategories.value = mutableListOf()
|
||||
|
||||
val directories = directoriesLiveData.value ?: return
|
||||
directories.forEach {
|
||||
it.data?.forEach { child ->
|
||||
child.selected = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun postSelectedCount() {
|
||||
selectedCountLiveData.postValue(selectedCount)
|
||||
}
|
||||
|
||||
fun postCategoryPosition(primaryIndex: Int, subIndex: Int) {
|
||||
categoryPositionLiveData.postValue(Pair(primaryIndex, subIndex))
|
||||
}
|
||||
|
||||
fun postCategoryDirectoryList() {
|
||||
directoriesLiveData.postValue(directories)
|
||||
}
|
||||
|
||||
fun resetDirectoryList() {
|
||||
selectedCount = 0
|
||||
selectedCategoryList.clear()
|
||||
directories.forEach {
|
||||
it.data?.forEach { entity ->
|
||||
entity.selected = false
|
||||
}
|
||||
directoriesLiveData.value = directories
|
||||
}
|
||||
postSelectedCount()
|
||||
postCategoryDirectoryList()
|
||||
}
|
||||
|
||||
fun logAppearance() {
|
||||
LogUtils.logCategoryV2AppearanceEvent(entrance, categoryTitle)
|
||||
LogUtils.logCategoryV2AppearanceEvent(entrance, pageName)
|
||||
}
|
||||
|
||||
fun logClickSide() {
|
||||
LogUtils.logCategoryV2ClickSideEvent(entrance, categoryTitle, selectedCategoryName, selectedCategoryPosition)
|
||||
val sidebars = sidebarsLiveData.value?.sidebars
|
||||
val selectedPosition = selectedSidebarsPosition.value ?: 0
|
||||
val selectedCategoryName = sidebars?.getOrNull(selectedPosition)?.name ?: ""
|
||||
LogUtils.logCategoryV2ClickSideEvent(entrance, pageName, selectedCategoryName, selectedPosition)
|
||||
}
|
||||
|
||||
fun logClickClassification(primaryIndex: Int, categoryName: String, position: Int) {
|
||||
private fun logClickClassification(selected: SelectedTags) {
|
||||
LogUtils.logCategoryV2ClickClassificationEvent(
|
||||
entrance,
|
||||
categoryTitle,
|
||||
selectedCategoryName,
|
||||
directories[primaryIndex].name,
|
||||
categoryName,
|
||||
primaryIndex,
|
||||
position
|
||||
pageName,
|
||||
selectedSidebarsName,
|
||||
selected.parentName,
|
||||
selected.category.name ?: "",
|
||||
selected.parentPosition,
|
||||
selected.position
|
||||
)
|
||||
}
|
||||
|
||||
fun logClickClassificationDelete(primaryIndex: Int, categoryName: String, location: String) {
|
||||
private fun logClickClassificationDelete(directoryName: String, categoryName: String, location: String) {
|
||||
LogUtils.logCategoryV2ClickClassificationDeleteEvent(
|
||||
entrance,
|
||||
categoryTitle,
|
||||
directories[primaryIndex].name,
|
||||
pageName,
|
||||
directoryName,
|
||||
categoryName,
|
||||
location
|
||||
)
|
||||
}
|
||||
|
||||
fun logClickDetermine() {
|
||||
val categoryName = StringBuilder()
|
||||
selectedCategoryList.forEachIndexed { index, s ->
|
||||
categoryName.append(s.name)
|
||||
if (index != selectedCategoryList.size - 1) {
|
||||
categoryName.append("+")
|
||||
}
|
||||
}
|
||||
LogUtils.logCategoryV2ClickDetermineEvent(entrance, categoryTitle, categoryName.toString())
|
||||
val categoryNames = selectedSubCategories.value?.joinToString("+") { it.category.name ?: "" }
|
||||
LogUtils.logCategoryV2ClickDetermineEvent(entrance, pageName, categoryNames ?: "")
|
||||
}
|
||||
|
||||
fun logClickReset(location: String) {
|
||||
val categoryName = StringBuilder()
|
||||
selectedCategoryList.forEachIndexed { index, s ->
|
||||
categoryName.append(s.name)
|
||||
if (index != selectedCategoryList.size - 1) {
|
||||
categoryName.append("+")
|
||||
}
|
||||
}
|
||||
LogUtils.logCategoryV2ClickResetEvent(entrance, categoryTitle, categoryName.toString(), location)
|
||||
val categoryName = selectedSubCategories.value?.joinToString("+") { it.category.name ?: "" }
|
||||
LogUtils.logCategoryV2ClickResetEvent(entrance, pageName, categoryName ?: "", location)
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val categoryId: String,
|
||||
private val categoryTitle: String
|
||||
) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CategoryV2ViewModel(
|
||||
HaloApp.getInstance().application,
|
||||
categoryId,
|
||||
categoryTitle
|
||||
) as T
|
||||
private val _notifySubCategorySelected = MutableLiveData<Event<String>>()
|
||||
val notifySubCategorySelected: LiveData<Event<String>> = _notifySubCategorySelected
|
||||
|
||||
private val _selectedSubCategories = MutableLiveData<List<SelectedTags>>()
|
||||
val selectedSubCategories: LiveData<List<SelectedTags>> = _selectedSubCategories
|
||||
fun addSubCategorySelected(selected: SelectedTags) {
|
||||
val list = _selectedSubCategories.value
|
||||
val newData = if (list == null) {
|
||||
mutableListOf(selected)
|
||||
} else {
|
||||
list + selected
|
||||
}
|
||||
selected.category.selected = true
|
||||
_selectedSubCategories.value = newData
|
||||
|
||||
// 当搜索条件发生变化时,侧边栏默认选中 “全部”
|
||||
selectSidebarsPosition(0, false)
|
||||
|
||||
updateGameFiltered()
|
||||
_notifySubCategorySelected.value = Event(selected.parentId)
|
||||
logClickClassification(selected)
|
||||
}
|
||||
|
||||
fun removeSubCategorySelected(parentId: String, categoryId: String, location: String) {
|
||||
val list = _selectedSubCategories.value ?: return
|
||||
val position = list.indexOfFirst {
|
||||
it.parentId == parentId && categoryId == it.category.id
|
||||
}
|
||||
if (position != -1) {
|
||||
val item = list[position]
|
||||
|
||||
item.category.selected = false
|
||||
_selectedSubCategories.value = list - item
|
||||
|
||||
updateGameFiltered()
|
||||
_notifySubCategorySelected.value = Event(parentId)
|
||||
|
||||
logClickClassificationDelete(item.parentName, item.category.name ?: "", location)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val _selectedSidebarsPosition = MutableLiveData<Int>()
|
||||
val selectedSidebarsPosition: LiveData<Int> = _selectedSidebarsPosition
|
||||
val selectedSidebarsName: String
|
||||
get() = sidebarsLiveData.value?.sidebars?.getOrNull(selectedSidebarsPosition.value ?: 0)?.name ?: ""
|
||||
|
||||
fun selectSidebarsPosition(position: Int, triggerSearch: Boolean = true) {
|
||||
val oldPosition = _selectedSidebarsPosition.value ?: INVALID_POSITION
|
||||
if (position != oldPosition) {
|
||||
_selectedSidebarsPosition.value = position
|
||||
// 如果是点击搜索而被动切换到 “全部” tab,则这里不需要更新筛选条件
|
||||
if (triggerSearch && position != 1) {
|
||||
updateGameFiltered()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val _gameFiltered = MutableLiveData<GameFiltered>()
|
||||
val gameFiltered: LiveData<GameFiltered> = _gameFiltered
|
||||
fun updateGameFiltered(
|
||||
size: SubjectSettingEntity.Size? = null,
|
||||
sortType: CategoryFilterView.SortType? = null
|
||||
) {
|
||||
val oldFiltered = _gameFiltered.value
|
||||
val newSize = size ?: oldFiltered?.size ?: SubjectSettingEntity.Size()
|
||||
val newSortType = sortType ?: oldFiltered?.sortType ?: CategoryFilterView.SortType.RECOMMENDED
|
||||
val selectedSidebarPosition = selectedSidebarsPosition.value ?: 0
|
||||
val categoryIds = selectedSubCategories.value?.joinToString("-") { it.category.id } ?: ""
|
||||
val sidebarCategoryId =
|
||||
sidebarsLiveData.value?.sidebars?.getOrNull(selectedSidebarPosition)?.categoryId ?: "all"
|
||||
_gameFiltered.value = GameFiltered(newSize, newSortType, categoryIds, sidebarCategoryId)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INVALID_POSITION = -1
|
||||
}
|
||||
|
||||
data class SelectedTags(
|
||||
val parentId: String,
|
||||
val parentName: String,
|
||||
val parentPosition: Int,
|
||||
val category: CategoryEntity,
|
||||
val position: Int
|
||||
)
|
||||
|
||||
data class GameFiltered(
|
||||
val size: SubjectSettingEntity.Size = SubjectSettingEntity.Size(),
|
||||
val sortType: CategoryFilterView.SortType = CategoryFilterView.SortType.RECOMMENDED,
|
||||
val categoryIds: String = "",
|
||||
val sidebarCategoryId: String = "全部"
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,292 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.blankj.utilcode.util.KeyboardUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.common.utils.toResString
|
||||
import com.gh.gamecenter.common.view.BugFixedPopupWindow
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.PopSearchCategoryBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||
|
||||
class SearchCategoryPop(
|
||||
height: Int,
|
||||
private val isAutoRequestFocus: Boolean,
|
||||
private val pageId: String,
|
||||
private val pageName: String,
|
||||
private val binding: PopSearchCategoryBinding
|
||||
) : BugFixedPopupWindow(binding.root, ViewGroup.LayoutParams.MATCH_PARENT, height) {
|
||||
|
||||
private var listener: OnSearchCategoryListener? = null
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private var searchDataList: List<CategoryV2ViewModel.SelectedTags>? = null
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private var isSearching = false
|
||||
|
||||
private val adapter by lazy {
|
||||
CategoryDirectoryAdapter(object : OnSearchCategoryListener {
|
||||
override fun isEnableSelected(): Boolean {
|
||||
return listener?.isEnableSelected() ?: false
|
||||
}
|
||||
|
||||
override fun onItemSelected(selected: CategoryV2ViewModel.SelectedTags) {
|
||||
listener?.onItemSelected(selected)
|
||||
|
||||
}
|
||||
|
||||
override fun onItemRemoved(parentId: String, subCategoryId: String) {
|
||||
listener?.onItemRemoved(parentId, subCategoryId)
|
||||
}
|
||||
|
||||
override fun onResetSelected() {
|
||||
listener?.onResetSelected()
|
||||
}
|
||||
|
||||
override fun onSubmit() {
|
||||
listener?.onSubmit()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private val resultAdapter by lazy {
|
||||
SearchCategoryResultsAdapter {
|
||||
SensorsBridge.logClassificationSearchReturnClick(
|
||||
pageId,
|
||||
pageName,
|
||||
binding.searchView.searchKey,
|
||||
it.category.name ?: ""
|
||||
)
|
||||
if (listener?.isEnableSelected() == true) {
|
||||
clearSearchKey()
|
||||
listener?.onItemSelected(it)
|
||||
} else {
|
||||
ToastUtils.toast(R.string.selected_category_tags_max_toast.toResString())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private val selectedTagAdapter by lazy {
|
||||
SelectedTagsAdapter {
|
||||
listener?.onItemRemoved(it.parentId, it.category.id)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
isOutsideTouchable = true
|
||||
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
isFocusable = true
|
||||
inputMethodMode = INPUT_METHOD_NEEDED
|
||||
softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
|
||||
|
||||
initView()
|
||||
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.rvCategory.layoutManager = LinearLayoutManager(binding.root.context)
|
||||
binding.rvCategory.adapter = adapter
|
||||
|
||||
binding.tvSelectedNumber.typeface =
|
||||
Typeface.createFromAsset(binding.root.context.assets, Constants.DIN_FONT_PATH)
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.clContent.setOnClickListener {
|
||||
// 不需要具体实现,只是为了拦截 root 的点击事件
|
||||
}
|
||||
|
||||
binding.tvReset.setOnClickListener {
|
||||
listener?.onResetSelected()
|
||||
}
|
||||
|
||||
binding.vSubmit.setOnClickListener {
|
||||
dismiss()
|
||||
listener?.onSubmit()
|
||||
}
|
||||
|
||||
binding.searchView.addTextChangedListener {
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
val key = it?.toString() ?: ""
|
||||
if (key.isEmpty()) {
|
||||
changeToSearching(false)
|
||||
} else {
|
||||
handler.postDelayed({
|
||||
search(it?.toString() ?: "")
|
||||
}, SEARCH_DELAY_DURATION)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAutoRequestFocus) {
|
||||
binding.searchView.setEditTextOnFocusChangeListener { _, hasFocus ->
|
||||
if (hasFocus) {
|
||||
SensorsBridge.logClassificationSearch(pageId, pageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun setData(data: List<CategoryEntity>) {
|
||||
searchDataList = data
|
||||
.asSequence()
|
||||
.mapIndexed { index, parent ->
|
||||
parent.data?.mapIndexed { childIndex, child ->
|
||||
CategoryV2ViewModel.SelectedTags(parent.id, parent.name ?: "", index, child, childIndex)
|
||||
} ?: listOf()
|
||||
}
|
||||
.flatten()
|
||||
.toList()
|
||||
|
||||
adapter.setListData(data)
|
||||
}
|
||||
|
||||
fun updateCategorySelected(selectedList: List<CategoryV2ViewModel.SelectedTags>?) {
|
||||
val size = selectedList?.size ?: 0
|
||||
binding.tvSelectedNumber.goneIf(size == 0) {
|
||||
binding.tvSelectedNumber.text = "$size"
|
||||
}
|
||||
|
||||
if (binding.rvSelected.adapter == null) {
|
||||
binding.rvSelected.layoutManager =
|
||||
LinearLayoutManager(binding.root.context, RecyclerView.HORIZONTAL, false)
|
||||
binding.rvSelected.adapter = selectedTagAdapter
|
||||
}
|
||||
selectedTagAdapter.submitList(selectedList)
|
||||
binding.rvSelected.goneIf(selectedList.isNullOrEmpty())
|
||||
|
||||
if (isSearching) {
|
||||
search(binding.searchView.searchKey)
|
||||
}
|
||||
}
|
||||
|
||||
private fun search(key: String) {
|
||||
Single.create {
|
||||
val data = searchDataList?.filterNot { item -> item.category.selected } ?: emptyList()
|
||||
val resultList = FuzzySearch.extractSorted(key, data, { item -> item.category.name ?: "" }, 1)
|
||||
.map { item -> item.referent }
|
||||
it.onSuccess(resultList)
|
||||
}.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<CategoryV2ViewModel.SelectedTags>>() {
|
||||
override fun onSuccess(data: List<CategoryV2ViewModel.SelectedTags>) {
|
||||
val hasResult = data.isNotEmpty()
|
||||
SensorsBridge.logClassificationSearchReturn(pageId, pageName, key, hasResult)
|
||||
changeToSearching(true, key, data)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
SensorsBridge.logClassificationSearchReturn(pageId, pageName, key, false)
|
||||
changeToSearching(true, key)
|
||||
}
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun clearSearchKey() {
|
||||
binding.searchView.clear()
|
||||
}
|
||||
|
||||
private fun changeToSearching(
|
||||
isSearching: Boolean,
|
||||
key: String = "",
|
||||
results: List<CategoryV2ViewModel.SelectedTags>? = null
|
||||
) {
|
||||
this.isSearching = isSearching
|
||||
binding.rvCategory.goneIf(isSearching)
|
||||
binding.rvResults.goneIf(results.isNullOrEmpty()) {
|
||||
if (binding.rvResults.adapter == null) {
|
||||
binding.rvResults.layoutManager = LinearLayoutManager(binding.rvResults.context)
|
||||
binding.rvResults.adapter = resultAdapter
|
||||
}
|
||||
}
|
||||
resultAdapter.setData(results ?: listOf(), key)
|
||||
binding.reuseNoConnection.root.goneIf(!isSearching || !results.isNullOrEmpty()) {
|
||||
binding.reuseNoConnection.reuseNoneDataTv.text = R.string.no_relevant_content_found.toResString()
|
||||
binding.reuseNoConnection.reuseNoneDataDescTv.goneIf(false)
|
||||
binding.reuseNoConnection.reuseNoneDataDescTv.text = R.string.try_a_different_search_term.toResString()
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnSearchCategoryListener(listener: OnSearchCategoryListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
fun notifyItemSelectedChanged(parentId: String) {
|
||||
adapter.notifyItemSelectedChanged(parentId)
|
||||
}
|
||||
|
||||
override fun showAtLocation(parent: View?, gravity: Int, x: Int, y: Int) {
|
||||
super.showAtLocation(parent, gravity, x, y)
|
||||
if (isAutoRequestFocus) {
|
||||
binding.searchView.requestFocus()
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
// 在某些机型上延时一下才能弹起软键盘
|
||||
handler.postDelayed({
|
||||
KeyboardUtils.showSoftInput()
|
||||
}, SHOW_SOFT_INPUT_DELAY)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
super.dismiss()
|
||||
clearSearchKey()
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private const val SEARCH_DELAY_DURATION = 300L
|
||||
private const val SHOW_SOFT_INPUT_DELAY = 200L
|
||||
|
||||
fun newInstance(
|
||||
context: Context,
|
||||
height: Int,
|
||||
isAutoRequestFocus: Boolean,
|
||||
pageId: String,
|
||||
pageName: String
|
||||
): SearchCategoryPop {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = PopSearchCategoryBinding.inflate(inflater)
|
||||
return SearchCategoryPop(height, isAutoRequestFocus, pageId, pageName, binding)
|
||||
}
|
||||
}
|
||||
|
||||
interface OnSearchCategoryListener {
|
||||
|
||||
fun isEnableSelected(): Boolean
|
||||
|
||||
fun onItemSelected(selected: CategoryV2ViewModel.SelectedTags)
|
||||
|
||||
fun onItemRemoved(parentId: String, subCategoryId: String)
|
||||
|
||||
fun onResetSelected()
|
||||
|
||||
fun onSubmit()
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.RecyclerSearchCategoryResultBinding
|
||||
|
||||
class SearchCategoryResultsAdapter(val clickListener: (CategoryV2ViewModel.SelectedTags) -> Unit) :
|
||||
RecyclerView.Adapter<SearchCategoryResultsAdapter.ResultViewHolder>() {
|
||||
private val dataList = arrayListOf<CategoryV2ViewModel.SelectedTags>()
|
||||
private var key = ""
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setData(data: List<CategoryV2ViewModel.SelectedTags>, newKey: String) {
|
||||
key = newKey
|
||||
dataList.clear()
|
||||
dataList.addAll(data)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ResultViewHolder {
|
||||
return ResultViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun getItemCount() = dataList.size
|
||||
|
||||
override fun onBindViewHolder(holder: ResultViewHolder, position: Int) {
|
||||
val item = dataList[position]
|
||||
val text = item.category.name ?: ""
|
||||
val spannableString = SpannableString(text)
|
||||
val highlightColor = com.gh.gamecenter.common.R.color.text_theme.toColor(holder.itemView.context)
|
||||
text.forEachIndexed { index, char ->
|
||||
if (key.contains(char)) {
|
||||
// 需要高亮
|
||||
spannableString.setSpan(
|
||||
ForegroundColorSpan(highlightColor),
|
||||
index,
|
||||
index + 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
holder.binding.root.text = spannableString
|
||||
holder.itemView.setOnClickListener {
|
||||
clickListener(item)
|
||||
}
|
||||
}
|
||||
|
||||
class ResultViewHolder(val binding: RecyclerSearchCategoryResultBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.databinding.RecyclerCategorySelectedTagBinding
|
||||
|
||||
class SelectedTagsAdapter(val click: (CategoryV2ViewModel.SelectedTags) -> Unit) :
|
||||
ListAdapter<CategoryV2ViewModel.SelectedTags, SelectedTagsAdapter.TagsViewHolder>(
|
||||
createDiffUtil()
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagsViewHolder {
|
||||
return TagsViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TagsViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.binding.root.setText(item.category.name ?: "")
|
||||
holder.itemView.setOnClickListener {
|
||||
click(item)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun createDiffUtil() = object : DiffUtil.ItemCallback<CategoryV2ViewModel.SelectedTags>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: CategoryV2ViewModel.SelectedTags,
|
||||
newItem: CategoryV2ViewModel.SelectedTags
|
||||
): Boolean {
|
||||
return oldItem.category.id == newItem.category.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: CategoryV2ViewModel.SelectedTags,
|
||||
newItem: CategoryV2ViewModel.SelectedTags
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class TagsViewHolder(val binding: RecyclerCategorySelectedTagBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
@ -1,84 +1,94 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.SubCategoryItemBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class SubCategoryAdapter(
|
||||
context: Context,
|
||||
private val mViewModel: CategoryV2ViewModel,
|
||||
private val mList: List<CategoryEntity>,
|
||||
private val mPrimaryIndex: Int
|
||||
) : BaseRecyclerAdapter<SubCategoryAdapter.SubCategoryItemViewHolder>(context) {
|
||||
private val listener: SearchCategoryPop.OnSearchCategoryListener,
|
||||
) : RecyclerView.Adapter<SubCategoryAdapter.SubCategoryItemViewHolder>() {
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
private lateinit var itemData: CategoryEntity
|
||||
private val data: List<CategoryEntity>
|
||||
get() = itemData.data ?: emptyList()
|
||||
|
||||
private var directoryPosition = 0
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setData(directoryPosition: Int, newItem: CategoryEntity) {
|
||||
this.directoryPosition = directoryPosition
|
||||
itemData = newItem
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = data.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
SubCategoryItemViewHolder(SubCategoryItemBinding.inflate(mLayoutInflater))
|
||||
SubCategoryItemViewHolder(parent.toBinding())
|
||||
|
||||
override fun onBindViewHolder(holder: SubCategoryItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
if (payloads.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
} else {
|
||||
val item = data[position]
|
||||
updateSelectedState(item.selected, holder.binding)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SubCategoryItemViewHolder, position: Int) {
|
||||
holder.binding.run {
|
||||
val categoryEntity = mList[position]
|
||||
name.text = categoryEntity.name
|
||||
val categoryEntity = data[position]
|
||||
tvName.text = categoryEntity.name
|
||||
recommendIv.goneIf(categoryEntity.recommend == false)
|
||||
|
||||
if (categoryEntity.selected) {
|
||||
selectedIv.visibility = View.VISIBLE
|
||||
container.background = R.drawable.bg_category_selected.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext))
|
||||
} else {
|
||||
selectedIv.visibility = View.GONE
|
||||
container.background = com.gh.gamecenter.common.R.drawable.bg_shape_space_radius_8.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
}
|
||||
updateSelectedState(categoryEntity.selected, this)
|
||||
|
||||
root.setOnClickListener {
|
||||
when {
|
||||
mViewModel.selectedCount >= 5 && !categoryEntity.selected -> ToastUtils.toast("最多只能选择5个类别")
|
||||
!categoryEntity.selected && !listener.isEnableSelected() -> {
|
||||
ToastUtils.toast(R.string.selected_category_tags_max_toast.toResString())
|
||||
}
|
||||
|
||||
categoryEntity.selected -> {
|
||||
categoryEntity.selected = false
|
||||
selectedIv.visibility = View.GONE
|
||||
container.background = com.gh.gamecenter.common.R.drawable.bg_shape_space_radius_8.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
mViewModel.run {
|
||||
if (selectedCount > 0) {
|
||||
selectedCount--
|
||||
postSelectedCount()
|
||||
postCategoryPosition(mPrimaryIndex, position)
|
||||
logClickClassificationDelete(mPrimaryIndex, categoryEntity.name ?: "", "全部类别")
|
||||
}
|
||||
}
|
||||
|
||||
listener.onItemRemoved(itemData.id, categoryEntity.id)
|
||||
}
|
||||
|
||||
!categoryEntity.selected -> {
|
||||
categoryEntity.selected = true
|
||||
categoryEntity.primaryIndex = mPrimaryIndex
|
||||
selectedIv.visibility = View.VISIBLE
|
||||
container.background = R.drawable.bg_category_selected.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext))
|
||||
mViewModel.run {
|
||||
if (selectedCount < 5) {
|
||||
selectedCount++
|
||||
logClickClassification(mPrimaryIndex, categoryEntity.name ?: "", position)
|
||||
postSelectedCount()
|
||||
postCategoryPosition(mPrimaryIndex, position)
|
||||
}
|
||||
}
|
||||
|
||||
listener.onItemSelected(
|
||||
CategoryV2ViewModel.SelectedTags(
|
||||
itemData.id,
|
||||
itemData.name ?: "",
|
||||
directoryPosition,
|
||||
categoryEntity,
|
||||
position
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SubCategoryItemViewHolder(val binding: SubCategoryItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
private fun updateSelectedState(isSelected: Boolean, binding: SubCategoryItemBinding) {
|
||||
with(binding) {
|
||||
val context = root.context
|
||||
if (isSelected) {
|
||||
container.background = R.drawable.bg_category_selected.toDrawable(context)
|
||||
tvName.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
} else {
|
||||
container.background = com.gh.gamecenter.common.R.drawable.bg_shape_space_radius_8.toDrawable(context)
|
||||
tvName.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SubCategoryItemViewHolder(val binding: SubCategoryItemBinding) : ViewHolder(binding.root)
|
||||
}
|
||||
@ -2,16 +2,22 @@ package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class CategoryEntity(
|
||||
@SerializedName("_id")
|
||||
var id: String? = "",
|
||||
private var _id: String? = null,
|
||||
var icon: String? = "",
|
||||
var name: String? = "",
|
||||
var recommend: Boolean? = false,
|
||||
var data: List<CategoryEntity>? = null,
|
||||
var selected: Boolean = false,
|
||||
var primaryIndex: Int = -1
|
||||
) : Parcelable
|
||||
var data: List<CategoryEntity>? = null
|
||||
) : Parcelable {
|
||||
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
@IgnoredOnParcel
|
||||
var selected: Boolean = false
|
||||
}
|
||||
Reference in New Issue
Block a user