完成光环助手V4.5.0-新分类功能(前端)https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/1055

This commit is contained in:
lyr
2020-11-19 09:59:33 +08:00
parent fee4e4635c
commit bb3f888f88
54 changed files with 2143 additions and 13 deletions

View File

@ -9,7 +9,6 @@ import android.text.TextUtils
import androidx.appcompat.app.AppCompatActivity
import com.gh.base.BaseActivity
import com.gh.base.BaseActivity_TabLayout
import com.gh.base.ToolBarActivity
import com.gh.base.fragment.BaseFragment_TabLayout
import com.gh.common.AppExecutor
import com.gh.common.constant.Config
@ -22,6 +21,7 @@ import com.gh.common.exposure.ExposureType
import com.gh.common.util.EntranceUtils.*
import com.gh.gamecenter.*
import com.gh.gamecenter.amway.AmwayActivity
import com.gh.gamecenter.catalog.CatalogActivity
import com.gh.gamecenter.category.CategoryDirectoryActivity
import com.gh.gamecenter.download.DownloadFragment.Companion.INDEX_UPDATE
import com.gh.gamecenter.entity.*
@ -47,7 +47,6 @@ import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity
import com.gh.gamecenter.qa.subject.CommunitySubjectActivity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.servers.GameServersActivity
import com.gh.gamecenter.subject.SubjectActivity
import com.gh.gamecenter.suggest.SuggestType
import com.gh.gamecenter.tag.TagsActivity
@ -133,7 +132,9 @@ object DirectUtils {
"server",
"top_game_comment",
"wechat_bind",
"video")
"video",
"catalog"
)
fun directToLinkPage(context: Context,
linkEntity: LinkEntity,
@ -200,6 +201,8 @@ object DirectUtils {
"category", "分类" -> directCategoryDirectory(context, linkEntity.link!!, linkEntity.text!!)
"catalog" -> directCatalog(context, linkEntity.link!!, linkEntity.text!!)
"block", "版块" -> {
if (linkEntity.link.isNullOrEmpty()) return
directToBlock(context,
@ -889,6 +892,21 @@ object DirectUtils {
jumpActivity(context, bundle)
}
/**
* 跳转新分类
*/
@JvmStatic
fun directCatalog(context: Context, catalogId: String, catalogTitle: String, entrance: String? = null, path: String? = "") {
if (catalogId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, CategoryDirectoryActivity::class.java.name)
bundle.putString(KEY_CATALOG_ID, catalogId)
bundle.putString(KEY_CATALOG_TITLE, catalogTitle)
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_PATH, path)
jumpActivity(context, bundle)
}
/**
* 跳转到问题标签详情
*/

View File

@ -215,6 +215,10 @@ public class EntranceUtils {
public static final String KEY_BBS_ID = "bbs_id";
public static final String KEY_DIAGNOSIS = "diagnosis";
public static final String KEY_SIMULATOR = "simulator";
public static final String KEY_CATALOG_ID = "catalogId";
public static final String KEY_PRIMARY_CATALOG_ID = "primaryCatalogId";
public static final String KEY_CATALOG_TITLE = "catalog_title";
public static final String KEY_CATALOG_INIT_TITLE = "catalog_init_title";
public static void jumpActivity(Context context, Bundle bundle) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);

View File

@ -0,0 +1,320 @@
package com.gh.common.view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.gh.common.util.toColor
import com.gh.common.util.visibleIf
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CatalogSwitch
import com.gh.gamecenter.entity.SubCatalogEntity
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.google.android.flexbox.FlexboxLayout
class CatalogFilterView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(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 var mTypeFilterArray = ArrayList<SortType>()
private var mCatalogFilterArray = ArrayList<SubCatalogEntity>()
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
private var mOnCatalogFilterSetupListener: OnCatalogFilterSetupListener? = null
init {
View.inflate(context, R.layout.layout_catalog_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_catalog)
mSizeContainer = findViewById(R.id.container_size)
mTypeContainer.setOnClickListener {
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
}
mCatalogContainer.setOnClickListener {
showSelectCatalogPopupWindow(this, mCatalogTv, mCatalogTv.text.toString())
}
mSizeContainer.setOnClickListener {
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
}
// ratingTv.setOnClickListener {
// mOnConfigFilterSetupListener?.onSetupSortType(SortType.RATING)
// toggleHighlightedTextView(ratingTv, true)
// toggleHighlightedTextView(newestTv, false)
// toggleHighlightedTextView(recommendedTv, false)
// }
//
// newestTv.setOnClickListener {
// mOnConfigFilterSetupListener?.onSetupSortType(SortType.NEWEST)
// toggleHighlightedTextView(ratingTv, false)
// toggleHighlightedTextView(newestTv, true)
// toggleHighlightedTextView(recommendedTv, false)
// }
//
// recommendedTv.setOnClickListener {
// mOnConfigFilterSetupListener?.onSetupSortType(SortType.RECOMMENDED)
// toggleHighlightedTextView(ratingTv, false)
// toggleHighlightedTextView(newestTv, false)
// toggleHighlightedTextView(recommendedTv, true)
// }
}
fun setTypeList(switch: CatalogSwitch) {
switch.run {
if ("on" == hotSort) mTypeFilterArray.add(SortType.RECOMMENDED)
if ("on" == newSort) mTypeFilterArray.add(SortType.NEWEST)
if ("on" == starSort) mTypeFilterArray.add(SortType.RATING)
}
if (mTypeFilterArray.isNotEmpty()) mTypeTv.text = mTypeFilterArray[0].value
}
fun setCatalogList(subCatalogList: List<SubCatalogEntity>, initCatalogName: String) {
mCatalogFilterArray = ArrayList(subCatalogList)
mCatalogTv.text = initCatalogName
}
fun setOnConfigSetupListener(onCatalogFilterSetupListener: OnCatalogFilterSetupListener) {
mOnCatalogFilterSetupListener = onCatalogFilterSetupListener
}
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, R.color.text_757575))
}
}
private fun showSelectTypePopupWindow(containerView: View, typeTv: TextView, typeText: String) {
val drawableUp = ContextCompat.getDrawable(typeTv.context, R.drawable.ic_filter_arrow_up)
val drawableDown = ContextCompat.getDrawable(typeTv.context, R.drawable.ic_filter_arrow_down)
drawableUp?.setBounds(0, 0, drawableUp.minimumWidth, drawableUp.minimumHeight)
drawableDown?.setBounds(0, 0, drawableDown.minimumWidth, drawableDown.minimumHeight)
typeTv.setTextColor(R.color.theme_font.toColor())
typeTv.setCompoundDrawables(null, null, drawableUp, null)
val inflater = LayoutInflater.from(typeTv.context)
val layout = inflater.inflate(R.layout.layout_filter_size, null)
val popupWindow = PopupWindow(
layout,
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT)
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
val backgroundView = layout.findViewById<View>(R.id.background)
backgroundView.setOnClickListener {
popupWindow.dismiss()
}
for (type in mTypeFilterArray) {
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
// 单列 3 个,强行设置宽度为屏幕的 1/3
val width = typeTv.context.resources.displayMetrics.widthPixels / 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 = type.value
toggleHighlightedTextView(tv, typeText == type.value)
tv.tag = type.value
item.setOnClickListener {
toggleHighlightedTextView(tv, true)
popupWindow.dismiss()
typeTv.text = type.value
mOnCatalogFilterSetupListener?.onSetupSortType(type)
}
}
popupWindow.setOnDismissListener {
typeTv.setTextColor(R.color.text_757575.toColor())
typeTv.setCompoundDrawables(null, null, drawableDown, null)
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.showAsDropDown(containerView, 0, 0)
}
private fun showSelectCatalogPopupWindow(containerView: View, catalogTv: TextView, catalogText: String) {
val drawableUp = ContextCompat.getDrawable(catalogTv.context, R.drawable.ic_filter_arrow_up)
val drawableDown = ContextCompat.getDrawable(catalogTv.context, R.drawable.ic_filter_arrow_down)
drawableUp?.setBounds(0, 0, drawableUp.minimumWidth, drawableUp.minimumHeight)
drawableDown?.setBounds(0, 0, drawableDown.minimumWidth, drawableDown.minimumHeight)
catalogTv.setTextColor(R.color.theme_font.toColor())
catalogTv.setCompoundDrawables(null, null, drawableUp, null)
val inflater = LayoutInflater.from(catalogTv.context)
val layout = inflater.inflate(R.layout.layout_filter_size, null)
val popupWindow = PopupWindow(
layout,
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT)
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
val backgroundView = layout.findViewById<View>(R.id.background)
backgroundView.setOnClickListener {
popupWindow.dismiss()
}
for (entity in mCatalogFilterArray) {
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
// 单列 3 个,强行设置宽度为屏幕的 1/3
val width = catalogTv.context.resources.displayMetrics.widthPixels / 3
val height = item.layoutParams.height
item.layoutParams = ViewGroup.LayoutParams(width, height)
flexboxLayout.addView(item)
val tv = item.findViewById<TextView>(R.id.size_tv)
val iv = item.findViewById<ImageView>(R.id.recommend_iv)
tv.text = entity.name
iv.visibleIf(entity.recommended)
toggleHighlightedTextView(tv, catalogText == entity.name)
tv.tag = entity.name
item.setOnClickListener {
toggleHighlightedTextView(tv, true)
popupWindow.dismiss()
catalogTv.text = entity.name
mOnCatalogFilterSetupListener?.onSetupSortCatalog(entity)
}
}
popupWindow.setOnDismissListener {
catalogTv.setTextColor(R.color.text_757575.toColor())
catalogTv.setCompoundDrawables(null, null, drawableDown, null)
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.showAsDropDown(containerView, 0, 0)
}
private fun showSelectSizePopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
val drawableUp = ContextCompat.getDrawable(sizeTv.context, R.drawable.ic_filter_arrow_up)
val drawableDown = ContextCompat.getDrawable(sizeTv.context, R.drawable.ic_filter_arrow_down)
drawableUp?.setBounds(0, 0, drawableUp.minimumWidth, drawableUp.minimumHeight)
drawableDown?.setBounds(0, 0, drawableDown.minimumWidth, drawableDown.minimumHeight)
sizeTv.setTextColor(R.color.theme_font.toColor())
sizeTv.setCompoundDrawables(null, null, drawableUp, null)
val inflater = LayoutInflater.from(sizeTv.context)
val layout = inflater.inflate(R.layout.layout_filter_size, null)
val popupWindow = PopupWindow(
layout,
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT)
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 {
popupWindow.dismiss()
}
for (size in sizeFilterArray!!) {
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
// 单列 3 个,强行设置宽度为屏幕的 1/3
val width = sizeTv.context.resources.displayMetrics.widthPixels / 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
mOnCatalogFilterSetupListener?.onSetupSortSize(size)
}
}
popupWindow.setOnDismissListener {
sizeTv.setTextColor(R.color.text_757575.toColor())
sizeTv.setCompoundDrawables(null, null, drawableDown, null)
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
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 = 20, text = "20M以下"))
add(SubjectSettingEntity.Size(min = 20, max = 50, text = "20-50M"))
add(SubjectSettingEntity.Size(min = 50, max = 100, text = "50-100M"))
add(SubjectSettingEntity.Size(min = 100, max = 500, text = "100-500M"))
add(SubjectSettingEntity.Size(min = 500, max = -1, text = "500M以上"))
}
}
interface OnCatalogFilterSetupListener {
fun onSetupSortSize(sortSize: SubjectSettingEntity.Size)
fun onSetupSortType(sortType: SortType)
fun onSetupSortCatalog(sortCatalog: SubCatalogEntity)
}
enum class SortType(val value: String) {
RECOMMENDED("热门推荐"),
NEWEST("最新上线"),
RATING("最高评分")
}
}

