Compare commits

...

7 Commits

123 changed files with 4203 additions and 1579 deletions

View File

@ -539,6 +539,8 @@ dependencies {
if(!gradle.ext.excludeOptionalModules || gradle.ext.enableWechatPay){
implementation(project(":feature:wechat_pay"))
}
implementation "me.xdrop:fuzzywuzzy:${fuzzyVersion}"
}
File propFile = file('sign.properties')

View File

@ -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,

View File

@ -147,6 +147,15 @@ object ViewPagerFragmentHelper {
bundle.putString(EntranceConsts.KEY_QUESTIONS_ID, linkEntity.link)
NewQuestionDetailFragment().with(bundle)
}
// 专题合集详情页
TYPE_COLUMN_COLLECTION -> {
bundle.putString(EntranceConsts.KEY_COLLECTION_ID, linkEntity.link)
bundle.putInt(EntranceConsts.KEY_POSITION, 0)
bundle.putString(EntranceConsts.KEY_COLUMNNAME, linkEntity.text)
bundle.putBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION, true)
bundle.putString(EntranceConsts.KEY_SUBJECT_TYPE, "tab")
ColumnCollectionDetailFragment().with(bundle)
}
// 其他原来带Toolbar的Fragment
else -> createToolbarWrapperFragment(bundle, linkEntity, isTabWrapper)
}
@ -176,15 +185,6 @@ object ViewPagerFragmentHelper {
bundle.putString(EntranceConsts.KEY_SUBJECT_TYPE, "detail")
bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper)
}
// 专题合集详情页
TYPE_COLUMN_COLLECTION -> {
className = ColumnCollectionDetailFragment::class.java.name
bundle.putString(EntranceConsts.KEY_COLLECTION_ID, entity.link)
bundle.putInt(EntranceConsts.KEY_POSITION, 0)
bundle.putString(EntranceConsts.KEY_COLUMNNAME, entity.text)
bundle.putBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION, true)
bundle.putString(EntranceConsts.KEY_SUBJECT_TYPE, "tab")
}
// 开服表
TYPE_SERVER -> {
className = GameServersPublishFragment::class.java.name
@ -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)
}
// 通用内容合集详情页

View File

@ -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
}
}

View File

@ -1,18 +1,23 @@
package com.gh.common.view
import android.content.Context
import android.graphics.Typeface
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckedTextView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.forEach
import androidx.core.view.setPadding
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.setDrawableEnd
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.google.android.flexbox.FlexboxLayout
@ -30,11 +35,15 @@ class ConfigFilterView @JvmOverloads constructor(
var recommendedTv: TextView
var updateTv: TextView //更新
var container: View
private var dot1: View
private var dot2: View
private var dot3: View
var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
private var mOnConfigFilterSetupListener: OnConfigFilterSetupListener? = null
private var mSelectionTvList: ArrayList<TextView>
private var highlightedSortedType: SortType? = null
init {
View.inflate(context, R.layout.layout_config_filter, this)
@ -45,6 +54,9 @@ class ConfigFilterView @JvmOverloads constructor(
updateTv = findViewById(R.id.updateTv)
recommendedTv = findViewById(R.id.recommended_tv)
container = findViewById(R.id.config_controller)
dot1 = findViewById(R.id.dot1)
dot2 = findViewById(R.id.dot2)
dot3 = findViewById(R.id.dot3)
mSelectionTvList = arrayListOf(newestTv, ratingTv, updateTv, recommendedTv)
@ -54,24 +66,30 @@ class ConfigFilterView @JvmOverloads constructor(
}
ratingTv.setOnClickListener {
highlightedSortedType = SortType.RATING
mOnConfigFilterSetupListener?.onSetupSortType(SortType.RATING)
updateHighlightedTextView(ratingTv)
}
updateTv.setOnClickListener {
highlightedSortedType = SortType.UPDATE
mOnConfigFilterSetupListener?.onSetupSortType(SortType.UPDATE)
updateHighlightedTextView(updateTv)
}
newestTv.setOnClickListener {
highlightedSortedType = SortType.NEWEST
mOnConfigFilterSetupListener?.onSetupSortType(SortType.NEWEST)
updateHighlightedTextView(newestTv)
}
recommendedTv.setOnClickListener {
highlightedSortedType = SortType.RECOMMENDED
mOnConfigFilterSetupListener?.onSetupSortType(SortType.RECOMMENDED)
updateHighlightedTextView(recommendedTv)
}
updateAllTextView(SortType.UPDATE)
}
private fun updateHighlightedTextView(highlightedTv: TextView) {
@ -80,14 +98,17 @@ class ConfigFilterView @JvmOverloads constructor(
}
}
fun updateAllTextView(sortType: SortType) {
when (sortType) {
SortType.RECOMMENDED -> updateHighlightedTextView(recommendedTv)
SortType.NEWEST -> updateHighlightedTextView(newestTv)
SortType.RATING -> updateHighlightedTextView(ratingTv)
SortType.UPDATE -> updateHighlightedTextView(updateTv)
fun updateAllTextView(sortType: SortType? = highlightedSortedType) {
highlightedSortedType = sortType
sortType?.let {
when (it) {
SortType.RECOMMENDED -> updateHighlightedTextView(recommendedTv)
SortType.NEWEST -> updateHighlightedTextView(newestTv)
SortType.RATING -> updateHighlightedTextView(ratingTv)
SortType.UPDATE -> updateHighlightedTextView(updateTv)
}
mSizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
}
mSizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
}
fun setOnConfigSetupListener(onConfigFilterSetupListener: OnConfigFilterSetupListener) {
@ -96,11 +117,11 @@ class ConfigFilterView @JvmOverloads constructor(
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
if (highlightIt) {
targetTextView.background = R.drawable.bg_tag_text.toDrawable()
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_white.toColor(context))
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
targetTextView.setTypeface(Typeface.DEFAULT, Typeface.BOLD)
} else {
targetTextView.background = null
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
targetTextView.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
}
}
@ -112,8 +133,8 @@ class ConfigFilterView @JvmOverloads constructor(
}
private fun showSelectionPopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(sizeTv.context))
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(sizeTv.context))
sizeTv.setDrawableEnd(R.drawable.ic_auxiliary_arrow_up_primary_8)
val inflater = LayoutInflater.from(sizeTv.context)
val layout = inflater.inflate(R.layout.layout_filter_size, null)
@ -136,43 +157,39 @@ class ConfigFilterView @JvmOverloads constructor(
}
}
flexboxLayout.setOnClickListener { }
backgroundView.setOnClickListener {
popupWindow.dismiss()
}
for (size in sizeFilterArray!!) {
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
val item = inflater.inflate(R.layout.item_config_filter_size, flexboxLayout, false)
// 单列 4 个,强行设置宽度为屏幕的 1/4
val width = sizeTv.context.resources.displayMetrics.widthPixels / 4
val width = (sizeTv.context.resources.displayMetrics.widthPixels - 56F.dip2px()) / 4
val height = item.layoutParams.height
item.layoutParams = ViewGroup.LayoutParams(width, height)
flexboxLayout.addView(item)
val tv = item.findViewById<TextView>(R.id.size_tv)
val tv = item.findViewById<CheckedTextView>(R.id.size_tv)
tv.text = size.text
if (sizeText == size.text) {
toggleHighlightedTextView(tv, true)
} else {
toggleHighlightedTextView(tv, false)
}
tv.isChecked = sizeText == size.text
tv.tag = size.text
item.setOnClickListener {
toggleHighlightedTextView(tv, true)
tv.setOnClickListener {
flexboxLayout.forEach { checkedTv ->
(checkedTv as CheckedTextView).isChecked = checkedTv == tv
}
popupWindow.dismiss()
sizeTv.text = size.text
mOnConfigFilterSetupListener?.onSetupSortSize(size)
}
}
popupWindow.setOnDismissListener {
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(sizeTv.context))
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(sizeTv.context))
sizeTv.setDrawableEnd(R.drawable.ic_auxiliary_arrow_down_8)
mPopupWindow = null
}
@ -193,6 +210,49 @@ class ConfigFilterView @JvmOverloads constructor(
}
}
fun initSubjectFilterView(subjectSetting: SubjectSettingEntity) {
ratingTv.visibility = View.VISIBLE
if (subjectSetting.filterOptions.size > 1) {
// 重排序
subjectSetting.filterOptions.forEachIndexed { index, s ->
when (index) {
0 -> updateTv.text = s
1 -> recommendedTv.text = s
2 -> newestTv.text = s
3 -> ratingTv.text = s
}
}
} else {
updateTv.setPadding(0)
updateTv.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
updateTv.isClickable = false
updateTv.text = when (subjectSetting.filterOptions.first()) {
"推荐" -> "根据光环推荐排序"
"最新" -> "根据游戏上新排序"
"评分" -> "根据游戏评分排序"
"更新" -> "根据更新时间排序"
else -> subjectSetting.filterOptions.first()
}
}
// 隐藏相关选项
updateTv.goneIf(subjectSetting.filterOptions.isEmpty())
recommendedTv.goneIf(subjectSetting.filterOptions.size <= 1)
dot1.goneIf(subjectSetting.filterOptions.size <= 1)
newestTv.goneIf(subjectSetting.filterOptions.size <= 2)
dot2.goneIf(subjectSetting.filterOptions.size <= 2)
ratingTv.goneIf(subjectSetting.filterOptions.size <= 3)
dot3.goneIf(subjectSetting.filterOptions.size <= 3)
sizeFilterArray = subjectSetting.filterSizes
if (subjectSetting.filterOptions.size == 1) {
updateTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
updateTv.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
highlightedSortedType = null
}
}
interface OnConfigFilterSetupListener {
fun onShowSortSize()
fun onSetupSortSize(sortSize: SubjectSettingEntity.Size)

View File

@ -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, "")
}
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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 = "全部"
)
}

View File

@ -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()
}
}

View File

@ -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) {
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -7,5 +7,8 @@ class GameColumnCollection(
val id: String = "",
val name: String = "",
// 取值为 "1-1" 或 "1-2" 或 "top" 相应地代表 1行1个 或 1行2个 或 排行榜
val style: String = ""
val style: String = "",
val custom: Boolean = false, // 自定义设置
@SerializedName("custom_size")
val customSize: Int = 0 // 默认显示前X个专题
)

View File

@ -7,7 +7,7 @@ import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@Parcelize
class SubjectData(
data class SubjectData(
// 入口必填
var subjectId: String?,
var subjectName: String?,
@ -21,13 +21,28 @@ class SubjectData(
var subjectStyle: String = "",
var showDetailSubtitle: Boolean = false,
var showDetailIconSubscript: Boolean = false,
var customLimit: String = "", // unlimited无限制、forbidden禁止移出
var requireUpdateSetting: Boolean = false, // 多专题页面需要专题页面自行获取专题配置
var isAdData: Boolean = false,
var adId: String = "", // 广告ID(本地字段),不为空时为广告专题
var codeId: String = "" // 广告CODE_ID(本地字段),不为空时为广告专题
var codeId: String = "", // 广告CODE_ID(本地字段),不为空时为广告专题
var tag: String = "" // 分类标签,埋点用
) : Parcelable, Cloneable {
@IgnoredOnParcel
val isForbidden
get() = customLimit == "forbidden"
@IgnoredOnParcel
val sortChinese
get() = when {
sort.contains("publish") -> "最新"
sort.contains("star") -> "评分"
sort.contains("update") -> "更新"
else -> "推荐"
}
@IgnoredOnParcel
val subjectStyleChinese: String
get() = CustomPageItem.subjectTypeToComponentStyle[subjectStyle] ?: ""

View File

@ -13,7 +13,7 @@ class SubjectSettingEntity(
@SerializedName("type")
var typeEntity: TypeEntity = TypeEntity(),
var tag: String = "",
var filter: String = "", // rows: off/on
var filter: String = "", // off/on
var order: Boolean = false, // 是否显示序号
@SerializedName("brief_style")
@ -34,6 +34,9 @@ class SubjectSettingEntity(
private val _showDetailIconSubscript: Boolean? = null
) : Parcelable {
val isFilterEnabled: Boolean
get() = filter == "on"
val showDetailSubtitle: Boolean
get() = _showDetailSubtitle ?: false

View File

@ -2,7 +2,7 @@ package com.gh.gamecenter.game.columncollection.detail
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProviders
import androidx.core.view.isVisible
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.baselist.LazyListFragment
@ -14,6 +14,8 @@ import com.gh.gamecenter.common.json.json
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.FragmentColumnCollectionDetailBinding
import com.gh.gamecenter.entity.GameColumnCollection
import com.gh.gamecenter.entity.SubjectData
@ -23,6 +25,7 @@ class ColumnCollectionDetailFragment : LazyListFragment<LinkEntity, ColumnCollec
private var mAdapter: ColumnCollectionDetailAdapter? = null
private var mBinding: FragmentColumnCollectionDetailBinding? = null
private var mFragment: SubjectTabFragment? = null
private var mTabIndex = -1
private var mBasicExposureSourceList: ArrayList<ExposureSource>? = null
@ -56,6 +59,11 @@ class ColumnCollectionDetailFragment : LazyListFragment<LinkEntity, ColumnCollec
override fun onFragmentFirstVisible() {
super.onFragmentFirstVisible()
if (mIsFromMainWrapper) {
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn)
mBinding?.statusBar?.isVisible = true
}
mListViewModel.getGameColumnCollection()
mListViewModel.columnCollection.observeNonNull(this) {
setNavigationTitle(it.name)
@ -91,7 +99,7 @@ class ColumnCollectionDetailFragment : LazyListFragment<LinkEntity, ColumnCollec
}
override fun onChanged(list: MutableList<LinkEntity>?) {
if (list != null && list.isNotEmpty()) {
if (!list.isNullOrEmpty()) {
showSubjectTab(list)
}
}
@ -115,25 +123,28 @@ class ColumnCollectionDetailFragment : LazyListFragment<LinkEntity, ColumnCollec
isOrder = false,
requireUpdateSetting = true,
filter = "type:全部", // 默认显示大图
subjectType = subjectType
subjectType = subjectType,
customLimit = link.customLimit
)
)
}
val fragment = childFragmentManager.findFragmentByTag(SubjectTabFragment::class.java.name)
mFragment = childFragmentManager.findFragmentByTag(SubjectTabFragment::class.java.name) as? SubjectTabFragment
?: SubjectTabFragment()
val bundle = arguments
mListViewModel.columnCollection.value?.let {
bundle?.putString(EntranceConsts.KEY_COLUMN_COLLECTION_ID, it.id)
bundle?.putString(EntranceConsts.KEY_COLUMN_COLLECTION_NAME, it.name)
bundle?.putString(EntranceConsts.KEY_COLUMN_COLLECTION_STYLE, it.style)
bundle?.putBoolean(EntranceConsts.KEY_COLUMN_COLLECTION_CUSTOM, it.custom)
bundle?.putInt(EntranceConsts.KEY_COLUMN_COLLECTION_CUSTOM_SIZE, it.customSize)
}
bundle?.putParcelableArrayList(EntranceConsts.KEY_DATA, subjectDataList)
bundle?.putBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION, true)
fragment.arguments = bundle
mFragment!!.arguments = bundle
mBinding?.placeholder?.visibility = View.VISIBLE
childFragmentManager.beginTransaction().replace(R.id.placeholder, fragment, SubjectTabFragment::class.java.name)
childFragmentManager.beginTransaction().replace(R.id.placeholder, mFragment!!, SubjectTabFragment::class.java.name)
.commitAllowingStateLoss()
}
@ -167,9 +178,18 @@ class ColumnCollectionDetailFragment : LazyListFragment<LinkEntity, ColumnCollec
arguments?.getString(EntranceConsts.KEY_COLLECTION_ID)
?: ""
)
return ViewModelProviders.of(this, factory).get(ColumnCollectionDetailViewModel::class.java)
return viewModelProviderFromParent(factory, arguments?.getString(EntranceConsts.KEY_COLLECTION_ID) ?: "")
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
if (mIsFromMainWrapper) {
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn)
}
}
override fun onBackPressed(): Boolean = mFragment?.onBackPressed() ?: false
companion object {
const val TYPE_QQ_MINI_GAME_COLUMN = "qq_mini_game_column"
const val TYPE_WECHAT_MINI_GAME_COLUMN = "wechat_mini_game_column"

View File

@ -30,7 +30,9 @@ import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.CustomLinkMovementMethod
import com.gh.gamecenter.common.view.DrawableView
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.core.utils.CenterImageSpan
import com.gh.gamecenter.core.utils.NumberUtils
import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.databinding.RatingCommentItemBinding
import com.gh.gamecenter.entity.RatingComment
import com.gh.gamecenter.feature.entity.GameEntity
@ -249,7 +251,7 @@ class RatingCommentItemViewHolder(val binding: RatingCommentItemBinding, val pat
if (game.getApk().size > 0 && game.getApk()[0].version == commentData.gameVersion) {
version.text = "当前版本"
} else {
version.text = ("版本:" + commentData.gameVersion)
version.text = commentData.gameVersion
}
}
}