View File

@ -0,0 +1,33 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.common.util.EntranceUtils
import com.gh.gamecenter.NormalActivity
import com.gh.gamecenter.R
class CatalogActivity : NormalActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setToolbarMenu(R.menu.menu_download)
}
override fun showDownloadMenu(): Boolean {
return true
}
override fun provideNormalIntent(): Intent {
return getTargetIntent(this, CatalogActivity::class.java, CatalogFragment::class.java)
}
companion object {
fun getIntent(context: Context, catalogId: String, catalogTitle: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceUtils.KEY_CATALOG_ID, catalogId)
bundle.putString(EntranceUtils.KEY_CATALOG_TITLE, catalogTitle)
return getTargetIntent(context, CatalogActivity::class.java, CatalogFragment::class.java, bundle)
}
}
}

View File

@ -0,0 +1,47 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.view.View
import android.view.ViewGroup
import com.gh.base.BaseRecyclerViewHolder
import com.gh.common.util.toColor
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.CatalogItemBinding
import com.gh.gamecenter.entity.SubCatalogEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class CatalogAdapter(context: Context, private val mFragment: CatalogFragment, private val mList: List<SubCatalogEntity>) : BaseRecyclerAdapter<CatalogAdapter.CatalogItemViewHolder>(context) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CatalogItemViewHolder {
val view = mLayoutInflater.inflate(R.layout.catalog_item, parent, false)
return CatalogItemViewHolder(CatalogItemBinding.bind(view))
}
override fun getItemCount(): Int {
return mList.size
}
override fun onBindViewHolder(holder: CatalogItemViewHolder, position: Int) {
holder.binding.run {
val catalogEntity = mList[position]
entity = catalogEntity
executePendingBindings()
if (catalogEntity.name == mFragment.selectedCatalogName) {
selectedTag.visibility = View.VISIBLE
root.setBackgroundColor(R.color.white.toColor())
} else {
selectedTag.visibility = View.GONE
root.setBackgroundColor(R.color.text_F5F5F5.toColor())
}
root.setOnClickListener {
if (catalogEntity.name != mFragment.selectedCatalogName) {
mFragment.selectedCatalogName = catalogEntity.name
mFragment.changeCatalog(position)
notifyDataSetChanged()
}
}
}
}
class CatalogItemViewHolder(val binding: CatalogItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
}

View File

@ -0,0 +1,119 @@
package com.gh.gamecenter.catalog
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.util.EntranceUtils
import com.gh.common.util.viewModelProvider
import com.gh.common.view.FixLinearLayoutManager
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.entity.SubCatalogEntity
import com.gh.gamecenter.normal.NormalFragment
import kotterknife.bindView
class CatalogFragment : NormalFragment() {
private val mReuseLoading by bindView<LinearLayout>(R.id.reuse_ll_loading)
private val mReuseNoConnection by bindView<View>(R.id.reuse_no_connection)
private val mReuseNoData by bindView<View>(R.id.reuse_none_data)
private val mCatalogContainer by bindView<View>(R.id.container_catalog)
private val mCatalogRv by bindView<RecyclerView>(R.id.rv_catalog)
private lateinit var mViewModel: CatalogViewModel
private lateinit var mEntity: CatalogEntity
private lateinit var mSpecialCatalogFragment: SpecialCatalogFragment
private lateinit var mSubCatalogFragment: SubCatalogFragment
private var mCatalogId: String = ""
private var mSelectedPosition = 0
var selectedCatalogName: String = ""
override fun getLayoutId() = R.layout.fragment_catalog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mCatalogId = arguments?.getString(EntranceUtils.KEY_CATALOG_ID) ?: ""
mViewModel = viewModelProvider(CatalogViewModel.Factory(mCatalogId))
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setNavigationTitle(arguments?.getString(EntranceUtils.KEY_CATALOG_TITLE))
mViewModel.catalogs.observe(viewLifecycleOwner, Observer {
mReuseLoading.visibility = View.GONE
if (it != null) {
mReuseNoConnection.visibility = View.GONE
if (it.subCatalog.isNotEmpty()) {
mCatalogContainer.visibility = View.VISIBLE
mReuseNoData.visibility = View.GONE
mEntity = it
if (mEntity.hasSpecial) {
val specialEntity = SubCatalogEntity(name = "精选")
(mEntity.subCatalog as ArrayList).add(0, specialEntity)
}
initView()
} else {
mCatalogContainer.visibility = View.GONE
mReuseNoData.visibility = View.VISIBLE
}
} else {
mCatalogContainer.visibility = View.GONE
mReuseNoData.visibility = View.GONE
mReuseNoConnection.visibility = View.VISIBLE
mReuseNoConnection.setOnClickListener {
mReuseLoading.visibility = View.VISIBLE
mViewModel.getCatalogs()
}
}
})
}
private fun initView() {
mEntity.run {
if (subCatalog.isNotEmpty()) {
selectedCatalogName = subCatalog[0].name
mCatalogRv.layoutManager = FixLinearLayoutManager(requireContext())
mCatalogRv.adapter = CatalogAdapter(requireContext(), this@CatalogFragment, subCatalog)
if (hasSpecial) {
mSpecialCatalogFragment = childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.simpleName) as? SpecialCatalogFragment ?: SpecialCatalogFragment()
mSpecialCatalogFragment.arguments = bundleOf(EntranceUtils.KEY_CATALOG_ID to mEntity.id)
childFragmentManager.beginTransaction().replace(R.id.container_sub_catalog, mSpecialCatalogFragment, SpecialCatalogFragment::class.java.simpleName).commitAllowingStateLoss()
} else {
mSubCatalogFragment = childFragmentManager.findFragmentByTag(SubCatalogFragment::class.java.simpleName) as? SubCatalogFragment ?: SubCatalogFragment()
mSubCatalogFragment.arguments = bundleOf(EntranceUtils.KEY_CATALOG_ID to mEntity.id, EntranceUtils.KEY_PRIMARY_CATALOG_ID to subCatalog[0].id)
childFragmentManager.beginTransaction().replace(R.id.container_sub_catalog, mSubCatalogFragment, SubCatalogFragment::class.java.simpleName).commitAllowingStateLoss()
}
}
}
}
fun changeCatalog(position: Int) {
mEntity.run {
if (hasSpecial) {
if (mSelectedPosition == 0) {
mSubCatalogFragment = childFragmentManager.findFragmentByTag(SubCatalogFragment::class.java.simpleName) as? SubCatalogFragment ?: SubCatalogFragment()
mSubCatalogFragment.arguments = bundleOf(EntranceUtils.KEY_CATALOG_ID to mEntity.id, EntranceUtils.KEY_PRIMARY_CATALOG_ID to subCatalog[position].id)
childFragmentManager.beginTransaction().replace(R.id.container_sub_catalog, mSubCatalogFragment, SubCatalogFragment::class.java.simpleName).commitAllowingStateLoss()
} else {
if (position == 0) {
mSpecialCatalogFragment = childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.simpleName) as? SpecialCatalogFragment ?: SpecialCatalogFragment()
mSpecialCatalogFragment.arguments = bundleOf(EntranceUtils.KEY_CATALOG_ID to mEntity.id)
childFragmentManager.beginTransaction().replace(R.id.container_sub_catalog, mSpecialCatalogFragment, SpecialCatalogFragment::class.java.simpleName).commitAllowingStateLoss()
} else {
mSubCatalogFragment.changeSubCatalog(subCatalog[position].id)
}
}
} else {
mSubCatalogFragment.changeSubCatalog(subCatalog[position].id)
}
mSelectedPosition = position
}
}
}

View File

@ -0,0 +1,47 @@
package com.gh.gamecenter.catalog
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
class CatalogViewModel(application: Application, private val catalogId: String) : AndroidViewModel(application) {
private val api = RetrofitManager.getInstance(getApplication()).api
var catalogs = MutableLiveData<CatalogEntity>()
init {
getCatalogs()
}
@SuppressLint("CheckResult")
fun getCatalogs() {
api.getCatalogs(catalogId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<CatalogEntity>() {
override fun onSuccess(data: CatalogEntity) {
catalogs.postValue(data)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
catalogs.postValue(null)
}
})
}
class Factory(private val catalogId: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return CatalogViewModel(HaloApp.getInstance().application, catalogId) as T
}
}
}

View File