View File

@ -7,6 +7,7 @@ import android.text.Spanned
import android.text.TextPaint
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
@ -15,6 +16,7 @@ import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import com.gh.common.util.*
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.NewLogUtils
@ -27,7 +29,8 @@ 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.view.DrawableView
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.core.utils.NumberUtils
import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding
import com.gh.gamecenter.entity.RatingComment
import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity
@ -165,7 +168,36 @@ class RatingDetailCommentItemViewHolder(val binding: ItemArticleDetailCommentBin
if (game != null && game.getApk().size > 0 && game.getApk()[0].version == commentData.gameVersion) {
version.text = "当前版本"
} else {
version.text = ("版本:" + commentData.gameVersion)
version.text = commentData.gameVersion
}
version.buttonDrawable = R.drawable.ic_version.toDrawable(context)
version.post {
ConstraintSet().apply {
clone(bottomContainer)
if ((version.layout?.lineCount ?: 1) > 1) {
version.gravity = Gravity.TOP
connect(version.id, ConstraintSet.START, device.id, ConstraintSet.START)
connect(version.id, ConstraintSet.TOP, device.id, ConstraintSet.BOTTOM, 6F.dip2px())
connect(version.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 16F.dip2px())
connect(device.id, ConstraintSet.BOTTOM, version.id, ConstraintSet.TOP)
connect(device.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 16F.dip2px())
clear(device.id, ConstraintSet.END)
clear(likeCountTv.id, ConstraintSet.TOP)
connect(likeCountTv.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 4F.dip2px())
bottomContainer.updateLayoutParams<ConstraintLayout.LayoutParams> { height = ConstraintLayout.LayoutParams.WRAP_CONTENT }
} else {
version.gravity = Gravity.CENTER_VERTICAL
connect(version.id, ConstraintSet.START, device.id, ConstraintSet.END)
connect(version.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0)
connect(version.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 0)
connect(device.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
connect(device.id, ConstraintSet.END, version.id, ConstraintSet.START)
connect(device.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0)
connect(likeCountTv.id, ConstraintSet.TOP, timeTv.id, ConstraintSet.TOP, 0)
connect(likeCountTv.id, ConstraintSet.BOTTOM, timeTv.id, ConstraintSet.BOTTOM, 0)
bottomContainer.updateLayoutParams<ConstraintLayout.LayoutParams> { height = 48F.dip2px() }
}
}.applyTo(bottomContainer)
}
if (commentData.me.isCommented) {

View File

@ -42,6 +42,7 @@ class SubjectAdapter(
private val mSubjectViewModel: SubjectViewModel?,
private val mEntrance: String?,
private val mIsColumnCollection: Boolean,
private val mColumnCollectionCustomPosition: Int = -1,
private val mCollectionId: String,
private val mCollectionName: String,
private val mCollectionStyle: String
@ -217,9 +218,10 @@ class SubjectAdapter(
mSubjectViewModel?.subjectSettingLD?.value?.let { subjectSetting ->
subjectStyle = subjectSetting.typeEntity.layout
isFilterOn = subjectSetting.filter == "on"
isFilterOn = subjectSetting.isFilterEnabled
}
gameEntity.sequence = position
val exposureEvent = generateExposureEvent(gameEntity, subjectStyle, isFilterOn)
val humanReadablePosition = position + 1
mExposureEventSparseArray.put(position, exposureEvent)
@ -266,7 +268,10 @@ class SubjectAdapter(
gameColumnName = subjectData.subjectName ?: "",
gameId = gameEntity.id,
gameName = gameEntity.name ?: "",
text = "游戏"
text = "游戏",
gameTag = mViewModel.selectedLabelList.ifEmpty { listOf(mViewModel.subjectData.tag) },
sort = mViewModel.subjectData.sortChinese,
inclusionSize = mViewModel.selectedFilterSize.text ?: ""
)
}
}
@ -326,7 +331,10 @@ class SubjectAdapter(
gameColumnName = subjectData.subjectName ?: "",
gameId = gameEntity.id,
gameName = gameEntity.name ?: "",
text = "按钮"
text = "按钮",
gameTag = mViewModel.selectedLabelList.ifEmpty { listOf(mViewModel.subjectData.tag) },
sort = mViewModel.subjectData.sortChinese,
inclusionSize = mViewModel.selectedFilterSize.text ?: ""
)
}
@ -446,7 +454,10 @@ class SubjectAdapter(
linkId = linkEntity.link ?: "",
linkText = linkEntity.text ?: "",
linkType = linkEntity.type ?: "",
text = "头图"
text = "头图",
gameTag = mViewModel.selectedLabelList.ifEmpty { listOf(mViewModel.subjectData.tag) },
sort = mViewModel.subjectData.sortChinese,
inclusionSize = mViewModel.selectedFilterSize.text ?: ""
)
}
}
@ -471,13 +482,12 @@ class SubjectAdapter(
"$mCollectionName+${CustomPageItem.collectionTypeToComponentName[mCollectionStyle] ?: mCollectionStyle}+$mCollectionId"
)
)
exposureSourceList.add(ExposureSource("合集详情"))
exposureSourceList.add(
ExposureSource(
"专题",
"${subjectData.subjectName}+${subjectData.subjectStyleChinese}+${subjectData.subjectId}"
)
)
exposureSourceList.add(ExposureSource(if (mColumnCollectionCustomPosition == -1) "合集详情" else "自定义合集详情"))
var value = "${subjectData.subjectName}+${subjectData.subjectStyleChinese}+${subjectData.subjectId}"
if (mColumnCollectionCustomPosition != -1) {
value += "+${mColumnCollectionCustomPosition}"
}
exposureSourceList.add(ExposureSource("专题", value))
} else {
var sourceString = ""
exposureSourceList.add(
@ -493,12 +503,7 @@ class SubjectAdapter(
sourceString = filter
if (isFilterOn) {
val sort = when {
subjectData.sort.contains("publish") -> "最新"
subjectData.sort.contains("star") -> "评分"
else -> "最热"
}
val sort = subjectData.sortChinese
val filterSize = mViewModel.selectedFilterSize.text
sourceString =
@ -516,12 +521,7 @@ class SubjectAdapter(
}
if (isFilterOn) {
val sort = when {
subjectData.sort.contains("publish") -> "最新"
subjectData.sort.contains("star") -> "评分"
else -> "推荐"
}
val sort = subjectData.sortChinese
val filterSize = mViewModel.selectedFilterSize.text
sourceString =
@ -532,12 +532,7 @@ class SubjectAdapter(
}
else -> {
val sort = when {
subjectData.sort.contains("publish") -> "最新"
subjectData.sort.contains("star") -> "评分"
else -> "推荐"
}
val sort = subjectData.sortChinese
val filterSize = mViewModel.selectedFilterSize.text
if (isFilterOn) {

View File

@ -125,7 +125,7 @@ open class SubjectFragment : LazyFragment() {
}
val bundle = arguments
bundle?.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, mViewModel?.subjectData)
bundle?.putParcelable(SubjectSettingEntity::class.java.simpleName, entity)
bundle?.putParcelable(EntranceConsts.KEY_SUBJECT_SETTING_DATA, entity)
fragment.arguments = bundle
transaction.replace(R.id.subject_content, fragment, tag)
transaction.commitAllowingStateLoss()

View File

@ -6,6 +6,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.ethanhua.skeleton.Skeleton
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DialogUtils
import com.gh.common.view.ConfigFilterView
import com.gh.common.view.ConfigFilterView.SortType
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
import com.gh.download.DownloadManager
@ -14,10 +16,12 @@ import com.gh.gamecenter.common.baselist.LazyListFragment
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
import com.gh.gamecenter.common.view.SpacingItemDecoration
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.entity.SubjectData
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.eventbus.EBDownloadStatus
@ -43,6 +47,8 @@ open class SubjectListFragment : LazyListFragment<GameEntity, SubjectListViewMod
private var mCollectionName = ""
private var mCollectionStyle = ""
private var mScrollTop = false
private var mMaxSize = ""
private var mMinSize = ""
var selectedLabelList = arrayListOf<String>()
@ -78,6 +84,7 @@ open class SubjectListFragment : LazyListFragment<GameEntity, SubjectListViewMod
mSubjectViewModel,
arguments?.getString(EntranceConsts.KEY_ENTRANCE),
mIsColumnCollection,
arguments?.getInt(EntranceConsts.KEY_COLUMN_COLLECTION_CUSTOM_POSITION, -1) ?: -1,
mCollectionId,
mCollectionName,
mCollectionStyle
@ -119,10 +126,14 @@ open class SubjectListFragment : LazyListFragment<GameEntity, SubjectListViewMod
mCollectionName = arguments?.getString(EntranceConsts.KEY_COLUMN_COLLECTION_NAME, "") ?: ""
mCollectionStyle = arguments?.getString(EntranceConsts.KEY_COLUMN_COLLECTION_STYLE, "") ?: ""
super.onFragmentFirstVisible()
(mListViewModel as SubjectListViewModel).lastPageDataMap =
arguments?.get(SubjectFragment.LAST_PAGE_DATA) as? HashMap<String, String>
(mListViewModel as SubjectListViewModel).run {
lastPageDataMap =
arguments?.get(SubjectFragment.LAST_PAGE_DATA) as? HashMap<String, String>
arguments?.getString(EntranceConsts.KEY_SUBJECT_SORT_SIZE)?.let { selectedFilterSize = SubjectSettingEntity.Size(text = it) }
}
mCachedView?.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
initFilterView()
val skeletonLayoutId = when (arguments?.getString(EntranceConsts.KEY_SUBJECT_TYPE) ?: "") {
"detail" -> R.layout.fragment_subject_detail_skeleton
"tab" -> R.layout.fragment_subject_tab_skeleton
@ -140,7 +151,62 @@ open class SubjectListFragment : LazyListFragment<GameEntity, SubjectListViewMod
mListViewModel.load(LoadType.REFRESH)
}
override fun getRealLayoutId() = R.layout.fragment_list_base_skeleton
private fun initFilterView() {
val subjectSetting= arguments?.getParcelable<SubjectSettingEntity>(EntranceConsts.KEY_SUBJECT_SETTING_DATA) ?: return
val originalFilter = mListViewModel.subjectData.filter
mCachedView?.findViewById<ConfigFilterView>(R.id.filterView)?.run {
goneIf(!subjectSetting.isFilterEnabled) {
initSubjectFilterView(subjectSetting)
when (updateTv.text) {
"更新" -> mListViewModel.subjectData.sort = UrlFilterUtils.getFilterQuery("update_time", "-1")
"最新" -> mListViewModel.subjectData.sort = UrlFilterUtils.getFilterQuery("publish", "-1")
"评分" -> mListViewModel.subjectData.sort = UrlFilterUtils.getFilterQuery("star", "-1")
else -> mListViewModel.subjectData.sort = UrlFilterUtils.getFilterQuery("position", "1")
}
setOnConfigSetupListener(object :
ConfigFilterView.OnConfigFilterSetupListener {
override fun onShowSortSize() {}
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
mMinSize = sortSize.min.toString()
mMaxSize = sortSize.max.toString()
var filter = originalFilter.ifEmpty { "type:全部" }
if (mMinSize.isNotEmpty() && mMinSize != "-1") {
filter += ",${UrlFilterUtils.getFilterQuery("min_size", mMinSize)}"
}
if (mMaxSize.isNotEmpty() && mMaxSize != "-1") {
filter += ",${UrlFilterUtils.getFilterQuery("max_size", mMaxSize)}"
}
refreshPage(filter = filter, size = sortSize)
}
override fun onSetupSortType(sortType: SortType) {
val clickTv = when (sortType) {
SortType.RATING -> ratingTv
SortType.NEWEST -> newestTv
SortType.UPDATE -> updateTv
else -> recommendedTv
}
val sort = when (clickTv.text) {
"更新" -> UrlFilterUtils.getFilterQuery("update_time", "-1")
"最新" -> UrlFilterUtils.getFilterQuery("publish", "-1")
"评分" -> UrlFilterUtils.getFilterQuery("star", "-1")
else -> UrlFilterUtils.getFilterQuery("position", "1")
}
if (mListViewModel.subjectData.sort != sort) {
refreshPage(sort = sort)
}
}
})
}
}
}
override fun getRealLayoutId() = R.layout.fragment_subject_list
override fun initRealView() {
super.initRealView()
@ -160,7 +226,11 @@ open class SubjectListFragment : LazyListFragment<GameEntity, SubjectListViewMod
mListRv?.addOnScrollListener(mExposureListener)
}
fun refreshPage(filter: String, sort: String, size: SubjectSettingEntity.Size) {
fun refreshPage(
filter: String = mListViewModel.subjectData.filter,
sort: String = mListViewModel.subjectData.sort,
size: SubjectSettingEntity.Size = mListViewModel.selectedFilterSize
) {
mListViewModel.selectedFilterSize = size
mListViewModel.selectedLabelList = selectedLabelList
mListViewModel.subjectData.filter = filter
@ -181,10 +251,6 @@ open class SubjectListFragment : LazyListFragment<GameEntity, SubjectListViewMod
}
}
override fun onResume() {
super.onResume()
}
override fun onFragmentResume() {
super.onFragmentResume()
DownloadManager.getInstance().addObserver(dataWatcher)
@ -229,5 +295,10 @@ open class SubjectListFragment : LazyListFragment<GameEntity, SubjectListViewMod
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }
mCachedView?.findViewById<ConfigFilterView>(R.id.filterView)?.run {
if (isVisible) {
updateAllTextView()
}
}
}
}

View File

@ -28,7 +28,7 @@ open class SubjectListViewModel(
var exposureSourceList: List<ExposureSource>?
) : ListViewModel<GameEntity, GameEntity>(application) {
// 供专题类型为 rows 时统计用
// 供统计用
var selectedLabelList = arrayListOf<String>()
var selectedFilterSize = SubjectSettingEntity.Size(text = "全部大小")

View File

@ -1,17 +1,22 @@
package com.gh.gamecenter.subject.rows
import android.annotation.SuppressLint
import android.graphics.Typeface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.CheckedTextView
import android.widget.LinearLayout
import androidx.core.view.children
import androidx.core.view.updateLayoutParams
import com.gh.common.view.ConfigFilterView
import com.gh.common.view.ConfigFilterView.SortType
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.entity.SubjectData
@ -32,13 +37,15 @@ class SubjectRowsFragment : BaseFragment<Any>() {
// 统计用的大小筛选选中文案
private var mSelectedFilterSize = SubjectSettingEntity.Size(text = "全部大小")
private lateinit var filterView: ConfigFilterView
override fun getLayoutId(): Int {
return R.layout.fragment_subject_rows
}
override fun initView(view: View) {
super.initView(view)
mSettingEntity = arguments?.getParcelable(SubjectSettingEntity::class.java.simpleName)
mSettingEntity = arguments?.getParcelable(EntranceConsts.KEY_SUBJECT_SETTING_DATA)
?: return
mSubjectData = arguments?.getParcelable(EntranceConsts.KEY_SUBJECT_DATA) ?: return
@ -55,7 +62,7 @@ class SubjectRowsFragment : BaseFragment<Any>() {
mListFragment =
childFragmentManager.findFragmentByTag(SubjectListFragment::class.java.name) as? SubjectListFragment
?: SubjectListFragment()
mListFragment.arguments = arguments
mListFragment.arguments = (arguments?.clone() as? Bundle)?.apply { remove(EntranceConsts.KEY_SUBJECT_SETTING_DATA) }
childFragmentManager.beginTransaction()
.replace(R.id.rows_list_container, mListFragment, SubjectListFragment::class.java.name)
.commitAllowingStateLoss()
@ -64,18 +71,29 @@ class SubjectRowsFragment : BaseFragment<Any>() {
@SuppressLint("InflateParams")
private fun createLabelsLayout() {
val inflater = LayoutInflater.from(context)
for (labels in mSettingEntity.typeEntity.labels) {
mSettingEntity.typeEntity.labels.forEachIndexed { index, labels ->
val labelsItem = inflater.inflate(R.layout.subject_rows_label, mLabelsContainer, false)
val labelContainer = labelsItem.findViewById<LinearLayout>(R.id.label_container)
for (index in 0 until labels.label.size) {
val labelName = labels.label[index]
labelsItem.updateLayoutParams<MarginLayoutParams> {
topMargin = if (index == 0) 0 else 12F.dip2px()
}
for (labelIndex in 0 until labels.label.size) {
val labelName = labels.label[labelIndex]
val labelItem =
inflater.inflate(R.layout.subject_rows_label_item, labelContainer, false)
val label = labelItem.findViewById<CheckedTextView>(R.id.label)
if (index == 0) label.isChecked = true
label.updateLayoutParams<MarginLayoutParams> {
leftMargin = if (labelIndex == 0) 0 else 8F.dip2px()
}
if (labelIndex == 0) {
label.isChecked = true
label.setTypeface(Typeface.DEFAULT, Typeface.BOLD)
} else {
label.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
}
label.text = labelName
label.setOnClickListener {
mFilterMap[labels.name] = if (index == 0) {
mFilterMap[labels.name] = if (labelIndex == 0) {
""
} else {
labelName
@ -92,26 +110,18 @@ class SubjectRowsFragment : BaseFragment<Any>() {
private fun selectLabel(labelContainer: LinearLayout, label: CheckedTextView) {
for (i in 0 until labelContainer.childCount) {
val view = labelContainer.getChildAt(i)
if (view is CheckedTextView && view.isChecked) view.isChecked = false
if (view is CheckedTextView && view.isChecked) {
view.isChecked = false
view.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
}
}
label.isChecked = true
label.setTypeface(Typeface.DEFAULT, Typeface.BOLD)
}
private fun createFilterLayout() {
if (mSettingEntity.filter == "on") {
val filterView = ConfigFilterView(requireContext())
filterView.ratingTv.visibility = View.VISIBLE
// 重排序
mSettingEntity.filterOptions.forEachIndexed { index, s ->
when (index) {
0 -> filterView.updateTv.text = s
1 -> filterView.recommendedTv.text = s
2 -> filterView.newestTv.text = s
3 -> filterView.ratingTv.text = s
}
}
if (mSettingEntity.isFilterEnabled) {
filterView = ConfigFilterView(requireContext()).apply { initSubjectFilterView(mSettingEntity) }
// 设置默认页面链接
when (filterView.updateTv.text) {
"更新" -> mSubjectData.sort = UrlFilterUtils.getFilterQuery("update_time", "-1")
@ -119,15 +129,14 @@ class SubjectRowsFragment : BaseFragment<Any>() {
"评分" -> mSubjectData.sort = UrlFilterUtils.getFilterQuery("star", "-1")
else -> mSubjectData.sort = UrlFilterUtils.getFilterQuery("position", "1")
}
// 隐藏相关选项
filterView.updateTv.goneIf(mSettingEntity.filterOptions.isEmpty())
filterView.recommendedTv.goneIf(mSettingEntity.filterOptions.size <= 1)
filterView.newestTv.goneIf(mSettingEntity.filterOptions.size <= 2)
filterView.ratingTv.goneIf(mSettingEntity.filterOptions.size <= 3)
filterView.sizeFilterArray = mSettingEntity.filterSizes
mLabelsContainer.addView(filterView)
mLabelsContainer.addView(
filterView,
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
topMargin = 8F.dip2px()
})
filterView.setOnConfigSetupListener(object :
ConfigFilterView.OnConfigFilterSetupListener {
@ -150,11 +159,11 @@ class SubjectRowsFragment : BaseFragment<Any>() {
)
}
override fun onSetupSortType(sortType: ConfigFilterView.SortType) {
override fun onSetupSortType(sortType: SortType) {
val clickTv = when (sortType) {
ConfigFilterView.SortType.RATING -> filterView.ratingTv
ConfigFilterView.SortType.NEWEST -> filterView.newestTv
ConfigFilterView.SortType.UPDATE -> filterView.updateTv
SortType.RATING -> filterView.ratingTv
SortType.NEWEST -> filterView.newestTv
SortType.UPDATE -> filterView.updateTv
else -> filterView.recommendedTv
}
@ -171,6 +180,8 @@ class SubjectRowsFragment : BaseFragment<Any>() {
}
}
})
} else {
mLabelsContainer.setPadding(0, 8F.dip2px(), 0, 8F.dip2px())
}
}
@ -242,4 +253,11 @@ class SubjectRowsFragment : BaseFragment<Any>() {
mListFragment.selectedLabelList = findAllSelectedLabel(mLabelsContainer)
mListFragment.refreshPage(mSubjectData.filter, mSubjectData.sort, mSelectedFilterSize)
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
if (::filterView.isInitialized) {
filterView.updateAllTextView()
}
}
}

View File

@ -0,0 +1,97 @@
package com.gh.gamecenter.subject.tab
import android.app.Application
import android.content.Context
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.toJson
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.SubjectData
import com.gh.gamecenter.livedata.Event
import com.halo.assistant.HaloApp
class ColumnCollectionTabViewModel(
application: Application,
private val columnCollectionId: String,
private val customSize: Int,
private val subjectDataList: List<SubjectData>
) : AndroidViewModel(application) {
private val columnCollectionSp by lazy {
HaloApp.getInstance().getSharedPreferences(Constants.SP_COLUMN_COLLECTION_CUSTOM_TAB, Context.MODE_PRIVATE)
}
val tabListLiveData = MutableLiveData<List<SubjectData>>()
val followListLiveData = MutableLiveData<List<SubjectData>>()
val moreListLiveData = MutableLiveData<List<SubjectData>>()
val addFollowLiveData = MutableLiveData<Event<SubjectData>>()
val addMoreLiveData = MutableLiveData<Event<SubjectData>>()
init {
initData()
}
private fun initData() {
val customTabIdList = GsonUtils.fromJsonList<String>(SPUtils.getString(columnCollectionSp, columnCollectionId))
if (customTabIdList.isEmpty()) {
val followTabList = subjectDataList.take(customSize)
tabListLiveData.postValue(followTabList)
followListLiveData.postValue(followTabList)
moreListLiveData.postValue(subjectDataList - followTabList.toSet())
} else {
val newAddedForbiddenList = subjectDataList.filter { it.isForbidden && it.subjectId !in customTabIdList }
val tabList = arrayListOf<SubjectData>().apply { addAll(newAddedForbiddenList) }
customTabIdList.forEach { subjectId->
subjectDataList.find { subjectId == it.subjectId }?.let {
tabList.add(it)
}
}
val followTabList = tabList.take(CUSTOM_MAX_TAB).ifEmpty { subjectDataList.take(customSize) }
tabListLiveData.postValue(followTabList)
followListLiveData.postValue(followTabList)
moreListLiveData.postValue(subjectDataList - followTabList.toSet())
}
}
fun addFollow(subjectData: SubjectData) {
addFollowLiveData.postValue(Event(subjectData))
}
fun addMore(subjectData: SubjectData) {
addMoreLiveData.postValue(Event(subjectData))
}
fun saveCustomTab(followTabList: List<SubjectData>) {
tabListLiveData.postValue(followTabList)
followListLiveData.postValue(followTabList)
moreListLiveData.postValue(subjectDataList - followTabList.toSet())
val followSubjectIdList = followTabList.map { it.subjectId ?: "" }.distinct()
SPUtils.setString(columnCollectionSp, columnCollectionId, followSubjectIdList.toJson())
}
companion object {
const val CUSTOM_MAX_TAB = 15
}
class Factory(
private val columnCollectionId: String,
private val customSize: Int,
private val subjectDataList: List<SubjectData>
) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ColumnCollectionTabViewModel(
HaloApp.getInstance().application,
columnCollectionId,
customSize,
subjectDataList
) as T
}
}
}

View File

@ -0,0 +1,56 @@
package com.gh.gamecenter.subject.tab
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil.ItemCallback
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.view.Chips
import com.gh.gamecenter.databinding.ItemColumnCollectionCustomTabBinding
import com.gh.gamecenter.entity.SubjectData
class CustomTabFollowAdapter(private val viewModel: ColumnCollectionTabViewModel?) :
ListAdapter<SubjectData, CustomTabFollowAdapter.CustomTabFollowItemViewHolder>(createDiffItemCallBack()) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CustomTabFollowItemViewHolder {
return CustomTabFollowItemViewHolder(parent.toBinding())
}
override fun onBindViewHolder(holder: CustomTabFollowItemViewHolder, position: Int) {
val subjectData = currentList.getOrNull(position) ?: return
val isForbidden = subjectData.isForbidden
holder.binding.chips.run {
setState(if (isForbidden) Chips.STATE_FILL else Chips.STATE_DEFAULT)
setText(subjectData.subjectName ?: "")
endIcon.isVisible = !isForbidden
endIcon.setImageResource(R.drawable.ic_basic_x_8_secondary)
endIcon.imageTintList = null
setOnClickListener {
if (!isForbidden) {
viewModel?.addMore(subjectData)
}
}
}
}
class CustomTabFollowItemViewHolder(val binding: ItemColumnCollectionCustomTabBinding) :
ViewHolder(binding.root)
companion object {
fun createDiffItemCallBack() = object : ItemCallback<SubjectData>() {
override fun areItemsTheSame(oldItem: SubjectData, newItem: SubjectData): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: SubjectData, newItem: SubjectData): Boolean {
return oldItem == newItem
}
}
}
}

View File

@ -0,0 +1,49 @@
package com.gh.gamecenter.subject.tab
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil.ItemCallback
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.view.Chips
import com.gh.gamecenter.databinding.ItemColumnCollectionCustomTabBinding
import com.gh.gamecenter.entity.SubjectData
class CustomTabMoreAdapter(private val viewModel: ColumnCollectionTabViewModel?) :
ListAdapter<SubjectData, CustomTabMoreAdapter.CustomTabMoreItemViewHolder>(createDiffItemCallBack()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomTabMoreItemViewHolder {
return CustomTabMoreItemViewHolder(parent.toBinding())
}
override fun onBindViewHolder(holder: CustomTabMoreItemViewHolder, position: Int) {
val subjectData = currentList.getOrNull(position) ?: return
holder.binding.chips.run {
setState(Chips.STATE_DEFAULT)
setText(subjectData.subjectName ?: "")
startIcon.isVisible = true
startIcon.setImageResource(R.drawable.ic_auxiliary_plus_secondary_8)
startIcon.imageTintList = null
setOnClickListener {
viewModel?.addFollow(subjectData)
}
}
}
class CustomTabMoreItemViewHolder(val binding: ItemColumnCollectionCustomTabBinding) : ViewHolder(binding.root)
companion object {
fun createDiffItemCallBack() = object : ItemCallback<SubjectData>() {
override fun areItemsTheSame(oldItem: SubjectData, newItem: SubjectData): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: SubjectData, newItem: SubjectData): Boolean {
return oldItem == newItem
}
}
}
}

View File

@ -1,56 +1,154 @@
package com.gh.gamecenter.subject.tab
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.graphics.Typeface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.CheckedTextView
import android.widget.TextView
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.DialogHelper.Config
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.FragmentSubjectTabBinding
import com.gh.gamecenter.databinding.LayoutColumnCollectionSettingBinding
import com.gh.gamecenter.databinding.TabItemMainBinding
import com.gh.gamecenter.entity.SubjectData
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.home.custom.model.CustomPageItem
import com.gh.gamecenter.livedata.EventObserver
import com.gh.gamecenter.subject.SubjectFragment
import com.gh.gamecenter.subject.SubjectListFragment
import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import kotlin.math.abs
import kotlin.math.roundToInt
class SubjectTabFragment : BaseFragment<Any>() {
class SubjectTabFragment : ToolbarFragment() {
private val mBinding by lazy { FragmentSubjectTabBinding.inflate(layoutInflater) }
private val binding by lazy { FragmentSubjectTabBinding.inflate(layoutInflater) }
private var settingBinding: LayoutColumnCollectionSettingBinding? = null
private var isSubject = false
private var isCustomEnabled = false
private var tabStyle = TabStyle.SUBJECT_NORMAL
private var tabBindingList = arrayListOf<TabItemMainBinding>()
private var isSettingAnimating = false
private var columnCollectionTabViewModel: ColumnCollectionTabViewModel? = null
private val fragmentTag by lazy { "android:switcher:${binding.subjectViewpager.id}:" }
private var collectionId = ""
private var collectionName = ""
private var collectionStyle = ""
private val followAdapter by lazy { CustomTabFollowAdapter(columnCollectionTabViewModel) }
private val moreAdapter by lazy { CustomTabMoreAdapter(columnCollectionTabViewModel) }
private val showOrHideMore: (Boolean) -> Unit = { isShow ->
settingBinding?.run {
if (isShow == moreContainer.isVisible) return@run
val startHeight = if (isShow) 0 else moreContainer.height
val endHeight = if (isShow) {
moreContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
moreContainer.measuredHeight
} else 0
ValueAnimator.ofInt(startHeight, endHeight).apply {
duration = ANIMATOR_DURATION
interpolator = AccelerateDecelerateInterpolator()
doOnStart {
if (isShow) {
moreContainer.updateLayoutParams<MarginLayoutParams> { height = 0 }
moreContainer.isVisible = true
} else {
moreContainer.isInvisible = true
}
}
doOnEnd {
if (isShow) {
moreContainer.updateLayoutParams<MarginLayoutParams> { height = MarginLayoutParams.WRAP_CONTENT }
} else {
moreContainer.isVisible = false
}
}
addUpdateListener { animation ->
moreContainer.updateLayoutParams<MarginLayoutParams> { height = animation.animatedValue as Int }
}
}.start()
}
}
private val itemTouchHelper by lazy {
ItemTouchHelper(object : ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.START or ItemTouchHelper.END, 0)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val from = viewHolder.bindingAdapterPosition
val to = target.bindingAdapterPosition
val newList = followAdapter.currentList.toMutableList()
Collections.swap(newList, from, to)
followAdapter.submitList(newList)
return true
}
override fun isLongPressDragEnabled(): Boolean = true
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
})
}
override fun getLayoutId() = 0
override fun getInflatedLayout() = mBinding.root
override fun getInflatedLayout() = binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val subjectList = arguments?.getParcelableArrayList<SubjectData>(EntranceConsts.KEY_DATA)
val subjectList = arguments?.getParcelableArrayList<SubjectData>(EntranceConsts.KEY_DATA) ?: arrayListOf()
isSubject = subjectList.isEmpty()
tabStyle = when {
mIsFromMainWrapper && !isSubject -> TabStyle.COLUMN_COLLECTION_MAIN_WRAPPER
isSubject -> TabStyle.SUBJECT_NORMAL
else -> TabStyle.COLUMN_COLLECTION_NORMAL
}
val fragments = ArrayList<Fragment>()
var tagList = arrayListOf<String>()
val fragmentTag = "android:switcher:${mBinding.subjectViewpager.id}:"
if (subjectList.isNullOrEmpty()) {
val tagList: ArrayList<String>
if (isSubject) {
// 专题详情
val subjectData = arguments?.getParcelable<SubjectData>(EntranceConsts.KEY_SUBJECT_DATA)
?: return
val settingsEntity =
arguments?.getParcelable<SubjectSettingEntity>(SubjectSettingEntity::class.java.simpleName)
arguments?.getParcelable<SubjectSettingEntity>(EntranceConsts.KEY_SUBJECT_SETTING_DATA)
?: return
tagList = settingsEntity.typeEntity.content as ArrayList<String>
if (tagList.size > 1) {
mBinding.subjectTabContainer.visibility = View.VISIBLE
binding.subjectTabContainer.visibility = View.VISIBLE
}
tagList.forEachIndexed { index, tag ->
val element = childFragmentManager.findFragmentByTag("${fragmentTag}$index")
@ -60,69 +158,192 @@ class SubjectTabFragment : BaseFragment<Any>() {
copyData.filter = if (tag == "全部") {
UrlFilterUtils.getFilterQuery("tags", tag, "type", "全部")
} else {
copyData.tag = tag
UrlFilterUtils.getFilterQuery("tags", tag)
}
bundle?.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, copyData)
bundle?.putParcelable(EntranceConsts.KEY_SUBJECT_SETTING_DATA, settingsEntity)
element.arguments = bundle
fragments.add(element)
}
if (binding.subjectTabContainer.isVisible) {
binding.subjectTabIndicator.setupWithTabLayout(binding.subjectTab)
binding.subjectTabIndicator.setupWithViewPager(binding.subjectViewpager)
}
val adapter = object : FragmentStatePagerAdapter(childFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
return fragments[position]
}
override fun getCount(): Int {
return fragments.size
}
override fun getPageTitle(position: Int): CharSequence {
return tagList[position]
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
// super.destroyItem(container, position, `object`)
}
}
binding.subjectViewpager.adapter = adapter
binding.subjectTab.setupWithViewPager(binding.subjectViewpager)
initTabLayout()
} else {
// 专题合集详情
mBinding.subjectTabContainer.visibility = View.VISIBLE
isCustomEnabled = arguments?.getBoolean(EntranceConsts.KEY_COLUMN_COLLECTION_CUSTOM) ?: false
val customSize = (arguments?.getInt(EntranceConsts.KEY_COLUMN_COLLECTION_CUSTOM_SIZE)
?: ColumnCollectionTabViewModel.CUSTOM_MAX_TAB).coerceAtMost(ColumnCollectionTabViewModel.CUSTOM_MAX_TAB)
collectionId = arguments?.getString(EntranceConsts.KEY_COLUMN_COLLECTION_ID, "") ?: ""
collectionName = arguments?.getString(EntranceConsts.KEY_COLUMN_COLLECTION_NAME, "") ?: ""
collectionStyle = arguments?.getString(EntranceConsts.KEY_COLUMN_COLLECTION_STYLE, "") ?: ""
binding.optionIv.isVisible = isCustomEnabled
binding.maskView.isVisible = isCustomEnabled
binding.optionIv.setOnClickListener {
showSettingView()
SensorsBridge.trackColumnCollectionClick(
location = "合集详情",
columnCollectionName = collectionName,
columnCollectionId = collectionId,
text = "自定义设置",
columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[collectionStyle]
?: collectionStyle
)
}
binding.subjectTabContainer.visibility = View.VISIBLE
if (isCustomEnabled) {
val columnCollectionId = arguments?.getString(EntranceConsts.KEY_COLUMN_COLLECTION_ID) ?: ""
columnCollectionTabViewModel = viewModelProvider(ColumnCollectionTabViewModel.Factory(columnCollectionId, customSize, subjectList))
observerData()
if (SPUtils.getBoolean(Constants.SP_SHOW_COLUMN_COLLECTION_CUSTOM_TAB_SETTING, true)) {
mBaseHandler.post {
showSettingView()
}
SPUtils.setBoolean(Constants.SP_SHOW_COLUMN_COLLECTION_CUSTOM_TAB_SETTING, false)
}
} else {
initColumnCollectionViewPager(subjectList)
}
}
}
subjectList.filterIndexed { index, subject ->
val element = childFragmentManager.findFragmentByTag("${fragmentTag}$index")
?: SubjectFragment()
val bundle = arguments?.clone() as Bundle?
bundle?.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, subject)
bundle?.putParcelableArrayList(EntranceConsts.KEY_DATA, null)
element.arguments = bundle
fragments.add(element)
tagList.add(subject.subjectName ?: "")
private fun observerData() {
columnCollectionTabViewModel?.tabListLiveData?.observe(viewLifecycleOwner) {
binding.subjectTab.clearOnTabSelectedListeners()
initColumnCollectionViewPager(it)
}
columnCollectionTabViewModel?.followListLiveData?.observe(viewLifecycleOwner) {
followAdapter.submitList(it)
}
columnCollectionTabViewModel?.moreListLiveData?.observe(viewLifecycleOwner) {
moreAdapter.submitList(it) {
showOrHideMore.invoke(it.isNotEmpty())
}
}
columnCollectionTabViewModel?.addFollowLiveData?.observe(viewLifecycleOwner, EventObserver {
val newFollowList = followAdapter.currentList.toMutableList()
if (newFollowList.size < ColumnCollectionTabViewModel.CUSTOM_MAX_TAB) {
val newMoreList = moreAdapter.currentList.toMutableList().apply { remove(it) }
followAdapter.submitList(newFollowList.apply { add(it) })
moreAdapter.submitList(newMoreList) {
showOrHideMore.invoke(newMoreList.isNotEmpty())
}
} else {
toast("最多可以添加15个关注内容哦~")
}
})
columnCollectionTabViewModel?.addMoreLiveData?.observe(viewLifecycleOwner, EventObserver {
if (followAdapter.currentList.size == 1) {
toast("至少添加1个内容哦~")
return@EventObserver
}
mBinding.subjectViewpager.post {
mBinding.subjectViewpager.offscreenPageLimit = fragments.size
var position = arguments?.getInt(EntranceConsts.KEY_POSITION, 0) ?: 0
val columnName = arguments?.getString(EntranceConsts.KEY_COLUMNNAME) ?: ""
if (columnName.isNotEmpty()) {
position = subjectList.indexOfFirst { it.subjectName == columnName }
}
if (position < subjectList.size) {
mBinding.subjectViewpager.setCurrentItem(position, false)
}
followAdapter.submitList(followAdapter.currentList.toMutableList().apply { remove(it) })
val newMoreList = moreAdapter.currentList.toMutableList().apply { add(it) }
moreAdapter.submitList(newMoreList) {
showOrHideMore.invoke(newMoreList.isNotEmpty())
}
val categoryId = requireArguments().getString(EntranceConsts.KEY_COLUMN_COLLECTION_ID, "")
val categoryName = requireArguments().getString(EntranceConsts.KEY_COLUMN_COLLECTION_NAME, "")
})
}
mBinding.subjectViewpager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
subjectList[position]?.let {
MtaHelper.onEvent("游戏专题合集", "详情Tab", it.subjectName)
NewLogUtils.logColumnCategoryDetailContentClick(
it.subjectName ?: "", it.subjectId ?: "", categoryName, categoryId
)
private fun initColumnCollectionViewPager(subjectList: List<SubjectData>) {
val fragments = arrayListOf<Fragment>()
val tagList = arrayListOf<String>()
subjectList.filterIndexed { index, subject ->
val element = childFragmentManager.findFragmentByTag("${fragmentTag}$index")
?: SubjectFragment()
val bundle = arguments?.clone() as Bundle?
bundle?.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, subject)
bundle?.putParcelableArrayList(EntranceConsts.KEY_DATA, null)
bundle?.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, false)
bundle?.putBoolean(EntranceConsts.KEY_IS_FROM_TAB_WRAPPER, false)
bundle?.putBoolean(EntranceConsts.KEY_COLUMN_COLLECTION_CUSTOM, false)
if (isCustomEnabled) {
bundle?.putInt(EntranceConsts.KEY_COLUMN_COLLECTION_CUSTOM_POSITION, index)
}
element.arguments = bundle
fragments.add(element)
if (categoryName?.contains("排行榜") == true) {
val trackEvent = JSONObject()
try {
trackEvent.put("list_name", it.subjectName)
trackEvent.put("position", position)
} catch (e: JSONException) {
e.printStackTrace()
}
SensorsBridge.trackEvent("GameListPageSelected", trackEvent)
tagList.add(subject.subjectName ?: "")
}
binding.subjectViewpager.post {
binding.subjectViewpager.offscreenPageLimit = fragments.size
var position = arguments?.getInt(EntranceConsts.KEY_POSITION, 0) ?: 0
val columnName = arguments?.getString(EntranceConsts.KEY_COLUMNNAME) ?: ""
if (columnName.isNotEmpty()) {
position = subjectList.indexOfFirst { it.subjectName == columnName }
}
if (position < subjectList.size) {
binding.subjectViewpager.setCurrentItem(position, false)
}
}
val categoryId = requireArguments().getString(EntranceConsts.KEY_COLUMN_COLLECTION_ID, "")
val categoryName = requireArguments().getString(EntranceConsts.KEY_COLUMN_COLLECTION_NAME, "")
binding.subjectViewpager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
subjectList.getOrNull(position)?.let {
MtaHelper.onEvent("游戏专题合集", "详情Tab", it.subjectName)
NewLogUtils.logColumnCategoryDetailContentClick(
it.subjectName ?: "", it.subjectId ?: "", categoryName, categoryId
)
if (categoryName?.contains("排行榜") == true) {
val trackEvent = JSONObject()
try {
trackEvent.put("list_name", it.subjectName)
trackEvent.put("position", position)
} catch (e: JSONException) {
e.printStackTrace()
}
SensorsBridge.trackEvent("GameListPageSelected", trackEvent)
}
}
})
}
}
if (mBinding.subjectTabContainer.visibility == View.VISIBLE) {
mBinding.subjectTabIndicator.setupWithTabLayout(mBinding.subjectTab)
mBinding.subjectTabIndicator.setupWithViewPager(mBinding.subjectViewpager)
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
if (mIsFromMainWrapper) {
// 这里的 selectedPosition 指的是应该被高亮显示的 position
val selectedPosition = try {
(position + positionOffset).roundToInt()
} catch (e: IllegalArgumentException) {
// roundToInt() 方法有时候会报 Cannot round NaN value. 错误 positionOffset 的值为 NAN
// https://sentry.shanqu.cc/organizations/lightgame/issues/301377/?project=22
position
}
val positionOffsetOnRealSelectedPosition = if (positionOffset >= 0.5) {
positionOffset - 1
} else {
positionOffset
}
updateTabStyle(selectedPosition, positionOffsetOnRealSelectedPosition)
}
}
})
val adapter = object : FragmentStatePagerAdapter(childFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
@ -133,7 +354,7 @@ class SubjectTabFragment : BaseFragment<Any>() {
return fragments.size
}
override fun getPageTitle(position: Int): CharSequence? {
override fun getPageTitle(position: Int): CharSequence {
return tagList[position]
}
@ -141,76 +362,478 @@ class SubjectTabFragment : BaseFragment<Any>() {
// super.destroyItem(container, position, `object`)
}
}
mBinding.subjectViewpager.adapter = adapter
mBinding.subjectTab.setupWithViewPager(mBinding.subjectViewpager)
// 首页样式的 tabLayout
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
mBinding.subjectTabIndicator.visibility = View.GONE
for (i in 0 until mBinding.subjectTab.tabCount) {
val tab = mBinding.subjectTab.getTabAt(i) ?: continue
binding.subjectViewpager.adapter = adapter
binding.subjectTab.setupWithViewPager(binding.subjectViewpager)
binding.subjectTabIndicator.setupWithTabLayout(binding.subjectTab)
binding.subjectTabIndicator.setupWithViewPager(binding.subjectViewpager)
initTabLayout()
}
private fun initTabLayout() {
if (tabStyle == TabStyle.COLUMN_COLLECTION_NORMAL) {
if (isCustomEnabled && SPUtils.getBoolean(Constants.SP_SHOW_COLUMN_COLLECTION_CUSTOM_TAB_GUIDE, true)) {
binding.subjectTab.addOnTabSelectedListener(object : OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
showSettingGuideIfNeeded(this)
}
override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabReselected(tab: TabLayout.Tab?) {
showSettingGuideIfNeeded(this)
}
})
}
binding.subjectTabIndicator.run {
setIndicatorWidth(12)
updateLayoutParams<ConstraintLayout.LayoutParams> {
bottomMargin = 10F.dip2px()
}
}
for (i in 0 until binding.subjectTab.tabCount) {
binding.subjectTab.getTabAt(i)?.view?.run {
background = null
if (i == 0) {
updateLayoutParams<MarginLayoutParams> { leftMargin = 4F.dip2px() }
}
}
}
} else {
tabBindingList.clear()
val tabCount = binding.subjectTab.tabCount
for (i in 0 until tabCount) {
val tab = binding.subjectTab.getTabAt(i) ?: continue
val tabTitle = if (tab.text != null) tab.text.toString() else ""
tab.view.background = null
tab.customView = provideTabView(tabTitle)
if (i == 0) {
tab.customView?.background = R.drawable.border_round_theme_14.toDrawable(requireContext())
tab.customView?.findViewById<TextView>(R.id.tab_title)
?.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(requireContext()))
if (tabStyle == TabStyle.COLUMN_COLLECTION_MAIN_WRAPPER) {
tab.view.setPadding(0, 0, 0, 0)
if (i == 0) {
tab.view.updateLayoutParams {
this as LinearLayout.LayoutParams
setMargins(10F.dip2px(), 0, 0, 0)
}
} else if (i == tabCount - 1) {
tab.view.updateLayoutParams {
this as LinearLayout.LayoutParams
setMargins(0, 0, 10F.dip2px(), 0)
}
}
} else {
tab.customView?.background = R.drawable.border_round_eee_14.toDrawable(requireContext())
tab.customView?.findViewById<TextView>(R.id.tab_title)
?.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
}
when (i) {
0 -> tab.view.setPadding(12F.dip2px(), 0, 0, 0)
mBinding.subjectTab.tabCount - 1 -> tab.view.setPadding(8F.dip2px(), 0, 12F.dip2px(), 0)
else -> tab.view.setPadding(8F.dip2px(), 0, 0, 0)
when (i) {
0 -> tab.view.setPadding(16F.dip2px(), 0, 0, 0)
binding.subjectTab.tabCount - 1 -> tab.view.setPadding(8F.dip2px(), 0, 16F.dip2px(), 0)
else -> tab.view.setPadding(8F.dip2px(), 0, 0, 0)
}
}
}
mBinding.subjectTab.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {}
override fun onTabUnselected(tab: TabLayout.Tab?) {
tab?.customView?.background = R.drawable.border_round_eee_14.toDrawable(requireContext())
tab?.customView?.findViewById<TextView>(R.id.tab_title)
?.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
binding.subjectTabIndicator.isVisible = mIsFromMainWrapper
if (tabStyle == TabStyle.COLUMN_COLLECTION_MAIN_WRAPPER) {
binding.subjectTabIndicator.run {
setIndicatorWidth(18)
updateIndicatorDrawable(R.drawable.ic_commodity_selected.toDrawable(requireContext()))
updateLayoutParams<ConstraintLayout.LayoutParams> {
height = 8F.dip2px()
bottomMargin = 8F.dip2px()
}
}
override fun onTabSelected(tab: TabLayout.Tab?) {
tab?.customView?.background = R.drawable.border_round_theme_14.toDrawable(requireContext())
tab?.customView?.findViewById<TextView>(R.id.tab_title)
?.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(requireContext()))
binding.settingGuideIv.updateLayoutParams<ConstraintLayout.LayoutParams> { topMargin = 40F.dip2px() }
binding.subjectTabContainer.updateLayoutParams<ConstraintLayout.LayoutParams> { height = 56F.dip2px() }
binding.subjectTab.updateLayoutParams<ConstraintLayout.LayoutParams> {
setMargins(0, 8F.dip2px(), 8F.dip2px(), 8F.dip2px())
}
})
} else {
binding.subjectTab.addOnTabSelectedListener(object : OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {}
override fun onTabUnselected(tab: TabLayout.Tab?) {
tab?.customView?.findViewById<CheckedTextView>(R.id.tab_title)?.isChecked = false
}
override fun onTabSelected(tab: TabLayout.Tab?) {
tab?.customView?.findViewById<CheckedTextView>(R.id.tab_title)?.isChecked = true
}
})
}
}
}
private fun provideTabView(title: String): View {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.tab_item_ranking_home, null)
val tabTitle = view.findViewById<View>(R.id.tab_title)
if (tabTitle is CheckedTextView) {
tabTitle.text = title
if (tabStyle == TabStyle.COLUMN_COLLECTION_MAIN_WRAPPER) {
val tabBinding = TabItemMainBinding.inflate(LayoutInflater.from(requireContext()))
tabBinding.titleTv.run {
visibility = View.VISIBLE
text = title
textSize = 16F
setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext()))
}
tabBinding.invisibleTitleTv.run {
visibility = View.INVISIBLE
text = title
textSize = 16F
}
tabBindingList.add(tabBinding)
return tabBinding.root
} else {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.tab_item_subject_tab, null)
val tabTitle = view.findViewById<View>(R.id.tab_title)
if (tabTitle is CheckedTextView) {
tabTitle.text = title
}
return view
}
return view
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
for (i in 0 until mBinding.subjectTab.tabCount) {
val tab = mBinding.subjectTab.getTabAt(i) ?: continue
tab.customView?.background =
if (tab.isSelected) R.drawable.border_round_theme_14.toDrawable(requireContext()) else R.drawable.border_round_eee_14.toDrawable(
requireContext()
)
tab.customView?.findViewById<TextView>(R.id.tab_title)
?.setTextColor(
if (tab.isSelected) com.gh.gamecenter.common.R.color.text_theme.toColor(requireContext()) else com.gh.gamecenter.common.R.color.text_primary.toColor(
requireContext()
)
)
when (tabStyle) {
TabStyle.COLUMN_COLLECTION_NORMAL -> {
binding.subjectTabIndicator.updateIndicatorColor(com.gh.gamecenter.common.R.color.primary_theme)
binding.subjectTab.setTabTextColors(com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext()), com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
}
TabStyle.COLUMN_COLLECTION_MAIN_WRAPPER -> updateTabStyle(binding.subjectViewpager.currentItem, 0F)
TabStyle.SUBJECT_NORMAL -> {
for (i in 0 until binding.subjectTab.tabCount) {
val tab = binding.subjectTab.getTabAt(i) ?: continue
tab.customView?.findViewById<CheckedTextView>(R.id.tab_title)?.run {
background = R.drawable.subject_tab_background_selector.toDrawable(context)
setTextColor(ContextCompat.getColorStateList(context, com.gh.gamecenter.common.R.color.text_tabbar_style))
}
}
}
}
settingBinding?.run {
saveTv.background = com.gh.gamecenter.common.R.drawable.button_blue_oval.toDrawable(requireContext())
cancelTv.background = com.gh.gamecenter.feature.R.drawable.button_round_gray_light.toDrawable(requireContext())
cancelTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext()))
followAdapter.notifyItemRangeChanged(0, followAdapter.itemCount)
moreAdapter.notifyItemRangeChanged(0, moreAdapter.itemCount)
}
}
private fun updateTabStyle(selectedPosition: Int, positionOffset: Float) {
if (tabBindingList.isEmpty()) return
val currentTabDefaultColor = com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext())
val currentTabSelectedColor = com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext())
val prePosition = selectedPosition - 1
val nextPosition = selectedPosition + 1
// positionOffset 小于零,表示 indicator 当前位置处于选中的 tab 的左边
val indicatorOnLeft = positionOffset < 0F
val selectedTabBinding = tabBindingList.safelyGetInRelease(selectedPosition) ?: return
// 前一个 tab、当前选中的 tab、后一个 tab 的显示比例
val preScaleRatio = 1 + abs(positionOffset) / 4
val selectedScaleRatio = 1 + (1 - abs(positionOffset)) / 4
val nextScaleRatio = 1 + positionOffset / 4
// 处理前一个 tab
if (prePosition != -1) {
tabBindingList[prePosition].run {
if (indicatorOnLeft) {
titleTv.scaleX = preScaleRatio
titleTv.scaleY = preScaleRatio
titleTv.setTextColor(
ColorUtils.blendARGB(
currentTabDefaultColor,
currentTabSelectedColor,
abs(positionOffset)
)
)
} else {
titleTv.setTextColor(currentTabDefaultColor)
}
}
}
// 更新当前选中的 tab
selectedTabBinding.run {
titleTv.scaleX = selectedScaleRatio
titleTv.scaleY = selectedScaleRatio
titleTv.setTextColor(
ColorUtils.blendARGB(
currentTabDefaultColor,
currentTabSelectedColor,
1 - abs(positionOffset)
)
)
}
// 处理后一个 tab
if (nextPosition < tabBindingList.size) {
tabBindingList[nextPosition].run {
if (!indicatorOnLeft) {
titleTv.scaleX = nextScaleRatio
titleTv.scaleY = nextScaleRatio
titleTv.setTextColor(
ColorUtils.blendARGB(
currentTabDefaultColor,
currentTabSelectedColor,
positionOffset
)
)
} else {
titleTv.setTextColor(currentTabDefaultColor)
}
}
}
// 多 tab 切换的时候可能会出现某些 tab 的文字没有回归到原始大小的问题的问题 (positionOffset 不保证连续)
for ((index, tabBinding) in tabBindingList.withIndex()) {
if (index != prePosition && index != selectedPosition && index != nextPosition) {
if (tabBinding.titleTv.scaleX != 1F) {
tabBinding.titleTv.scaleX = 1F
tabBinding.titleTv.scaleY = 1F
}
tabBinding.titleTv.setTextColor(currentTabDefaultColor)
if (tabBinding.titleIv.scaleX != 1F) {
tabBinding.titleIv.scaleX = 1F
tabBinding.titleIv.scaleY = 1F
}
}
if (index == selectedPosition) {
if (positionOffset == 0F) {
tabBinding.titleTv.setTextColor(currentTabSelectedColor)
}
tabBinding.titleTv.setTypeface(tabBinding.titleTv.typeface, Typeface.BOLD)
} else {
if (positionOffset == 0F) {
tabBinding.titleTv.setTextColor(currentTabDefaultColor)
}
tabBinding.titleTv.setTypeface(null, Typeface.NORMAL)
}
}
}
private fun showSettingGuideIfNeeded(listener: OnTabSelectedListener) {
if (isCustomEnabled && SPUtils.getBoolean(Constants.SP_SHOW_COLUMN_COLLECTION_CUSTOM_TAB_GUIDE, true)) {
binding.skipView.setOnClickListener {
dismissSettingGuide()
SensorsBridge.trackColumnCollectionClick(
location = "合集详情",
columnCollectionName = collectionName,
columnCollectionId = collectionId,
text = "新手引导-取消",
columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[collectionStyle]
?: collectionStyle
)
}
binding.settingClickView.setOnClickListener {
dismissSettingGuide()
showSettingView()
SensorsBridge.trackColumnCollectionClick(
location = "合集详情",
columnCollectionName = collectionName,
columnCollectionId = collectionId,
text = "新手引导-立即设置",
columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[collectionStyle]
?: collectionStyle
)
}
binding.settingGuideContainer.setOnClickListener {
dismissSettingGuide()
SensorsBridge.trackColumnCollectionClick(
location = "合集详情",
columnCollectionName = collectionName,
columnCollectionId = collectionId,
text = "新手引导-关闭引导",
columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[collectionStyle]
?: collectionStyle
)
}
mBaseHandler.post {
binding.settingGuideContainer.isVisible = true
}
SPUtils.setBoolean(Constants.SP_SHOW_COLUMN_COLLECTION_CUSTOM_TAB_GUIDE, false)
binding.subjectTab.removeOnTabSelectedListener(listener)
}
}
private fun dismissSettingGuide() {
binding.settingGuideIv.animate()?.alpha(0F)?.setDuration(200L)?.doOnEnd {
binding.settingGuideContainer.isVisible = false
}?.start()
}
private fun showSettingView() {
if (isSettingAnimating) return
binding.background.setOnClickListener {
showDismissSettingDialogIfNeeded()
}
settingBinding = LayoutColumnCollectionSettingBinding.inflate(layoutInflater, null, false).apply {
followRv.isNestedScrollingEnabled = false
moreRv.isNestedScrollingEnabled = false
columnCollectionTabViewModel?.followListLiveData?.value?.let {
followAdapter.submitList(it) {
followRv.adapter = followAdapter
}
} ?: run {
followRv.adapter = followAdapter
}
columnCollectionTabViewModel?.moreListLiveData?.value?.let {
moreAdapter.submitList(it) {
moreRv.adapter = moreAdapter
}
} ?: run {
moreRv.adapter = moreAdapter
}
followRv.layoutManager = FlexboxLayoutManager(requireContext()).apply { justifyContent = JustifyContent.FLEX_START }
moreRv.layoutManager = FlexboxLayoutManager(requireContext()).apply { justifyContent = JustifyContent.FLEX_START }
itemTouchHelper.attachToRecyclerView(followRv)
moreContainer.isVisible = !columnCollectionTabViewModel?.moreListLiveData?.value.isNullOrEmpty()
saveTv.setOnClickListener {
dismissSettingView {
saveCustomTab()
}
}
cancelTv.setOnClickListener {
dismissSettingView()
}
root.setOnClickListener {}
}
binding.settingContainer.addView(
settingBinding!!.root,
FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)
)
binding.settingContainer.post {
val height = binding.settingContainer.height
binding.background.visibility = View.VISIBLE
AnimatorSet().apply {
val translateAnimator = ValueAnimator.ofFloat(0F, height.toFloat()).apply {
addUpdateListener { va -> binding.settingContainer.translationY = (va.animatedValue as Float) }
}
val alphaAnimator = ValueAnimator.ofFloat(0F, 0.2F).apply {
addUpdateListener { va -> binding.background.alpha = (va.animatedValue as Float) }
}
playTogether(translateAnimator, alphaAnimator)
duration = ANIMATOR_DURATION
doOnStart {
isSettingAnimating = true
}
doOnEnd {
ConstraintSet().apply {
clone(binding.root)
clear(binding.settingContainer.id, ConstraintSet.BOTTOM)
connect(binding.settingContainer.id, ConstraintSet.TOP, binding.subjectTabContainer.id, ConstraintSet.TOP)
}.applyTo(binding.root)
binding.settingContainer.translationY = 0F
isSettingAnimating = false
}
}.start()
}
}
private fun saveCustomTab() {
if (followAdapter.currentList.isEmpty()) {
toast("至少添加1个内容哦~")
return
}
SensorsBridge.trackEvent("ColumnCollectionCustomSetting",
"column_collection_name", collectionName,
"column_collection_id", collectionId,
"original_text", columnCollectionTabViewModel?.followListLiveData?.value?.joinToString("") { it.subjectName ?: "" } ?: "",
"save_text", followAdapter.currentList.joinToString("") { it.subjectName ?: "" },
)
columnCollectionTabViewModel?.saveCustomTab(followAdapter.currentList)
}
private fun showDismissSettingDialogIfNeeded() {
if (followAdapter.currentList == columnCollectionTabViewModel?.followListLiveData?.value) {
dismissSettingView()
} else {
DialogHelper.showDialog(
requireContext(),
title = "提示",
content = "是否保存当前自定义设置",
confirmText = "保存设置",
cancelText = "退出",
confirmClickCallback = {
dismissSettingView {
saveCustomTab()
}
},
cancelClickCallback = {
dismissSettingView()
},
extraConfig = Config(centerTitle = true, centerContent = true)
)
}
}
private fun dismissSettingView(doOnEnd: (() -> Unit)? = null) {
if (settingBinding == null || isSettingAnimating) return
val height = binding.settingContainer.height
AnimatorSet().apply {
val translateAnimator = ValueAnimator.ofFloat(0F, -height.toFloat()).apply {
addUpdateListener { va -> binding.settingContainer.translationY = (va.animatedValue as Float) }
}
val alphaAnimator = ValueAnimator.ofFloat(0.2F, 0F).apply {
addUpdateListener { va -> binding.background.alpha = (va.animatedValue as Float) }
}
playTogether(translateAnimator, alphaAnimator)
duration = ANIMATOR_DURATION
doOnStart {
isSettingAnimating = true
}
doOnEnd {
binding.background.visibility = View.GONE
binding.settingContainer.removeAllViews()
binding.settingContainer.translationY = 0F
ConstraintSet().apply {
clone(binding.root)
clear(binding.settingContainer.id, ConstraintSet.TOP)
connect(binding.settingContainer.id, ConstraintSet.BOTTOM, binding.subjectTabContainer.id, ConstraintSet.TOP)
}.applyTo(binding.root)
settingBinding = null
isSettingAnimating = false
doOnEnd?.invoke()
}
}.start()
}
override fun onBackPressed(): Boolean {
if (binding.settingGuideContainer.isVisible) {
binding.settingGuideContainer.performClick()
return true
}
if (settingBinding != null) {
showDismissSettingDialogIfNeeded()
return true
}
return super.onBackPressed()
}
enum class TabStyle {
/**
* 专题详情样式
*/
SUBJECT_NORMAL,
/**
* 专题合集-关联首页底部Tab样式
*/
COLUMN_COLLECTION_MAIN_WRAPPER,
/**
* 专题合集-常规样式
*/
COLUMN_COLLECTION_NORMAL,
}
companion object {
private const val ANIMATOR_DURATION = 200L
}
}