@ -0,0 +1,33 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.common.util.EntranceUtils
import com.gh.gamecenter.NormalActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CatalogEntity
class NewCatalogListActivity : NormalActivity() {
companion object {
fun getIntent(context: Context, catalogTitle: String, catalog: CatalogEntity, initTitle: String): Intent {
val bundle = Bundle()
bundle.putParcelable(EntranceUtils.KEY_DATA, catalog)
bundle.putString(EntranceUtils.KEY_NAME, catalog.name)
bundle.putString(EntranceUtils.KEY_CATALOG_TITLE, catalogTitle)
bundle.putString(EntranceUtils.KEY_CATALOG_INIT_TITLE, initTitle)
return getTargetIntent(context, NewCatalogListActivity::class.java, NewCatalogListFragment::class.java, bundle)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setToolbarMenu(R.menu.menu_download)
}
override fun showDownloadMenu(): Boolean {
return true
}
}

View File

@ -0,0 +1,168 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.util.SparseArray
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.constant.ItemViewType
import com.gh.common.exposure.ExposureEvent
import com.gh.common.exposure.ExposureSource
import com.gh.common.exposure.ExposureType
import com.gh.common.exposure.IExposable
import com.gh.common.util.DownloadItemUtils
import com.gh.common.util.StringUtils
import com.gh.common.util.dip2px
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.baselist.LoadType
import com.gh.gamecenter.databinding.GameItemBinding
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.game.GameItemViewHolder
import com.lightgame.download.DownloadEntity
import java.util.*
class NewCatalogListAdapter(context: Context,
private val mViewModel: NewCatalogListViewModel,
private val mEntrance: String?) : ListAdapter<GameEntity>(context), IExposable {
private val mExposureEventSparseArray: SparseArray<ExposureEvent> = SparseArray()
val positionAndPackageMap = HashMap<String, Int>()
override fun setListData(updateData: MutableList<GameEntity>?) {
// 记录游戏位置
if (updateData != null) {
for (i in 0 until updateData.size) {
val gameEntity = updateData[i]
var packages = gameEntity.id
for (apkEntity in gameEntity.getApk()) {
packages += apkEntity.packageName
}
positionAndPackageMap[packages + i] = i
}
}
super.setListData(updateData)
}
fun clearPositionAndPackageMap() {
positionAndPackageMap.clear()
}
override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
return oldItem?.id == newItem?.id
}
override fun getItemViewType(position: Int): Int {
if (position == itemCount - 1) {
return ItemViewType.ITEM_FOOTER
}
return ItemViewType.GAME_NORMAL
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ItemViewType.GAME_NORMAL -> {
GameItemViewHolder(GameItemBinding.bind(mLayoutInflater.inflate(R.layout.game_item, parent, false)))
}
else -> {
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
}
}
}
override fun getItemCount(): Int {
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is GameItemViewHolder) {
val gameEntity = mEntityList[position]
holder.binding.game = gameEntity
holder.initServerType(gameEntity)
holder.binding.hideSize = true
holder.binding.executePendingBindings()
val padTop = if (position == 0) 16F.dip2px() else 8F.dip2px()
holder.itemView.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 8F.dip2px())
gameEntity.sequence = position + 1
val sortType = mViewModel.sortType.value
val toolbarTitle = mViewModel.title
val categoryTitle = mViewModel.categoryTitle
val selectedCatalogName = mViewModel.selectedCatalog.name
val exposureSources = ArrayList<ExposureSource>()
exposureSources.add(ExposureSource(categoryTitle, selectedCatalogName))
exposureSources.add(ExposureSource("二级分类", "$selectedCatalogName+$sortType"))
gameEntity.sequence = position + 1
val event = ExposureEvent.createEvent(gameEntity, exposureSources, null, ExposureType.EXPOSURE)
mExposureEventSparseArray.put(position, event)
holder.itemView.setOnClickListener {
GameDetailActivity.startGameDetailActivity(
mContext,
gameEntity,
StringUtils.buildString(mEntrance, "+(", toolbarTitle, ":列表[", selectedCatalogName, "=", sortType, "=", (position + 1).toString(), "])"),
event)
}
DownloadItemUtils.setOnClickListener(mContext,
holder.binding.downloadBtn,
gameEntity,
position,
this,
StringUtils.buildString(StringUtils.buildString(mEntrance, "+(", toolbarTitle, ":列表[", selectedCatalogName, "=", sortType, "=", (position + 1).toString(), "])")),
StringUtils.buildString(selectedCatalogName, ":", gameEntity.name),
event)
DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding), true, "star&brief")
} else if (holder is FooterViewHolder) {
holder.initItemPadding()
holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver, R.string.ask_loadover_hint)
holder.itemView.setOnClickListener {
if (mIsNetworkError) {
mViewModel.load(LoadType.RETRY)
}
}
}
}
fun notifyItemByDownload(download: DownloadEntity) {
for (key in positionAndPackageMap.keys) {
if (key.contains(download.packageName) && key.contains(download.gameId)) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].getEntryMap()[download.platform] = download
notifyItemChanged(position)
}
}
}
}
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
for (key in positionAndPackageMap.keys) {
if (key.contains(status.packageName) && key.contains(status.gameId)) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].getEntryMap().remove(status.platform)
notifyItemChanged(position)
}
}
}
}
override fun getEventByPosition(pos: Int): ExposureEvent? {
return mExposureEventSparseArray.get(pos)
}
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
return null
}
}

View File

@ -0,0 +1,154 @@
package com.gh.gamecenter.catalog
import android.os.Bundle
import android.view.View
import com.ethanhua.skeleton.Skeleton
import com.gh.common.constant.Constants
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.*
import com.gh.common.view.CatalogFilterView
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import kotterknife.bindView
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class NewCatalogListFragment : ListFragment<GameEntity, NewCatalogListViewModel>() {
private val mSkeletonView by bindView<View>(R.id.list_skeleton)
private val mFilterView by bindView<CatalogFilterView>(R.id.filter_container)
private var mPrimeCatalog: CatalogEntity? = null
private var mSubCatalogList = arrayListOf<SubCatalogEntity>()
private var mInitCatalogName = ""
private var mAdapter: NewCatalogListAdapter? = null
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) {
showUnzipFailureDialog(downloadEntity)
}
}
}
private lateinit var mExposureListener: ExposureListener
private lateinit var mViewModel: NewCatalogListViewModel
override fun getLayoutId() = R.layout.fragment_catalog_list
override fun provideListViewModel() = viewModelProvider<NewCatalogListViewModel>()
override fun provideListAdapter() = mAdapter ?: NewCatalogListAdapter(requireContext(), mViewModel, mEntrance).apply { mAdapter = this }
override fun getItemDecoration() = null
override fun onCreate(savedInstanceState: Bundle?) {
mViewModel = provideListViewModel()
mViewModel.title = arguments?.getString(EntranceUtils.KEY_NAME) ?: ""
mViewModel.categoryTitle = arguments?.getString(EntranceUtils.KEY_CATALOG_TITLE) ?: ""
mEntrance = arguments?.getString(EntranceUtils.KEY_ENTRANCE) ?: Constants.ENTRANCE_UNKNOWN
mPrimeCatalog = arguments?.getParcelable(EntranceUtils.KEY_DATA)
mSubCatalogList = mPrimeCatalog?.subCatalog as ArrayList<SubCatalogEntity>
mInitCatalogName = arguments?.getString(EntranceUtils.KEY_CATALOG_INIT_TITLE) ?: ""
mViewModel.selectedCatalog = mSubCatalogList.find { entity -> entity.name == mInitCatalogName }
?: SubCatalogEntity()
super.onCreate(savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setNavigationTitle(mViewModel.title)
initFilterView()
mViewModel.refresh.observeNonNull(this) { onRefresh() }
mExposureListener = ExposureListener(this, mAdapter!!)
mListRv.addOnScrollListener(mExposureListener)
mSkeletonScreen = Skeleton.bind(mSkeletonView).shimmer(false).load(R.layout.fragment_subject_skeleton).show()
}
private fun initFilterView() {
mFilterView.run {
visibility = View.VISIBLE
setTypeList(mPrimeCatalog?.switch ?: CatalogSwitch())
setCatalogList(mSubCatalogList, mInitCatalogName)
setOnConfigSetupListener(object : CatalogFilterView.OnCatalogFilterSetupListener {
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
mViewModel.updateSortConfig(sortSize = sortSize)
}
override fun onSetupSortType(sortType: CatalogFilterView.SortType) {
mViewModel.updateSortConfig(sortType = sortType)
}
override fun onSetupSortCatalog(sortCatalog: SubCatalogEntity) {
mViewModel.updateSortConfig(sortCatalog = sortCatalog)
}
})
}
}
override fun onResume() {
if (isEverPause && mAdapter != null) mAdapter?.notifyDataSetChanged()
super.onResume()
DownloadManager.getInstance(context).addObserver(mDataWatcher)
}
override fun onPause() {
super.onPause()
DownloadManager.getInstance(context).removeObserver(mDataWatcher)
}
override fun onRefresh() {
mAdapter?.clearPositionAndPackageMap()
super.onRefresh()
}
// 下载被删除事件
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(status: EBDownloadStatus) {
if ("delete" == status.status) {
mAdapter?.notifyItemAndRemoveDownload(status)
}
}
// 安装/卸载 事件
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(busFour: EBPackage) {
if ("安装" == busFour.type || "卸载" == busFour.type) {
mAdapter?.notifyDataSetChanged()
}
}
fun showUnzipFailureDialog(downloadEntity: DownloadEntity) {
val data = mAdapter?.positionAndPackageMap ?: return
for (gameAndPosition in data) {
if (gameAndPosition.key.contains(downloadEntity.packageName)) {
val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value)
if (targetView != null) {
DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity)
return
}
}
}
}
}

View File

@ -0,0 +1,82 @@
package com.gh.gamecenter.catalog
import android.app.Application
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.UrlFilterUtils
import com.gh.common.view.CatalogFilterView
import com.gh.gamecenter.baselist.ListViewModel
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.entity.SubCatalogEntity
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
import io.reactivex.Single
class NewCatalogListViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
var title = "" // 显示在 Toolbar 的标题
var categoryTitle = "" // 跳转进来的分类的标题
val refresh = MutableLiveData<Boolean>()
var selectedCatalog = SubCatalogEntity()
var sortType = CatalogFilterView.SortType.RECOMMENDED
private var mSortSize = SubjectSettingEntity.Size()
private val api = RetrofitManager.getInstance(getApplication()).api
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? = null
override fun provideDataSingle(page: Int): Single<List<GameEntity>> {
return if (selectedCatalog.link.type == "column") {
api.getColumn(selectedCatalog.link.link, getSortType(), getSortSize(), page)
} else {
api.getGamesWithSpecificTag(getSortSize(), getSortType(), page)
}
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) }
}
fun updateSortConfig(sortSize: SubjectSettingEntity.Size? = null,
sortType: CatalogFilterView.SortType? = null,
sortCatalog: SubCatalogEntity? = null) {
when {
sortSize != null && sortSize != mSortSize -> {
mSortSize = sortSize
refresh.postValue(true)
}
sortType != null && sortType != this.sortType -> {
this.sortType = sortType
refresh.postValue(true)
}
sortCatalog != null && sortCatalog != selectedCatalog -> {
selectedCatalog = sortCatalog
refresh.postValue(true)
}
}
}
private fun getSortSize(): String? {
return if (selectedCatalog.link.type == "column") {
UrlFilterUtils.getFilterQuery(
"min_size", mSortSize.min.toString(),
"max_size", mSortSize.max.toString())
} else {
UrlFilterUtils.getFilterQuery(
"tag_id", selectedCatalog.link.link,
"min_size", mSortSize.min.toString(),
"max_size", mSortSize.max.toString())
}
}
private fun getSortType(): String? {
return when (sortType) {
CatalogFilterView.SortType.RECOMMENDED -> "download:-1"
CatalogFilterView.SortType.NEWEST -> "publish:-1"
CatalogFilterView.SortType.RATING -> "star:-1"
}
}
}

View File