View File

@ -4,19 +4,24 @@ import android.os.Bundle
import android.text.TextUtils
import android.view.MotionEvent
import android.view.View
import androidx.core.view.isVisible
import androidx.recyclerview.widget.GridLayoutManager
import com.gh.common.view.ConfigFilterView
import com.gh.common.view.ConfigFilterView.SortType
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.base.fragment.ToolbarController
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.view.GridSpacingItemDecoration
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.databinding.FragmentSubjectTiledBinding
import com.gh.gamecenter.entity.SubjectData
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.subject.SubjectListFragment
import com.google.android.material.appbar.AppBarLayout
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import kotlin.math.abs
@ -32,6 +37,28 @@ class SubjectTileFragment : BaseFragment<Any>() {
private var mIsTouchScreen: Boolean = false
private var mMaxSize = ""
private var mMinSize = ""
private var mSelectedFilterSize = SubjectSettingEntity.Size(text = "全部大小")
private val finalFilter: String
get() {
var filter = if (mSelectedTypeName == "全部") {
// 全部的时候添加头图
UrlFilterUtils.getFilterQuery("tags", mSelectedTypeName, "type", mSelectedTypeName)
} else {
UrlFilterUtils.getFilterQuery("tags", mSelectedTypeName)
}
if (mMinSize.isNotEmpty() && mMinSize != "-1") {
filter += ",${UrlFilterUtils.getFilterQuery("min_size", mMinSize)}"
}
if (mMaxSize.isNotEmpty() && mMaxSize != "-1") {
filter += ",${UrlFilterUtils.getFilterQuery("max_size", mMaxSize)}"
}
return filter
}
override fun getLayoutId() = 0
override fun getInflatedLayout() = mBinding.root
@ -40,45 +67,75 @@ class SubjectTileFragment : BaseFragment<Any>() {
// 初始化数据
mSubjectData = arguments?.getParcelable(EntranceConsts.KEY_SUBJECT_DATA) ?: return
mSettingsEntity =
arguments?.getParcelable(SubjectSettingEntity::class.java.simpleName) ?: return
arguments?.getParcelable(EntranceConsts.KEY_SUBJECT_SETTING_DATA) ?: return
mSelectedTypeName = "全部"
mSubjectData.filter = UrlFilterUtils.getFilterQuery("type", "全部")
mSubjectData.sort = UrlFilterUtils.getFilterQuery("position", "1")
mBinding.subjectFilterContainer.visibility =
if (mSettingsEntity.filter == "on") View.VISIBLE else View.GONE
// 初始化顶部类型列表
val layoutManager = object : GridLayoutManager(context, 4) {
override fun canScrollVertically(): Boolean {
return false
mBinding.subjectTypeList.goneIf(mSettingsEntity.typeEntity.content.size <= 1) {
// 初始化顶部类型列表
val layoutManager = object : GridLayoutManager(context, 4) {
override fun canScrollVertically(): Boolean {
return false
}
}
}
mBinding.subjectTypeList.isNestedScrollingEnabled = false
mBinding.subjectTypeList.layoutManager = layoutManager
mBinding.subjectTabbarHottest.setOnClickListener(this)
mBinding.subjectTabbarNewest.setOnClickListener(this)
if (mSettingsEntity.typeEntity.content.size > 1) {
mBinding.subjectTypeList.isNestedScrollingEnabled = false
mBinding.subjectTypeList.layoutManager = layoutManager
mBinding.subjectTypeList.addItemDecoration(GridSpacingItemDecoration(4, 8F.dip2px(), false))
val adapter = SubjectTypeListAdapter(requireContext(), mItemClickListener = {
mSelectedTypeName = it
if (it == "全部") {
// 全部的时候添加头图
mSubjectData.filter = UrlFilterUtils.getFilterQuery("tags", it, "type", it)
} else {
mSubjectData.filter = UrlFilterUtils.getFilterQuery("tags", it)
}
mSubjectData.filter = finalFilter
mSubjectData.tag = it
loadData()
}, mGameType = mSettingsEntity.typeEntity.content)
mBinding.subjectTypeList.adapter = adapter
}
mBinding.filterView.goneIf(!mSettingsEntity.isFilterEnabled) {
mBinding.filterView.run {
initSubjectFilterView(mSettingsEntity)
setOnConfigSetupListener(object :
ConfigFilterView.OnConfigFilterSetupListener {
override fun onShowSortSize() {}
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
mMinSize = sortSize.min.toString()
mMaxSize = sortSize.max.toString()
mSelectedFilterSize = sortSize
mSubjectData.filter = finalFilter
loadData()
}
override fun onSetupSortType(sortType: SortType) {
val clickTv = when (sortType) {
SortType.RATING -> ratingTv
SortType.NEWEST -> newestTv
SortType.UPDATE -> updateTv
else -> recommendedTv
}
val sort = when (clickTv.text) {
"更新" -> UrlFilterUtils.getFilterQuery("update_time", "-1")
"最新" -> UrlFilterUtils.getFilterQuery("publish", "-1")
"评分" -> UrlFilterUtils.getFilterQuery("star", "-1")
else -> UrlFilterUtils.getFilterQuery("position", "1")
}
if (mSubjectData.sort != sort) {
mSubjectData.sort = sort
loadData()
}
}
})
}
}
// 是否为专题合集详情子页面
val isColumnCollection = arguments?.getBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION) ?: false
if (!isColumnCollection) {
// Appbar 交互
mBinding.subjectAppbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
mBinding.subjectAppbar.addOnOffsetChangedListener { appBarLayout, verticalOffset ->
if (!TextUtils.isEmpty(mSubjectData.subjectName) && activity is ToolbarController) {
val totalScrollRange = appBarLayout.totalScrollRange
if (abs(verticalOffset) < totalScrollRange / 2) {
@ -94,7 +151,7 @@ class SubjectTileFragment : BaseFragment<Any>() {
)
}
}
})
}
}
loadData()
@ -116,23 +173,6 @@ class SubjectTileFragment : BaseFragment<Any>() {
}
}
override fun onClick(view: View) {
when (view.id) {
R.id.subject_tabbar_hottest -> {
mBinding.subjectTabbarHottest.isChecked = true
mBinding.subjectTabbarNewest.isChecked = false
mSubjectData.sort = UrlFilterUtils.getFilterQuery("position", "1")
loadData()
}
R.id.subject_tabbar_newest -> {
mBinding.subjectTabbarHottest.isChecked = false
mBinding.subjectTabbarNewest.isChecked = true
mSubjectData.sort = UrlFilterUtils.getFilterQuery("publish", "-1")
loadData()
}
}
}
fun loadData() {
val transaction = childFragmentManager.beginTransaction()
hideFragments(transaction)
@ -151,8 +191,10 @@ class SubjectTileFragment : BaseFragment<Any>() {
transaction.show(fragmentByTag)
} else {
fragmentByTag = SubjectListFragment()
val bundle = arguments ?: Bundle()
val bundle = arguments?.clone() as? Bundle ?: Bundle()
bundle.remove(EntranceConsts.KEY_SUBJECT_SETTING_DATA)
bundle.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, mSubjectData)
bundle.putString(EntranceConsts.KEY_SUBJECT_SORT_SIZE, mSelectedFilterSize.text)
fragmentByTag.arguments = bundle
transaction.add(R.id.subject_content_rl, fragmentByTag, fmTag)
}
@ -161,6 +203,13 @@ class SubjectTileFragment : BaseFragment<Any>() {
}
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
if (mBinding.filterView.isVisible) {
mBinding.filterView.updateAllTextView()
}
}
companion object {
private const val OPEN_APPBAR = "openAppBar"
}