@ -0,0 +1,148 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.base.BaseRecyclerViewHolder
import com.gh.common.constant.ItemViewType
import com.gh.common.util.DirectUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.databinding.CatalogHeaderItemBinding
import com.gh.gamecenter.databinding.CatalogImageItemBinding
import com.gh.gamecenter.databinding.CatalogSubjectCollectionItemBinding
import com.gh.gamecenter.databinding.CatalogSubjectItemBinding
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.subject.SubjectActivity.Companion.startSubjectActivity
class SpecialCatalogAdapter(context: Context) : ListAdapter<SpecialCatalogItemData>(context) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view: View
return when (viewType) {
ItemViewType.ITEM_FOOTER -> {
view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)
FooterViewHolder(view)
}
TYPE_HEADER -> CatalogHeaderItemHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.catalog_header_item, parent, false))
TYPE_BIG_IMAGE -> CatalogImageItemHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.catalog_image_item, parent, false))
TYPE_SUBJECT -> CatalogSubjectItemHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.catalog_subject_item, parent, false))
TYPE_SUBJECT_COLLECTION -> CatalogSubjectCollectionItemHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.catalog_subject_collection_item, parent, false))
else -> throw NullPointerException()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is CatalogImageItemHolder -> {
val imageEntity = mEntityList[position].bigImage!!
holder.binding.run {
entity = imageEntity.image
root.setOnClickListener { DirectUtils.directToLinkPage(mContext, imageEntity.link, "新分类-精选分类", "图片") }
}
}
is CatalogHeaderItemHolder -> {
val entity = mEntityList[position].header!!
val specialLink = entity.link
holder.binding.run {
link = specialLink
headMore.setOnClickListener {
if (entity.type == "专题合集") {
DirectUtils.directToColumnCollection(mContext, specialLink.link ?: "", -1, "(游戏-专题:" + specialLink.text + "-全部)")
} else {
startSubjectActivity(mContext, specialLink.link, specialLink.text, false, "(游戏-专题:" + specialLink.text + "-全部)")
}
}
}
}
is CatalogSubjectItemHolder -> {
val subject = mEntityList[position].subject!!
holder.bindSubject(subject.link.data)
}
is CatalogSubjectCollectionItemHolder -> {
val subjectCollection = mEntityList[position].subjectCollection!!
holder.bindSubjectCollection(subjectCollection.link.data)
}
is FooterViewHolder -> {
holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver, R.string.ask_loadover_hint)
}
}
}
override fun getItemViewType(position: Int): Int {
return if (position == itemCount - 1) {
ItemViewType.ITEM_FOOTER
} else {
val item = mEntityList[position]
when {
item.header != null -> TYPE_HEADER
item.bigImage != null -> TYPE_BIG_IMAGE
item.subject != null -> TYPE_SUBJECT
item.subjectCollection != null -> TYPE_SUBJECT_COLLECTION
else -> TYPE_SUBJECT_COLLECTION
}
}
}
override fun getItemCount() = if (mEntityList.isNullOrEmpty()) 0 else mEntityList.size + FOOTER_ITEM_COUNT
class CatalogImageItemHolder(val binding: CatalogImageItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
class CatalogHeaderItemHolder(val binding: CatalogHeaderItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
class CatalogSubjectItemHolder(val binding: CatalogSubjectItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
fun bindSubject(gameList: List<GameEntity>) {
binding.gameList.run {
var subjectAdapter = adapter
if (subjectAdapter is SpecialCatalogSubjectAdapter) {
subjectAdapter.checkResetData(gameList)
return
}
subjectAdapter = SpecialCatalogSubjectAdapter(context, gameList)
layoutManager = GridLayoutManager(context, 3)
adapter = subjectAdapter
}
}
}
class CatalogSubjectCollectionItemHolder(val binding: CatalogSubjectCollectionItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
fun bindSubjectCollection(subjectCollection: List<GameEntity>) {
binding.subjectCollectionList.run {
var collectionAdapter = adapter
if (collectionAdapter is SpecialCatalogSubjectCollectionAdapter) {
collectionAdapter.checkResetData(subjectCollection)
return
}
collectionAdapter = SpecialCatalogSubjectCollectionAdapter(context, subjectCollection)
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
adapter = collectionAdapter
}
}
}
companion object {
private const val TYPE_HEADER = 900
private const val TYPE_BIG_IMAGE = 901
private const val TYPE_SUBJECT = 902
private const val TYPE_SUBJECT_COLLECTION = 903
}
}

View File

@ -0,0 +1,32 @@
package com.gh.gamecenter.catalog
import android.graphics.Color
import android.os.Bundle
import android.view.View
import com.gh.common.util.*
import com.gh.gamecenter.baselist.ListFragment
class SpecialCatalogFragment : ListFragment<SpecialCatalogItemData, SpecialCatalogViewModel>() {
private var mCatalogId = ""
private var mAdapter: SpecialCatalogAdapter? = null
override fun provideListViewModel() = viewModelProvider<SpecialCatalogViewModel>(SpecialCatalogViewModel.Factory(mCatalogId))
override fun provideListAdapter() = mAdapter ?: SpecialCatalogAdapter(requireContext()).apply { mAdapter = this }
override fun getItemDecoration() = null
override fun onCreate(savedInstanceState: Bundle?) {
mCatalogId = arguments?.getString(EntranceUtils.KEY_CATALOG_ID) ?: ""
super.onCreate(savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mListRv.setBackgroundColor(Color.WHITE)
}
}

View File

@ -0,0 +1,10 @@
package com.gh.gamecenter.catalog
import com.gh.gamecenter.entity.SpecialCatalogEntity
data class SpecialCatalogItemData(
val header: SpecialCatalogEntity? = null,
val bigImage: SpecialCatalogEntity? = null,
val subject: SpecialCatalogEntity? = null,
val subjectCollection: SpecialCatalogEntity? = null
)

View File

@ -0,0 +1,57 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import com.gh.base.BaseRecyclerViewHolder
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.CatalogSubjectGameItemBinding
import com.gh.gamecenter.entity.GameEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class SpecialCatalogSubjectAdapter(context: Context, private var mList: List<GameEntity>) : BaseRecyclerAdapter<SpecialCatalogSubjectAdapter.CatalogSubjectGameItemViewHolder>(context) {
private val mEntrance = "精选分类"
private var countAndKey: Pair<Int, String>? = null
init {
var dataIds = ""
mList.forEach {
dataIds += it.id
}
if (dataIds.isNotEmpty()) countAndKey = Pair(mList.size, dataIds)
}
override fun getItemCount() = mList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
= CatalogSubjectGameItemViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.catalog_subject_game_item, parent, false))
override fun onBindViewHolder(holder: CatalogSubjectGameItemViewHolder, position: Int) {
holder.binding.run {
val entity = mList[position]
game = entity
root.setOnClickListener {
GameDetailActivity.startGameDetailActivity(mContext, entity, "(${mEntrance})")
}
}
}
fun checkResetData(update: List<GameEntity>) {
var dataIds = ""
mList.forEach { dataIds += it.id }
mList = update
if (countAndKey?.first == update.size && countAndKey?.second != dataIds) { // 数量不变,内容发生改变
notifyItemRangeChanged(0, itemCount)
} else if (countAndKey?.first != update.size) { // 数量发生改变
notifyDataSetChanged()
}
// 重新刷新数据标识
countAndKey = Pair(update.size, dataIds)
}
class CatalogSubjectGameItemViewHolder(val binding: CatalogSubjectGameItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
}

View File

@ -0,0 +1,61 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import com.gh.base.BaseRecyclerViewHolder
import com.gh.common.util.DirectUtils
import com.gh.common.util.ImageUtils
import com.gh.common.util.dip2px
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.CatalogSubjectCollectionListItemBinding
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.entity.LinkEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class SpecialCatalogSubjectCollectionAdapter(context: Context, private var mList: List<GameEntity>) : BaseRecyclerAdapter<SpecialCatalogSubjectCollectionAdapter.CatalogSubjectCollectionListItemViewHolder>(context) {
private var countAndKey: Pair<Int, String>? = null
init {
var dataIds = ""
mList.forEach {
dataIds += it.id
}
if (dataIds.isNotEmpty()) countAndKey = Pair(mList.size, dataIds)
}
override fun getItemCount() = mList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
= CatalogSubjectCollectionListItemViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.catalog_subject_collection_list_item, parent, false))
override fun onBindViewHolder(holder: CatalogSubjectCollectionListItemViewHolder, position: Int) {
holder.binding.run {
root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply {
leftMargin = if (position == 0) 16F.dip2px() else 0
}
val entity = mList[position]
ImageUtils.display(subjectCollectionImage, entity.image)
root.setOnClickListener { DirectUtils.directToLinkPage(mContext, LinkEntity(link = entity.link, type = entity.type), "精选分类", "专题合集") }
}
}
fun checkResetData(update: List<GameEntity>) {
var dataIds = ""
mList.forEach { dataIds += it.id }
mList = update
if (countAndKey?.first == update.size && countAndKey?.second != dataIds) { // 数量不变,内容发生改变
notifyItemRangeChanged(0, itemCount)
} else if (countAndKey?.first != update.size) { // 数量发生改变
notifyDataSetChanged()
}
// 重新刷新数据标识
countAndKey = Pair(update.size, dataIds)
}
class CatalogSubjectCollectionListItemViewHolder(val binding: CatalogSubjectCollectionListItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
}

View File

@ -0,0 +1,50 @@
package com.gh.gamecenter.catalog
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.baselist.ListViewModel
import com.gh.gamecenter.entity.SpecialCatalogEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.Observable
import io.reactivex.Single
class SpecialCatalogViewModel(application: Application, private val catalogId: String): ListViewModel<SpecialCatalogEntity, SpecialCatalogItemData>(application) {
override fun provideDataObservable(page: Int): Observable<List<SpecialCatalogEntity>>? = null
override fun provideDataSingle(page: Int): Single<List<SpecialCatalogEntity>> {
return RetrofitManager.getInstance(getApplication())
.sensitiveApi
.getSpecialCatalogs(catalogId, page)
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) { list ->
val itemDataList = arrayListOf<SpecialCatalogItemData>()
list.forEach {
when (it.type) {
"图片" -> itemDataList.add(SpecialCatalogItemData(bigImage = it))
"专题" -> {
itemDataList.add(SpecialCatalogItemData(header = it))
itemDataList.add(SpecialCatalogItemData(subject = it))
}
"专题合集" -> {
itemDataList.add(SpecialCatalogItemData(header = it))
itemDataList.add(SpecialCatalogItemData(subjectCollection = it))
}
}
}
mResultLiveData.postValue(itemDataList)
}
}
class Factory(private val catalogId: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return SpecialCatalogViewModel(HaloApp.getInstance().application, catalogId) as T
}
}
}

View File

@ -0,0 +1,34 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.view.ViewGroup
import com.gh.base.BaseRecyclerViewHolder
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.SubCatalogItemBinding
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.entity.SubCatalogEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class SubCatalogAdapter(context: Context, private val mPrimaryCatalog: CatalogEntity, private var mList: List<SubCatalogEntity>) : BaseRecyclerAdapter<SubCatalogAdapter.SubCatalogItemViewHolder>(context) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubCatalogItemViewHolder {
val view = mLayoutInflater.inflate(R.layout.sub_catalog_item, parent, false)
return SubCatalogItemViewHolder(SubCatalogItemBinding.bind(view))
}
override fun getItemCount(): Int {
return mList.size
}
override fun onBindViewHolder(holder: SubCatalogItemViewHolder, position: Int) {
holder.binding.run {
val catalogEntity = mList[position]
entity = catalogEntity
root.setOnClickListener {
root.context.startActivity(NewCatalogListActivity.getIntent(mContext, catalogEntity.name, mPrimaryCatalog, catalogEntity.name))
}
}
}
class SubCatalogItemViewHolder(val binding: SubCatalogItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
}

View File

@ -0,0 +1,84 @@
package com.gh.gamecenter.catalog
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.util.EntranceUtils
import com.gh.common.util.viewModelProvider
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.normal.NormalFragment
import kotterknife.bindView
class SubCatalogFragment : NormalFragment() {
private val mReuseLoading by bindView<LinearLayout>(R.id.reuse_ll_loading)
private val mReuseNoConnection by bindView<View>(R.id.reuse_no_connection)
private val mReuseNoData by bindView<View>(R.id.reuse_none_data)
private val mSubCatalogRv by bindView<RecyclerView>(R.id.rv_sub_catalog)
private lateinit var mViewModel: SubCatalogViewModel
private lateinit var mSubCatalogAdapter: SubCatalogAdapter
private lateinit var mEntity: CatalogEntity
private var mCatalogId: String = ""
private var mPrimaryCatalogId: String = ""
override fun getLayoutId() = R.layout.fragment_sub_catalog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mCatalogId = arguments?.getString(EntranceUtils.KEY_CATALOG_ID) ?: ""
mPrimaryCatalogId = arguments?.getString(EntranceUtils.KEY_PRIMARY_CATALOG_ID) ?: ""
mViewModel = viewModelProvider(SubCatalogViewModel.Factory(mCatalogId))
mViewModel.getSubCatalogs(mPrimaryCatalogId)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewModel.catalogs.observe(viewLifecycleOwner, Observer {
mReuseLoading.visibility = View.GONE
if (it != null) {
mReuseNoConnection.visibility = View.GONE
if (it.subCatalog.isNotEmpty()) {
mSubCatalogRv.visibility = View.VISIBLE
mReuseNoData.visibility = View.GONE
mEntity = it
initView()
} else {
mSubCatalogRv.visibility = View.GONE
mReuseNoData.visibility = View.VISIBLE
}
} else {
mSubCatalogRv.visibility = View.GONE
mReuseNoData.visibility = View.GONE
mReuseNoConnection.visibility = View.VISIBLE
mReuseNoConnection.setOnClickListener {
mReuseLoading.visibility = View.VISIBLE
mViewModel.getSubCatalogs(mPrimaryCatalogId)
}
}
})
}
private fun initView() {
mEntity.run {
mSubCatalogRv.layoutManager = GridLayoutManager(requireContext(), 3)
mSubCatalogAdapter = SubCatalogAdapter(requireContext(), mEntity, subCatalog)
mSubCatalogRv.adapter = mSubCatalogAdapter
}
}
fun changeSubCatalog(primaryCatalogId: String) {
mPrimaryCatalogId = primaryCatalogId
mSubCatalogRv.visibility = View.GONE
mReuseNoData.visibility = View.GONE
mReuseNoConnection.visibility = View.GONE
mReuseLoading.visibility = View.VISIBLE
mViewModel.getSubCatalogs(mPrimaryCatalogId)
}
}