View File

@ -1,14 +1,16 @@
package com.gh.gamecenter.subject.tile
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.CheckedTextView
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.toDrawable
import com.lightgame.adapter.BaseRecyclerAdapter
class SubjectTypeListAdapter(
@ -20,28 +22,26 @@ class SubjectTypeListAdapter(
private var mCurType = "全部"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubjectTypeViewHolder {
return SubjectTypeViewHolder(mLayoutInflater.inflate(R.layout.item_filter_size, parent, false))
return SubjectTypeViewHolder(mLayoutInflater.inflate(R.layout.subject_rows_label_item, parent, false))
}
override fun onBindViewHolder(holder: SubjectTypeViewHolder, position: Int) {
holder.itemView.layoutParams = holder.itemView.layoutParams.apply {
holder.type.updateLayoutParams<ViewGroup.MarginLayoutParams> {
width = ViewGroup.LayoutParams.MATCH_PARENT
}
holder.type.background = R.drawable.subject_label_background_style.toDrawable(mContext)
holder.type.setTextColor(ContextCompat.getColorStateList(mContext, R.color.subject_label_text_style))
holder.type.text = mGameType[position]
if (!TextUtils.isEmpty(mCurType) && mCurType == mGameType[position]) {
holder.type.background = ContextCompat.getDrawable(mContext, R.drawable.bg_tag_text)
holder.type.setTextColor(Color.WHITE)
} else {
holder.type.background = null
holder.type.setTextColor(ContextCompat.getColor(mContext, com.gh.gamecenter.common.R.color.text_757575))
}
val isChecked = !TextUtils.isEmpty(mCurType) && mCurType == mGameType[position]
holder.type.isChecked = isChecked
holder.type.setTypeface(Typeface.DEFAULT, if (isChecked) Typeface.BOLD else Typeface.NORMAL)
holder.type.setOnClickListener {
if (holder.adapterPosition == -1) return@setOnClickListener
if (holder.bindingAdapterPosition == -1) return@setOnClickListener
mCurType = mGameType[holder.adapterPosition]
mItemClickListener.invoke(mGameType[holder.adapterPosition])
mCurType = mGameType[holder.bindingAdapterPosition]
mItemClickListener.invoke(mGameType[holder.bindingAdapterPosition])
notifyDataSetChanged()
}
}
@ -51,6 +51,6 @@ class SubjectTypeListAdapter(
}
inner class SubjectTypeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val type by lazy { itemView.findViewById<TextView>(R.id.size_tv) }
val type: CheckedTextView by lazy { itemView.findViewById(R.id.label) }
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/text_tertiary" android:state_checked="false" />
<item android:color="@color/text_theme" android:state_checked="true" />
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/text_757575" android:state_checked="false" />
<item android:color="@color/white" android:state_checked="true" />
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="@color/bg_EBF5FF" />
<solid android:color="@color/primary_theme_10" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="@color/primary_theme_10"
android:startColor="@color/primary_theme_40" />
<corners android:radius="2dp" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="1dp"
android:color="@color/ui_background" />
<corners android:topRightRadius="8dp" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="28dp" />
<stroke
android:width="1dp"
android:color="@color/ui_divider_2" />
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="28dp" />
<stroke
android:width="1dp"
android:color="@color/primary_theme_20" />
<gradient
android:endColor="@color/theme_alpha_4"
android:startColor="@color/theme_alpha_8" />
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="100dp"/>
<solid android:color="@color/white_alpha_20"/>
</shape>

View File

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/title" />
<corners android:radius="4dp" />
<corners android:radius="6dp" />
<solid android:color="@color/primary_theme_10" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_shape_primary_theme_alpha_10_radius_6" android:state_checked="true" />
<item android:drawable="@drawable/bg_shape_primary_theme_alpha_10_radius_6" android:state_focused="true" />
<item android:drawable="@drawable/bg_shape_f8_radius_6" />
</selector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="6dp"
android:viewportWidth="8"
android:viewportHeight="6">
<path
android:strokeWidth="1"
android:pathData="M7,1.5L4,4.5L1,1.5"
android:strokeLineJoin="round"
android:fillColor="#00000000"
android:strokeColor="@color/text_primary"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="6dp"
android:viewportWidth="8"
android:viewportHeight="6">
<path
android:strokeWidth="1"
android:pathData="M7,4.5L4,1.5L1,4.5"
android:strokeLineJoin="round"
android:fillColor="#00000000"
android:strokeColor="@color/text_primary"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="6dp"
android:viewportWidth="8"
android:viewportHeight="6">
<path
android:strokeWidth="1"
android:pathData="M7,4.5L4,1.5L1,4.5"
android:strokeLineJoin="round"
android:fillColor="#00000000"
android:strokeColor="@color/text_primary"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="8dp"
android:viewportWidth="8"
android:viewportHeight="8">
<group>
<clip-path
android:pathData="M0,0h8v8h-8z"/>
<path
android:pathData="M4,0C4.2761,0 4.5,0.2239 4.5,0.5V3.5H7.5C7.7761,3.5 8,3.7239 8,4C8,4.2761 7.7761,4.5 7.5,4.5H4.5V7.5C4.5,7.7761 4.2761,8 4,8C3.7239,8 3.5,7.7761 3.5,7.5V4.5H0.5C0.2239,4.5 0,4.2761 0,4C0,3.7239 0.2239,3.5 0.5,3.5H3.5V0.5C3.5,0.2239 3.7239,0 4,0Z"
android:fillColor="@color/text_secondary"
android:fillType="evenOdd"/>
</group>
</vector>

View File

@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<group>
<clip-path
android:pathData="M0,0h18v18h-18z"/>
<path
android:pathData="M1.875,9C1.875,5.065 5.065,1.875 9,1.875C12.935,1.875 16.125,5.065 16.125,9C16.125,12.935 12.935,16.125 9,16.125C5.065,16.125 1.875,12.935 1.875,9Z"
android:fillColor="#2496FF"
android:fillType="evenOdd"/>
<path
android:pathData="M6.75,9L8.25,10.5L11.25,7.5"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</group>
</vector>

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<group>
<clip-path
android:pathData="M0,0h16v16h-16z"/>
<path
android:pathData="M8.5,4.667C8.5,3.102 9.769,1.833 11.333,1.833C12.898,1.833 14.167,3.102 14.167,4.667C14.167,6.232 12.898,7.5 11.333,7.5C9.769,7.5 8.5,6.232 8.5,4.667ZM11.333,3.167C10.505,3.167 9.833,3.838 9.833,4.667C9.833,5.495 10.505,6.167 11.333,6.167C12.162,6.167 12.833,5.495 12.833,4.667C12.833,3.838 12.162,3.167 11.333,3.167ZM4,2C2.895,2 2,2.895 2,4V5.333C2,6.438 2.895,7.333 4,7.333H5.333C6.438,7.333 7.333,6.438 7.333,5.333V4C7.333,2.895 6.438,2 5.333,2H4ZM3.333,4C3.333,3.632 3.632,3.333 4,3.333H5.333C5.702,3.333 6,3.632 6,4V5.333C6,5.702 5.702,6 5.333,6H4C3.632,6 3.333,5.702 3.333,5.333V4ZM4,8.667C2.895,8.667 2,9.562 2,10.667V12C2,13.105 2.895,14 4,14H5.333C6.438,14 7.333,13.105 7.333,12V10.667C7.333,9.562 6.438,8.667 5.333,8.667H4ZM3.333,10.667C3.333,10.299 3.632,10 4,10H5.333C5.702,10 6,10.299 6,10.667V12C6,12.368 5.702,12.667 5.333,12.667H4C3.632,12.667 3.333,12.368 3.333,12V10.667ZM10.667,8.667C9.562,8.667 8.667,9.562 8.667,10.667V12C8.667,13.105 9.562,14 10.667,14H12C13.105,14 14,13.105 14,12V10.667C14,9.562 13.105,8.667 12,8.667H10.667ZM10,10.667C10,10.299 10.299,10 10.667,10H12C12.368,10 12.667,10.299 12.667,10.667V12C12.667,12.368 12.368,12.667 12,12.667H10.667C10.299,12.667 10,12.368 10,12V10.667Z"
android:fillColor="@color/text_primary"
android:fillType="evenOdd"/>
</group>
</vector>

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="8dp"
android:viewportWidth="8"
android:viewportHeight="8">
<group>
<clip-path
android:pathData="M0,0h8v8h-8z"/>
<path
android:pathData="M0.6464,0.6464C0.8417,0.4512 1.1583,0.4512 1.3535,0.6464L4,3.2929L6.6465,0.6464C6.8417,0.4512 7.1583,0.4512 7.3535,0.6464C7.5488,0.8417 7.5488,1.1583 7.3535,1.3535L4.7071,4L7.3535,6.6465C7.5488,6.8417 7.5488,7.1583 7.3535,7.3535C7.1583,7.5488 6.8417,7.5488 6.6465,7.3535L4,4.7071L1.3535,7.3535C1.1583,7.5488 0.8417,7.5488 0.6464,7.3535C0.4512,7.1583 0.4512,6.8417 0.6464,6.6465L3.2929,4L0.6464,1.3535C0.4512,1.1583 0.4512,0.8417 0.6464,0.6464Z"
android:fillColor="@color/text_secondary"
android:fillType="evenOdd"/>
</group>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="14"
android:viewportHeight="14">
<path
android:pathData="M7,14C10.866,14 14,10.866 14,7C14,3.134 10.866,0 7,0C3.134,0 0,3.134 0,7C0,10.866 3.134,14 7,14ZM5.53,4.47C5.237,4.177 4.763,4.177 4.47,4.47C4.177,4.763 4.177,5.237 4.47,5.53L5.939,7L4.47,8.47C4.177,8.763 4.177,9.237 4.47,9.53C4.763,9.823 5.237,9.823 5.53,9.53L7,8.061L8.47,9.53C8.763,9.823 9.237,9.823 9.53,9.53C9.823,9.237 9.823,8.763 9.53,8.47L8.061,7L9.53,5.53C9.823,5.237 9.823,4.763 9.53,4.47C9.237,4.177 8.763,4.177 8.47,4.47L7,5.939L5.53,4.47Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/ui_divider" />
</shape>

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:strokeWidth="1"
android:pathData="M4,1.5L8,1.5A1.5,1.5 0,0 1,9.5 3L9.5,9A1.5,1.5 0,0 1,8 10.5L4,10.5A1.5,1.5 0,0 1,2.5 9L2.5,3A1.5,1.5 0,0 1,4 1.5z"
android:fillColor="#00000000"
android:strokeColor="@color/text_tertiary"/>
<path
android:pathData="M5.5,3L6.5,3A0.5,0.5 0,0 1,7 3.5L7,3.5A0.5,0.5 0,0 1,6.5 4L5.5,4A0.5,0.5 0,0 1,5 3.5L5,3.5A0.5,0.5 0,0 1,5.5 3z"
android:fillColor="@color/text_tertiary"/>
</vector>

View File

@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:strokeWidth="1"
android:pathData="M7.5,5L5.5,7.5L5,5H4.5"
android:strokeLineJoin="round"
android:fillColor="#00000000"
android:strokeColor="@color/text_tertiary"
android:strokeLineCap="round"/>
<path
android:strokeWidth="1"
android:pathData="M3.5,2H8.5C9.328,2 10,2.672 10,3.5V8.5C10,9.328 9.328,10 8.5,10H3.5C2.672,10 2,9.328 2,8.5V3.5C2,2.672 2.672,2 3.5,2Z"
android:fillColor="#00000000"
android:strokeColor="@color/text_tertiary"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="8dp"
android:viewportWidth="8"
android:viewportHeight="8">
<group>
<clip-path
android:pathData="M0,0h8v8h-8z"/>
<path
android:pathData="M0.6464,0.6465C0.8417,0.4512 1.1583,0.4512 1.3535,0.6465L4,3.2929L6.6465,0.6465C6.8417,0.4512 7.1583,0.4512 7.3535,0.6465C7.5488,0.8417 7.5488,1.1583 7.3535,1.3536L4.7071,4L7.3535,6.6465C7.5488,6.8417 7.5488,7.1583 7.3535,7.3536C7.1583,7.5488 6.8417,7.5488 6.6465,7.3536L4,4.7071L1.3535,7.3536C1.1583,7.5488 0.8417,7.5488 0.6464,7.3536C0.4512,7.1583 0.4512,6.8417 0.6464,6.6465L3.2929,4L0.6464,1.3536C0.4512,1.1583 0.4512,0.8417 0.6464,0.6465Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</group>
</vector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/ui_container_1" />
<corners android:radius="6dp"/>
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/primary_theme_10" />
<corners android:radius="6dp"/>
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="@color/ui_surface_0"
android:startColor="@color/ui_surface" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="@color/ui_surface"
android:startColor="@color/ui_surface_0" />
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_shape_primary_theme_alpha_10_radius_6" android:state_checked="true" />
<item android:drawable="@drawable/bg_shape_primary_theme_alpha_10_radius_6" android:state_focused="true" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_round_theme_alpha_10" android:state_pressed="true" />
<item android:drawable="@drawable/button_round_theme_alpha_10" android:state_focused="true" />
<item android:drawable="@drawable/button_round_theme_alpha_10" android:state_selected="true" />
<item android:drawable="@drawable/button_round_theme_alpha_10" android:state_checked="true" />
<item android:drawable="@drawable/bg_shape_ui_container_1_radius_999" />
</selector>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/subject_tab_down" android:state_selected="true" />
<item android:drawable="@color/white" android:state_focused="false" />
</selector>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_tag_text" android:state_checked="true" />
<item android:drawable="@drawable/bg_tag_text" android:state_focused="true" />
</selector>

View File

@ -2,20 +2,19 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="24dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical">
android:paddingBottom="24dp">
<TextView
android:id="@+id/title"
style="@style/TextHeadline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textColor="@color/text_primary"
android:textStyle="bold"
android:textSize="14sp"
tools:text="玩法" />
<androidx.recyclerview.widget.RecyclerView

View File

@ -1,34 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="80dp"
android:layout_height="52dp">
<ImageView
<View
android:id="@+id/selected_tag"
android:layout_width="14dp"
android:layout_width="0dp"
android:layout_height="6dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="36dp"
android:src="@drawable/ic_catalog_selected"
android:layout_marginTop="28dp"
android:background="@drawable/bg_category_selected_tag"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/catalog_name"
app:layout_constraintStart_toStartOf="@id/catalog_name"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/catalog_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_primary"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_max="0dp"
tools:text="精选" />
<ImageView
android:id="@+id/recommend_tag"
android:layout_width="16dp"
android:layout_height="12dp"
android:layout_marginLeft="52dp"
android:layout_marginStart="52dp"
android:layout_marginTop="16dp"
android:src="@drawable/ic_recommend"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,18 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/ui_surface">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/v_more_category"
android:layout_width="92dp"
android:layout_height="32dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_more_category_default"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/ui_divider" />
<TextView
android:id="@+id/tv_tag_number"
android:layout_width="12dp"
android:layout_height="12dp"
android:background="@drawable/background_shape_theme_radius_999"
android:gravity="center"
android:textColor="@color/text_aw_primary"
android:textSize="8sp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="@id/v_more_category"
app:layout_constraintTop_toTopOf="@id/v_more_category"
tools:text="5"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_more_category"
style="@style/TextCaption1B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:gravity="center"
android:text="@string/more_category"
app:drawableStartCompat="@drawable/ic_basic_classification"
app:layout_constraintBottom_toBottomOf="@id/v_more_category"
app:layout_constraintEnd_toEndOf="@id/v_more_category"
app:layout_constraintStart_toStartOf="@id/v_more_category"
app:layout_constraintTop_toTopOf="@id/v_more_category" />
<View
android:id="@+id/v_search_category"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="12dp"
android:background="@drawable/bg_shape_ui_container_2_radius_999"
app:layout_constraintBottom_toBottomOf="@id/v_more_category"
app:layout_constraintEnd_toStartOf="@id/v_more_category"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/v_more_category" />
<ImageView
android:id="@+id/iv_search_category"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginStart="12dp"
app:layout_constraintBottom_toBottomOf="@id/v_search_category"
app:layout_constraintStart_toStartOf="@id/v_search_category"
app:layout_constraintTop_toTopOf="@id/v_search_category"
app:srcCompat="@drawable/ic_search"
app:tint="@color/text_instance" />
<TextView
android:id="@+id/tv_search_category"
style="@style/TextBody2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/search_category_tags"
android:textColor="@color/text_instance"
app:layout_constraintBottom_toBottomOf="@id/v_search_category"
app:layout_constraintStart_toEndOf="@id/iv_search_category"
app:layout_constraintTop_toTopOf="@id/v_search_category" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/v_more_category">
<LinearLayout
android:id="@+id/categoryContainer"
@ -21,11 +96,24 @@
android:layout_marginTop="1dp"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/categoryRv"
<com.gh.gamecenter.common.view.RadiusCardView
android:id="@+id/cv_category"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@color/ui_background" />
android:layout_marginTop="8dp"
app:topRightRadius="8dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/categoryRv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_category_sidebar" />
</com.gh.gamecenter.common.view.RadiusCardView>
<FrameLayout
android:id="@+id/gamesContainer"
@ -51,62 +139,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<ImageView
android:visibility="gone"
android:id="@+id/guideContainer"
android:layout_width="187dp"
android:layout_height="63dp"
android:layout_marginTop="20dp"
android:layout_marginLeft="66dp"
android:src="@drawable/pic_category_guide" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/directoryContainer"
android:layout_width="260dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@color/ui_surface">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/directoryRv"
android:layout_above="@+id/bottom"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/bottom"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:id="@+id/resetTv"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:gravity="center"
android:background="@drawable/bg_shape_ui_container_2_radius_999"
android:textColor="@color/text_primary"
android:textSize="14sp"
android:text="重置" />
<TextView
android:id="@+id/confirmTv"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:layout_marginLeft="16dp"
android:gravity="center"
android:background="@drawable/bg_tag_text"
android:textColor="@color/white"
android:textSize="14sp"
android:text="确定" />
</LinearLayout>
</RelativeLayout>
</androidx.drawerlayout.widget.DrawerLayout>
<ImageView
android:id="@+id/guideContainer"
android:layout_width="244dp"
android:layout_height="57dp"
android:layout_marginEnd="8dp"
android:src="@drawable/pic_category_guide"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/v_more_category"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,37 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/ui_surface">
<FrameLayout
android:id="@+id/fl_tags_container"
android:layout_width="match_parent"
android:layout_height="40dp"
android:visibility="gone">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_tags"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:clipToPadding="false"
android:paddingHorizontal="12dp" />
<View
android:layout_width="16dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@drawable/shape_filtered_left" />
<View
android:layout_width="16dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@drawable/shape_filtered_right" />
</FrameLayout>
<com.gh.common.view.CategoryFilterView
android:id="@+id/filterContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.flexbox.FlexboxLayout
android:visibility="gone"
android:layout_below="@+id/filterContainer"
android:id="@+id/selectedCategoryContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingBottom="8dp"
app:flexWrap="wrap" />
android:layout_height="36dp"
android:layout_below="@id/fl_tags_container" />
<FrameLayout
android:id="@+id/list_skeleton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/selectedCategoryContainer" />
android:layout_below="@+id/filterContainer" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/list_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/selectedCategoryContainer">
android:layout_below="@+id/filterContainer">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_rv"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -7,9 +8,18 @@
android:id="@+id/contentContainer"
layout="@layout/fragment_list_base_skeleton" />
<com.gh.gamecenter.common.view.StatusBarView
android:id="@+id/statusBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/ui_surface"
android:visibility="gone"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
android:layout_below="@+id/statusBar" />
</RelativeLayout>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/ui_surface">
<com.gh.common.view.ConfigFilterView
android:id="@+id/filterView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/list_skeleton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/filterView"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/list_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/filterView">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include
layout="@layout/reuse_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<include layout="@layout/reuse_no_connection" />
<include layout="@layout/reuse_none_data" />
<include layout="@layout/reuse_data_exception" />
</RelativeLayout>

View File

@ -1,48 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/subject_tab_container"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_layout_height"
android:background="@color/ui_surface"
android:visibility="gone">
<androidx.viewpager.widget.ViewPager
android:id="@+id/subject_viewpager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/subject_tab_container" />
<!-- <View-->
<!-- android:id="@+id/subject_tab_line"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:layout_alignParentBottom="true"-->
<!-- android:background="@color/background"-->
<!-- android:visibility="gone" />-->
<View
android:id="@+id/background"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0"
android:background="@color/black"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/subject_tab_container"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/subject_tab_container"
android:layout_width="0dp"
android:layout_height="48dp"
android:background="@color/ui_surface"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/subject_viewpager"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<com.gh.gamecenter.common.view.TabIndicatorView
android:id="@+id/subject_tab_indicator"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="@dimen/default_tab_indicator_height"
android:layout_alignParentBottom="true" />
android:layout_marginEnd="8dp"
app:disableIndicatorScaling="true"
app:indicatorColor="@color/primary_theme"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/optionIv"
app:layout_constraintStart_toStartOf="parent"
app:layout_goneMarginEnd="0dp" />
<com.google.android.material.tabs.TabLayout
<com.gh.gamecenter.common.view.NoDefaultMinWidthTabLayout
android:id="@+id/subject_tab"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:tabIndicatorColor="@color/primary_theme"
app:tabMode="scrollable"
app:tabIndicatorHeight="0dp"
app:tabRippleColor="@color/transparent"
app:tabSelectedTextColor="@color/primary_theme"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:overScrollMode="never"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/optionIv"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginEnd="0dp"
app:tabIndicator="@null"
app:tabTextAppearance="@style/TabLayoutTextAppearance" />
app:tabIndicatorHeight="0dp"
app:tabMaxWidth="0dp"
app:tabMode="scrollable"
app:tabRippleColor="@color/transparent"
app:tabTextAppearance="@style/ColumnCollectionTabDetailTabTextAppearance" />
</RelativeLayout>
<ImageView
android:id="@+id/optionIv"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_shape_ui_container_2_radius_999"
android:padding="6dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_options"
tools:visibility="visible" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/subject_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<View
android:id="@+id/maskView"
android:layout_width="16dp"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_game_detail_tab_gradient_mask"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/optionIv"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:id="@+id/settingContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/subject_tab_container" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/settingGuideContainer"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<ImageView
android:id="@+id/settingGuideIv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="36dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/guide_column_collection_custom_setting" />
<View
android:id="@+id/skipView"
android:layout_width="24dp"
android:layout_height="32dp"
android:layout_marginEnd="64dp"
app:layout_constraintBottom_toBottomOf="@+id/settingClickView"
app:layout_constraintEnd_toStartOf="@+id/settingClickView"
app:layout_constraintTop_toTopOf="@+id/settingClickView" />
<View
android:id="@+id/settingClickView"
android:layout_width="80dp"
android:layout_height="32dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
app:layout_constraintBottom_toBottomOf="@+id/settingGuideIv"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -17,44 +18,17 @@
android:id="@+id/subject_type_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingTop="8dp"
android:paddingBottom="12dp"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed" />
<LinearLayout
android:id="@+id/subject_filter_container"
android:layout_width="wrap_content"
<com.gh.common.view.ConfigFilterView
android:id="@+id/filterView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginBottom="16dp"
android:background="@drawable/subject_tiled_tab_background">
<CheckedTextView
android:id="@+id/subject_tabbar_hottest"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:background="@drawable/subject_tiled_tab_selector"
android:checked="true"
android:gravity="center"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:text="最热"
android:textAlignment="center"
android:textColor="@color/subject_tiled_tab_color_selector"
android:textSize="12sp" />
<CheckedTextView
android:id="@+id/subject_tabbar_newest"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:background="@drawable/subject_tiled_tab_selector"
android:checked="false"
android:gravity="center"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:text="最新"
android:textAlignment="center"
android:textColor="@color/subject_tiled_tab_color_selector"
android:textSize="12sp" />
</LinearLayout>
android:visibility="gone"
tools:visibility="visible" />
</com.google.android.material.appbar.AppBarLayout>

View File

@ -415,15 +415,16 @@
android:id="@+id/device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/rating_phone_icon"
android:drawablePadding="4dp"
android:drawablePadding="2dp"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="1"
android:paddingRight="8dp"
android:paddingEnd="8dp"
android:textColor="@color/text_tertiary"
android:textSize="10sp"
android:textSize="@dimen/tag_text_size"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_phone"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/version"
@ -433,19 +434,27 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="小米2S" />
<TextView
<RadioButton
android:id="@+id/version"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:gravity="center"
android:layout_marginEnd="16dp"
android:button="@drawable/ic_version"
android:checked="false"
android:clickable="false"
android:gravity="center_vertical"
android:includeFontPadding="false"
android:lineSpacingExtra="2sp"
android:paddingStart="2dp"
android:textColor="@color/text_tertiary"
android:textSize="10sp"
android:textSize="@dimen/tag_text_size"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/commentCountTv"
app:layout_constraintStart_toEndOf="@+id/device"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginStart="8dp" />
app:layout_goneMarginStart="8dp"
tools:text="版本号" />
<TextView
android:id="@+id/likeCountTv"

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="8dp"
android:paddingBottom="8dp">
<com.gh.gamecenter.common.view.Chips
android:id="@+id/chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/size_tv"
android:layout_width="match_parent"
android:layout_height="28dp"
android:background="@drawable/config_filter_size_background_style"
android:gravity="center"
android:includeFontPadding="false"
android:text="全部大小"
android:textAlignment="center"
android:textColor="@color/subject_label_text_style"
android:textSize="@dimen/little_secondary_size" />

View File

@ -1,36 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/config_controller"
style="@style/filterRoot">
android:layout_width="match_parent"
android:layout_height="36dp">
<LinearLayout
android:id="@+id/container_category"
style="@style/filterItemContainer">
android:id="@+id/ll_type_container"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:gravity="center"
android:orientation="horizontal"
android:paddingHorizontal="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/catalog_tv"
style="@style/filterItem"
android:text="全部类别" />
android:id="@+id/tv_type"
style="@style/TextCaption1B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:gravity="center"
android:text="@string/popular"
android:textColor="@color/text_tertiary" />
<ImageView
android:id="@+id/iv_type_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/container_type"
style="@style/filterItemContainer">
android:id="@+id/ll_size_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="4dp"
android:gravity="center"
android:orientation="horizontal"
android:paddingHorizontal="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/type_tv"
style="@style/filterItem"
tools:text="热门推荐" />
android:id="@+id/tv_size"
style="@style/TextCaption1B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/size"
android:textColor="@color/text_tertiary" />
<ImageView
android:id="@+id/iv_size_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/container_size"
style="@style/filterItemContainer">
<TextView
android:id="@+id/size_tv"
style="@style/filterItem"
android:text="全部大小" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/TextCaption2"
android:layout_width="match_parent"
android:layout_height="28dp"
android:layout_margin="4dp"
android:gravity="center"
tools:text="全部大小" />

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_shape_white_radius_12_bottom_only">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
android:layout_height="328dp"
android:overScrollMode="never"
android:scrollbars="none"
app:layout_constraintBottom_toTopOf="@+id/actionContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/followTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="关注内容"
android:textColor="@color/text_primary"
android:textSize="@dimen/secondary_title_text_size"
android:textStyle="bold" />
<TextView
android:id="@+id/followDesTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="16dp"
android:text="长按拖动排序最少添加1个内容上限为15个"
android:textColor="@color/text_tertiary"
android:textSize="@dimen/little_secondary_size" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/followRv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:overScrollMode="never" />
<LinearLayout
android:id="@+id/moreContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="16dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/moreTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="更多内容"
android:textColor="@color/text_primary"
android:textSize="@dimen/secondary_title_text_size"
android:textStyle="bold" />
<TextView
android:id="@+id/moreDesTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="16dp"
android:text="点击可添加至关注内容"
android:textColor="@color/text_tertiary"
android:textSize="@dimen/little_secondary_size" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/moreRv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:overScrollMode="never" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/actionContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingLeft="16dp"
android:paddingTop="16dp"
android:paddingEnd="20dp"
android:paddingRight="16dp"
android:paddingBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/nestedScrollView">
<TextView
android:id="@+id/cancelTv"
style="@style/GrayLightButton"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:gravity="center"
android:text="取消" />
<TextView
android:id="@+id/saveTv"
style="@style/PrimaryButton"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginStart="12dp"
android:layout_weight="3"
android:gravity="center"
android:text="保存" />
</LinearLayout>
<View
android:id="@+id/topDivider"
android:layout_width="0dp"
android:layout_height="@dimen/divider_1px"
android:background="@color/ui_divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/bottomDivider"
android:layout_width="0dp"
android:layout_height="@dimen/divider_1px"
android:background="@color/ui_divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/actionContainer" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,47 +4,71 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/config_controller"
android:layout_width="match_parent"
android:layout_height="@dimen/default_filter_row_height"
android:layout_height="32dp"
android:background="@color/ui_surface"
android:paddingLeft="@dimen/default_filter_row_padding"
android:paddingRight="@dimen/default_filter_row_padding">
<TextView
android:id="@+id/updateTv"
style="@style/filterOptionItem"
android:background="@drawable/bg_tag_text"
style="@style/ConfigFilterOptionItem"
android:text="更新"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/dot1"
android:layout_width="2dp"
android:layout_height="2dp"
android:background="@drawable/ic_filter_dot"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/recommended_tv"
app:layout_constraintStart_toEndOf="@+id/updateTv"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/recommended_tv"
style="@style/filterOptionItem"
android:layout_marginLeft="@dimen/default_filter_row_item_margin"
style="@style/ConfigFilterOptionItem"
android:text="推荐"
android:textColor="@color/text_757575"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/updateTv"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginLeft="0dp" />
<View
android:id="@+id/dot2"
android:layout_width="2dp"
android:layout_height="2dp"
android:background="@drawable/ic_filter_dot"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/newest_tv"
app:layout_constraintStart_toEndOf="@+id/recommended_tv"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/newest_tv"
style="@style/filterOptionItem"
android:layout_marginLeft="@dimen/default_filter_row_item_margin"
style="@style/ConfigFilterOptionItem"
android:text="最新"
android:textColor="@color/text_757575"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/recommended_tv"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/dot3"
android:layout_width="2dp"
android:layout_height="2dp"
android:background="@drawable/ic_filter_dot"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/rating_tv"
app:layout_constraintStart_toEndOf="@+id/newest_tv"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/rating_tv"
style="@style/filterOptionItem"
android:layout_marginLeft="@dimen/default_filter_row_item_margin"
style="@style/ConfigFilterOptionItem"
android:text="评分"
android:textColor="@color/text_757575"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/newest_tv"
@ -55,14 +79,14 @@
<TextView
android:id="@+id/size_tv"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:drawableRight="@drawable/ic_filter_arrow_down"
android:drawablePadding="8dp"
android:layout_height="match_parent"
android:drawablePadding="4dp"
android:gravity="center_vertical"
android:text="全部大小"
android:textColor="@color/text_757575"
android:textColor="@color/text_tertiary"
android:textSize="12sp"
android:textStyle="bold"
app:drawableEndCompat="@drawable/ic_auxiliary_arrow_down_8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@ -1,19 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
android:orientation="vertical">
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/flexbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/ui_surface"
android:paddingBottom="8dp"
app:flexDirection="row"
app:flexWrap="wrap" />
android:background="@color/transparent">
<View
android:id="@+id/background"
@ -22,4 +12,17 @@
android:alpha="0.3"
android:background="@color/black" />
</LinearLayout>
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/flexbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_shape_white_radius_12_bottom_only"
android:paddingHorizontal="16dp"
android:paddingTop="8dp"
android:paddingBottom="16dp"
app:showDivider="middle"
app:dividerDrawable="@drawable/shape_flexbox_divider_w8_h8"
app:flexDirection="row"
app:flexWrap="wrap" />
</FrameLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_mask_20">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_size"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_shape_ui_surface_bottom_radius_12"
android:paddingHorizontal="12dp"
android:paddingTop="4dp"
android:paddingBottom="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_mask_20">
<RadioGroup
android:id="@+id/rg_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:background="@drawable/bg_shape_ui_surface_bottom_radius_12"
android:orientation="vertical">
<RadioButton
android:id="@+id/rb_popular"
style="@style/TextBody3"
android:layout_width="match_parent"
android:layout_height="40dp"
android:button="@null"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/popular"
android:textColor="@color/text_secondary" />
<RadioButton
android:id="@+id/rb_newest"
style="@style/TextBody3"
android:layout_width="match_parent"
android:layout_height="40dp"
android:button="@null"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/newest"
android:textColor="@color/text_secondary" />
<RadioButton
android:id="@+id/rb_score"
style="@style/TextBody3"
android:layout_width="match_parent"
android:layout_height="40dp"
android:button="@null"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/score"
android:textColor="@color/text_secondary" />
</RadioGroup>
</FrameLayout>

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_mask_20">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_content"
android:layout_width="match_parent"
android:layout_height="448dp"
android:background="@drawable/bg_shape_ui_surface_bottom_radius_12">
<com.gh.gamecenter.common.view.GhSearch
android:id="@+id/search_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:search_hint="@string/search_category_tags" />
<TextView
android:id="@+id/tv_reset"
style="@style/TextButton2"
android:layout_width="80dp"
android:layout_height="40dp"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_common_button_light_fill_gray"
android:gravity="center"
android:textColor="@color/text_secondary"
android:text="@string/reset"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/v_submit"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_common_button_fill_blue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_reset" />
<TextView
android:id="@+id/tv_submit"
style="@style/TextButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/servers_subscribed_game_unsubscribe_dialog_confirm"
android:textColor="@color/text_aw_primary"
app:layout_constraintBottom_toBottomOf="@id/v_submit"
app:layout_constraintEnd_toStartOf="@id/tv_selected_number"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@id/v_submit"
app:layout_constraintTop_toTopOf="@id/v_submit" />
<TextView
android:id="@+id/tv_selected_number"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="4dp"
android:background="@drawable/bg_search_category_number"
android:gravity="center"
android:textColor="@color/text_aw_primary"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="@id/v_submit"
app:layout_constraintEnd_toEndOf="@id/v_submit"
app:layout_constraintStart_toEndOf="@id/tv_submit"
app:layout_constraintTop_toTopOf="@id/v_submit"
tools:text="5" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_selected"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:clipToPadding="false"
android:paddingHorizontal="12dp"
app:layout_constraintBottom_toTopOf="@id/tv_reset"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/v_baseline"
android:layout_width="0dp"
android:layout_height="0.5dp"
android:layout_marginBottom="12dp"
android:background="@color/ui_divider"
app:layout_constraintBottom_toTopOf="@id/rv_selected"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_category"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingTop="16dp"
app:layout_constraintBottom_toTopOf="@id/v_baseline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_view" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_results"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/v_baseline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_view" />
<include
android:id="@+id/reuse_no_connection"
layout="@layout/reuse_none_data"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/v_baseline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -201,14 +201,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:drawableLeft="@drawable/rating_phone_icon"
android:drawablePadding="4dp"
android:drawablePadding="2dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingRight="8dp"
android:paddingEnd="8dp"
android:textColor="@color/text_tertiary"
android:textSize="10sp"
android:textSize="@dimen/tag_text_size"
app:drawableStartCompat="@drawable/ic_phone"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@+id/vote"
app:layout_constraintEnd_toStartOf="@+id/version"
@ -220,17 +220,23 @@
<TextView
android:id="@+id/version"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="center"
android:layout_marginEnd="16dp"
android:drawablePadding="2dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:lines="1"
android:textColor="@color/text_tertiary"
android:textSize="10sp"
android:textSize="@dimen/tag_text_size"
app:drawableStartCompat="@drawable/ic_version"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@+id/vote"
app:layout_constraintEnd_toStartOf="@+id/comment"
app:layout_constraintStart_toEndOf="@id/device"
app:layout_constraintTop_toTopOf="@+id/vote"
app:layout_goneMarginStart="16dp" />
app:layout_goneMarginStart="16dp"
tools:text="版本号版本号版本号版本号版本号版本号版本号版本号" />
<TextView
android:id="@+id/comment"

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<com.gh.gamecenter.common.view.Chips xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
app:chips_state="selected"
app:chips_style="small"
app:end_icon="@drawable/ic_x_size_8" />

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/TextBody2"
android:layout_width="match_parent"
android:layout_height="46dp"
android:gravity="center_vertical"
android:paddingHorizontal="16dp"
android:textColor="@color/text_secondary"
tools:text="标签名称">
</TextView>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
@ -7,25 +8,24 @@
android:background="@drawable/bg_shape_space_radius_8">
<TextView
android:id="@+id/name"
android:layout_width="60dp"
android:id="@+id/tv_name"
style="@style/TextCaption1"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_centerHorizontal="true"
android:layout_marginHorizontal="8dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/text_primary"
android:textSize="12sp"
tools:text="最多五个字啊啊啊" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="最多五个字" />
<ImageView
android:id="@+id/selectedIv"
android:layout_width="20dp"
android:layout_height="16dp"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:src="@drawable/ic_category_selected"
android:visibility="gone"
tools:visibility="visible" />
<ImageView
android:id="@+id/recommendIv"
@ -33,5 +33,7 @@
android:layout_height="12dp"
android:src="@drawable/ic_category_recommend"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,22 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="24dp"
android:overScrollMode="never"
android:scrollbars="none">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="40dp"
android:scrollbars="none">
<LinearLayout
android:id="@+id/label_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/default_filter_row_padding"
android:paddingRight="@dimen/default_filter_row_padding" />
</HorizontalScrollView>
</LinearLayout>
<LinearLayout
android:id="@+id/label_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/default_filter_row_padding"
android:paddingRight="@dimen/default_filter_row_padding" />
</HorizontalScrollView>

View File

@ -3,7 +3,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/label"
style="@style/filterOptionItem"
android:layout_marginRight="@dimen/default_filter_row_item_margin"
android:background="@drawable/text_blue_or_white_style"
android:textColor="@color/text_black_or_white_color_style"
android:background="@drawable/subject_label_background_style"
android:textColor="@color/subject_label_text_style"
tools:text="全部" />

View File

@ -2,17 +2,19 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/border_round_eee_14"
android:gravity="center">
<CheckedTextView
android:id="@+id/tab_title"
android:layout_width="68dp"
android:layout_height="28dp"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:background="@drawable/subject_tab_background_selector"
android:gravity="center"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:text="人气榜"
android:textAlignment="center"
android:textColor="@color/text_tabbar_video_style"
android:textColor="@color/text_tabbar_style"
android:textSize="12sp" />
</LinearLayout>

View File

@ -180,25 +180,6 @@
<item name="android:orientation">horizontal</item>
</style>
<style name="filterItemContainer">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:gravity">center</item>
</style>
<style name="filterItem">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginRight">5dp</item>
<item name="android:drawableRight">@drawable/ic_filter_arrow_down</item>
<item name="android:drawablePadding">5dp</item>
<item name="android:gravity">center_vertical</item>
<item name="android:textColor">@color/text_secondary</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">12sp</item>
</style>
<style name="filterOptionItem">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">24dp</item>

View File

@ -687,6 +687,16 @@
<string name="go_live">上線</string>
<string name="number_of_reservations">%1$s人預約</string>
<string name="wechat_app_not_install_tips">請檢查是否安裝微信客戶端</string>
<string name="more_category">更多分類</string>
<string name="search_category_tags">搜尋分類標籤</string>
<string name="reset">重置</string>
<string name="popular">熱門</string>
<string name="size">大小</string>
<string name="newest">最新</string>
<string name="score">評分</string>
<string name="selected_category_tags_max_toast">最多只能選擇5個類別</string>
<string name="no_relevant_content_found">沒有找到相關內容~</string>
<string name="try_a_different_search_term">換個搜尋詞試試看吧~</string>
<string name="delete_test">刪測</string>
<string name="v_play">暢玩</string>
<string name="update">更新</string>

View File

@ -687,6 +687,16 @@
<string name="go_live">上线</string>
<string name="number_of_reservations">%1$s人预约</string>
<string name="wechat_app_not_install_tips">请检查是否安装微信客户端</string>
<string name="more_category">更多分类</string>
<string name="search_category_tags">搜索分类标签</string>
<string name="reset">重置</string>
<string name="popular">热门</string>
<string name="size">大小</string>
<string name="newest">最新</string>
<string name="score">评分</string>
<string name="selected_category_tags_max_toast">最多只能选择5个类别</string>
<string name="no_relevant_content_found">没有找到相关内容~</string>
<string name="try_a_different_search_term">换个搜索词试试看吧~</string>
<string name="delete_test">删测</string>
<string name="v_play">畅玩</string>
<string name="update">更新</string>

Some files were not shown because too many files have changed in this diff Show More