View File

@ -0,0 +1,44 @@
package com.gh.gamecenter.catalog
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
class SubCatalogViewModel(application: Application, private val catalogId: String): AndroidViewModel(application) {
private val api = RetrofitManager.getInstance(getApplication()).api
var catalogs = MutableLiveData<CatalogEntity>()
@SuppressLint("CheckResult")
fun getSubCatalogs(primaryCatalogId: String) {
api.getSubCatalogs(catalogId, primaryCatalogId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<CatalogEntity>() {
override fun onSuccess(data: CatalogEntity) {
catalogs.postValue(data)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
catalogs.postValue(null)
}
})
}
class Factory(private val catalogId: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return SubCatalogViewModel(HaloApp.getInstance().application, catalogId) as T
}
}
}

View File

@ -4,7 +4,6 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.common.util.EntranceUtils
import com.gh.download.DownloadManager
import com.gh.gamecenter.NormalActivity
import com.gh.gamecenter.R

View File

@ -0,0 +1,59 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize
@Parcelize
data class CatalogEntity(
@SerializedName("_id")
var id: String = "",
var name: String = "",
var switch: CatalogSwitch = CatalogSwitch(),
@SerializedName("has_special")
var hasSpecial: Boolean = false,
@SerializedName("sub_catalogs")
var subCatalog: List<SubCatalogEntity> = emptyList()
): Parcelable
@Parcelize
data class SubCatalogEntity(
@SerializedName("_id")
var id: String = "",
var name: String = "",
var icon: String = "",
var link: LinkEntity = LinkEntity(),
var recommended: Boolean = false
): Parcelable
@Parcelize
data class CatalogSwitch(
@SerializedName("sort_hot")
var hotSort: String = "",
@SerializedName("sort_new")
var newSort: String = "",
@SerializedName("sort_star")
var starSort: String = ""
): Parcelable
@Parcelize
data class SpecialCatalogEntity(
@SerializedName("_id")
var id: String = "",
var type: String = "",
var link: SpecialLink = SpecialLink(),
var image: Image = Image()
): Parcelable
@Parcelize
data class SpecialLink(
@SerializedName("_id")
var id: String = "",
var data: List<GameEntity> = emptyList()
): LinkEntity(), Parcelable
@Parcelize
data class Image(
var url: String = "",
var title: String = ""
): Parcelable

View File

@ -24,6 +24,7 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.ImagePagerAdapter
import com.gh.gamecenter.adapter.viewholder.*
import com.gh.gamecenter.baselist.LoadStatus
import com.gh.gamecenter.catalog.CatalogActivity
import com.gh.gamecenter.category.CategoryDirectoryActivity
import com.gh.gamecenter.databinding.*
import com.gh.gamecenter.entity.CommunityEntity
@ -419,6 +420,7 @@ class GameFragmentAdapter(context: Context,
entity.type == "column_collection" -> DirectUtils.directToColumnCollection(mContext, entity.link!!, -1, "(推荐入口)")
entity.type == "block" -> mContext.startActivity(BlockActivity.getIntent(mContext, entity, "(推荐入口)"))
entity.type == "category" -> mContext.startActivity(CategoryDirectoryActivity.getIntent(mContext, entity.link!!, entity.text!!))
entity.type == "catalog" -> mContext.startActivity(CatalogActivity.getIntent(mContext, entity.link!!, entity.text!!))
entity.type == "column" -> {
SubjectActivity.startSubjectActivity(mContext, entity.link, entity.text, entity.order
, StringUtils.buildString("(游戏-专题:", entity.name, "[1-", (data + 1).toString(), "]", ")"))

View File

@ -11,6 +11,7 @@ import com.gh.gamecenter.entity.AuthDialogEntity;
import com.gh.gamecenter.entity.AvatarBorderEntity;
import com.gh.gamecenter.entity.BackgroundImageEntity;
import com.gh.gamecenter.entity.BadgeEntity;
import com.gh.gamecenter.entity.CatalogEntity;
import com.gh.gamecenter.entity.CategoryEntity;
import com.gh.gamecenter.entity.CommentEntity;
import com.gh.gamecenter.entity.CommentnumEntity;
@ -60,6 +61,7 @@ import com.gh.gamecenter.entity.SettingsEntity;
import com.gh.gamecenter.entity.SignEntity;
import com.gh.gamecenter.entity.SimpleGameEntity;
import com.gh.gamecenter.entity.SimulatorEntity;
import com.gh.gamecenter.entity.SpecialCatalogEntity;
import com.gh.gamecenter.entity.SubjectEntity;
import com.gh.gamecenter.entity.SubjectRecommendEntity;
import com.gh.gamecenter.entity.SubjectRefreshEntity;
@ -301,7 +303,7 @@ public interface ApiService {
* 获取专题数据
*/
@GET("columns/{column_id}/games")
Observable<List<GameEntity>> getColumn(@Path("column_id") String column_id, @Query("sort") String sort, @Query("filter") String order, @Query("page") int page);
Single<List<GameEntity>> getColumn(@Path("column_id") String column_id, @Query("sort") String sort, @Query("filter") String order, @Query("page") int page);
/**
* 获取专题数据标题
@ -2720,4 +2722,22 @@ public interface ApiService {
*/
@GET("devices/{device_id}/download_simulator_games")
Single<List<GameEntity>> getSimulatorGames(@Path("device_id") String deviceId, @Query("page") int page, @Query("filter") String filter);
/**
* 获取一级分类数据
*/
@GET("catalogs/{catalog_id}")
Single<CatalogEntity> getCatalogs(@Path("catalog_id") String catalogId);
/**
* 获取一级分类数据 及其 二级分类数据
*/
@GET("catalogs/{catalog_id}/{primary_catalog_id}")
Single<CatalogEntity> getSubCatalogs(@Path("catalog_id") String catalogId, @Path("primary_catalog_id") String primaryCatalogId);
/**
* 获取精选分类
*/
@GET("catalogs/{catalog_id}/special")
Single<List<SpecialCatalogEntity>> getSpecialCatalogs(@Path("catalog_id") String catalogId, @Query("page") int page);
}

View File

@ -12,6 +12,7 @@ import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import retrofit2.HttpException
@ -23,7 +24,9 @@ class SubjectListViewModel(application: Application,
var selectedLabelList = arrayListOf<String>()
var selectedFilterSize = SubjectSettingEntity.Size(text = "全部大小")
override fun provideDataObservable(page: Int): Observable<List<GameEntity>> {
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? = null
override fun provideDataSingle(page: Int): Single<MutableList<GameEntity>> {
return RetrofitManager.getInstance(getApplication()).sensitiveApi.getColumn(
subjectData.subjectId,
subjectData.sort,