feat: 【光环助手V5.15.0】新增 猜你喜欢-发现页(完成UI、接口对接) https://git.shanqu.cc/pm/halo/halo-app-issues/-/issues/2065

This commit is contained in:
张玉久
2022-10-26 12:06:00 +08:00
parent 2cb91a7917
commit b032f054f9
33 changed files with 1178 additions and 36 deletions

View File

@ -739,6 +739,10 @@
android:name=".BbsCertificationActivity"
android:screenOrientation="portrait" />
<activity
android:name=".discovery.DiscoveryActivity"
android:screenOrientation="portrait" />
<!-- <activity-->
<!-- android:name="${applicationId}.douyinapi.DouYinEntryActivity"-->
<!-- android:launchMode="singleTask"-->

View File

@ -6,13 +6,11 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.CountDownTimer;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.text.Html;
import android.text.Spannable;
@ -1325,6 +1323,7 @@ public class DialogUtils {
dialog.show();
}
}
public static void showGameH5DownloadDialog(Context context, GameEntity gameEntity, RegionSetting.GameH5Download gameH5Download) {
context = checkDialogContext(context);
@ -1906,13 +1905,21 @@ public class DialogUtils {
}
}
@SuppressLint("SetTextI18n")
public static void showReportReasonDialog(Context context, ArrayList<String> items, ReportReasonCallBack callBack) {
showReportReasonDialog(context, items, "", callBack);
}
@SuppressLint("SetTextI18n")
public static void showReportReasonDialog(Context context, ArrayList<String> items, String title, ReportReasonCallBack callBack) {
context = checkDialogContext(context);
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
DialogReportReasonBinding binding = DialogReportReasonBinding.inflate(LayoutInflater.from(context));
if (!title.isEmpty()) {
binding.reasonTitle.setText(title);
}
ReportReasonAdapter reportReasonAdapter = new ReportReasonAdapter(context, items, reason -> {
if (reason.equals("其他原因")) {
binding.reasonTitle.setText(R.string.report_reason_other_title);

View File

@ -35,6 +35,7 @@ import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.discovery.DiscoveryActivity
import com.gh.gamecenter.download.DownloadFragment.Companion.INDEX_UPDATE
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBSkip
@ -95,7 +96,14 @@ object DirectUtils {
* 跳转到特定页面,根据 [type] 决定跳转页面,[path] 为跳转前的页面名称
*/
@JvmStatic
fun directToSpecificPage(context: Context, type: String, link: String, text: String? = "", entrance: String? = null, path: String? = null) {
fun directToSpecificPage(
context: Context,
type: String,
link: String,
text: String? = "",
entrance: String? = null,
path: String? = null
) {
when (type) {
HOST_ARTICLE -> directToArticle(context, id = link, entrance = entrance)
@ -111,9 +119,19 @@ object DirectUtils {
HOST_WEB -> directToWebView(context, url = link, entrance = entrance)
HOST_DOWNLOAD -> directToDownloadManagerAndStartDownload(context, gameId = link, packageName = text, entrance = entrance)
HOST_DOWNLOAD -> directToDownloadManagerAndStartDownload(
context,
gameId = link,
packageName = text,
entrance = entrance
)
HOST_UPDATE -> directToDownloadManagerAndStartUpdate(context, gameId = link, packageName = text, entrance = entrance)
HOST_UPDATE -> directToDownloadManagerAndStartUpdate(
context,
gameId = link,
packageName = text,
entrance = entrance
)
HOST_LIBAO -> directToGiftDetail(context, giftId = link, entrance = entrance)
@ -126,7 +144,13 @@ object DirectUtils {
directToLinkPage(context, linkEntity, entrance, path, null)
}
fun directToLinkPage(context: Context, linkEntity: LinkEntity, entrance: String, path: String, exposureEvent: ExposureEvent? = null) {
fun directToLinkPage(
context: Context,
linkEntity: LinkEntity,
entrance: String,
path: String,
exposureEvent: ExposureEvent? = null
) {
directToLinkPage(context, linkEntity, entrance, path, exposureEvent, null)
}
@ -209,9 +233,21 @@ object DirectUtils {
directToCommunity(context, CommunityEntity(linkEntity.link!!, linkEntity.text!!))
}
"community_article", "社区文章" -> directToCommunityArticle(context, linkEntity.community!!, linkEntity.link!!, entrance, path)
"community_article", "社区文章" -> directToCommunityArticle(
context,
linkEntity.community!!,
linkEntity.link!!,
entrance,
path
)
"community_column", "社区专题" -> directToCommunityColumn(context, linkEntity.community, linkEntity.link!!, entrance, path)
"community_column", "社区专题" -> directToCommunityColumn(
context,
linkEntity.community,
linkEntity.link!!,
entrance,
path
)
"community_special_column" -> directAskColumnDetail(
context, linkEntity.link
@ -223,7 +259,11 @@ object DirectUtils {
linkEntity.link!!.contains("v.douyin") && PackageHelper.localPackageNameSet.contains("com.ss.android.ugc.aweme") -> {
directDouyin(context, "1402577827140941")
}
else -> directToWebView(context, url = linkEntity.link!!, entrance = BaseActivity.mergeEntranceAndPath(entrance, path))
else -> directToWebView(
context,
url = linkEntity.link!!,
entrance = BaseActivity.mergeEntranceAndPath(entrance, path)
)
}
}
@ -233,7 +273,16 @@ object DirectUtils {
"qqqun", "QQ群" -> directToQqGroup(context, linkEntity.link!!)
"tag" -> context.startActivity(TagsActivity.getIntent(context, linkEntity.text!!, linkEntity.title, entrance, path, exposureEvent?.source))
"tag" -> context.startActivity(
TagsActivity.getIntent(
context,
linkEntity.text!!,
linkEntity.title,
entrance,
path,
exposureEvent?.source
)
)
"all_community_article" -> directSimpleArticleList(
context, linkEntity.link
@ -244,7 +293,14 @@ object DirectUtils {
"catalog" -> directCatalog(context, linkEntity.link!!, linkEntity.text!!, entrance, path)
"category_v2" -> directCategoryV2(context, linkEntity.link!!, linkEntity.text!!, entrance, path, exposureEvent)
"category_v2" -> directCategoryV2(
context,
linkEntity.link!!,
linkEntity.text!!,
entrance,
path,
exposureEvent
)
"block", "版块" -> {
if (linkEntity.link.isNullOrEmpty()) return
@ -365,7 +421,13 @@ object DirectUtils {
"halo_tab" -> directToPersonalTab(context)
"common_collection" -> directToCommonCollectionDetail(context, linkEntity.link ?: "", linkEntity.blockId, linkEntity.blockName, entrance)
"common_collection" -> directToCommonCollectionDetail(
context,
linkEntity.link ?: "",
linkEntity.blockId,
linkEntity.blockName,
entrance
)
//"h5_game_center" -> directLetoGameCenter(context)
@ -373,6 +435,8 @@ object DirectUtils {
"game_list_detail" -> directToGameCollectionDetail(context, linkEntity.link ?: "", entrance)
"explore_column" -> context.startActivity(DiscoveryActivity.getIntent(context, entrance))
"" -> {
// do nothing
}
@ -416,7 +480,13 @@ object DirectUtils {
* 跳转至专题合集
*/
@JvmStatic
fun directToColumnCollection(context: Context, id: String, position: Int = -1, entrance: String, columnName: String = "") {
fun directToColumnCollection(
context: Context,
id: String,
position: Int = -1,
entrance: String,
columnName: String = ""
) {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, ColumnCollectionDetailActivity::class.java.name)
@ -594,7 +664,12 @@ object DirectUtils {
* 跳转到游戏评分详情
*/
@JvmStatic
fun directToGameRatingDetail(context: Context, gameId: String? = "", commentId: String? = "", entrance: String? = null) {
fun directToGameRatingDetail(
context: Context,
gameId: String? = "",
commentId: String? = "",
entrance: String? = null
) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_GAMEID, gameId)
@ -618,7 +693,12 @@ object DirectUtils {
}
@JvmStatic
fun directToGameDetail(context: Context, id: String, defaultTab: Int = GameDetailFragment.INDEX_DESC, entrance: String? = null) {
fun directToGameDetail(
context: Context,
id: String,
defaultTab: Int = GameDetailFragment.INDEX_DESC,
entrance: String? = null
) {
val bundle = Bundle()
bundle.putString(KEY_TO, GameDetailActivity::class.java.name)
bundle.putString(KEY_ENTRANCE, entrance)
@ -634,14 +714,23 @@ object DirectUtils {
// 专栏
@JvmStatic
fun directToSubject(context: Context, id: String, subjectName: String? = "", entrance: String? = null, exposureEvent: ExposureEvent? = null) {
fun directToSubject(
context: Context,
id: String,
subjectName: String? = "",
entrance: String? = null,
exposureEvent: ExposureEvent? = null
) {
if (id.isEmpty()) return
val bundle = Bundle()
val subjectData = SubjectData(subjectId = id, subjectName = subjectName, isOrder = false)
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, SubjectActivity::class.java.name)
bundle.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, subjectData)
if (exposureEvent != null) bundle.putParcelableArrayList(KEY_EXPOSURE_SOURCE_LIST, ArrayList(exposureEvent.source))
if (exposureEvent != null) bundle.putParcelableArrayList(
KEY_EXPOSURE_SOURCE_LIST,
ArrayList(exposureEvent.source)
)
jumpActivity(context, bundle)
}
@ -693,7 +782,12 @@ object DirectUtils {
* 跳转到下载管理器并开始下载 [gameId] 和 [packageName] 用于唯一确定一个下载文件
*/
@JvmStatic
fun directToDownloadManagerAndStartDownload(context: Context, gameId: String? = "", packageName: String? = "", entrance: String? = null) {
fun directToDownloadManagerAndStartDownload(
context: Context,
gameId: String? = "",
packageName: String? = "",
entrance: String? = null
) {
DownloadHelper.createABrandNewDownloadTaskQuietly(gameId, packageName) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
@ -706,7 +800,12 @@ object DirectUtils {
}
@JvmStatic
fun directToDownloadManagerAndStartUpdate(context: Context, gameId: String? = "", packageName: String? = "", entrance: String? = null) {
fun directToDownloadManagerAndStartUpdate(
context: Context,
gameId: String? = "",
packageName: String? = "",
entrance: String? = null
) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, DownloadManagerActivity.TAG)
@ -892,7 +991,13 @@ object DirectUtils {
}
@JvmStatic
fun directToCommunityArticle(context: Context, articleId: String?, communityId: String?, entrance: String?, path: String?) {
fun directToCommunityArticle(
context: Context,
articleId: String?,
communityId: String?,
entrance: String?,
path: String?
) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_PATH, path)
@ -903,7 +1008,13 @@ object DirectUtils {
}
@JvmStatic
fun directToCommunityArticle(context: Context, community: CommunityEntity?, articleId: String?, entrance: String?, path: String?) {
fun directToCommunityArticle(
context: Context,
community: CommunityEntity?,
articleId: String?,
entrance: String?,
path: String?
) {
if (articleId.isNullOrEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
@ -918,7 +1029,13 @@ object DirectUtils {
* 跳转到社区专题
*/
@JvmStatic
fun directToCommunityColumn(context: Context, community: CommunityEntity?, subjectId: String, entrance: String?, path: String?) {
fun directToCommunityColumn(
context: Context,
community: CommunityEntity?,
subjectId: String,
entrance: String?,
path: String?
) {
if (subjectId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_PATH, path)
@ -1008,7 +1125,12 @@ object DirectUtils {
* @param fixedTopAmwayCommentId 需要置顶的安利Id
*/
@JvmStatic
fun directToAmway(context: Context, fixedTopAmwayCommentId: String? = null, entrance: String? = null, path: String? = "") {
fun directToAmway(
context: Context,
fixedTopAmwayCommentId: String? = null,
entrance: String? = null,
path: String? = ""
) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, AmwayActivity::class.java.name)
@ -1149,7 +1271,13 @@ object DirectUtils {
* 跳转分类
*/
@JvmStatic
fun directCategoryDirectory(context: Context, categoryId: String, categoryTitle: String, entrance: String? = null, path: String? = "") {
fun directCategoryDirectory(
context: Context,
categoryId: String,
categoryTitle: String,
entrance: String? = null,
path: String? = ""
) {
if (categoryId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, CategoryDirectoryActivity::class.java.name)
@ -1198,7 +1326,10 @@ object DirectUtils {
bundle.putString(KEY_CATEGORY_ID, categoryId)
bundle.putString(KEY_CATEGORY_TITLE, categoryTitle)
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
if (exposureEvent != null) bundle.putParcelableArrayList(KEY_EXPOSURE_SOURCE_LIST, ArrayList(exposureEvent.source))
if (exposureEvent != null) bundle.putParcelableArrayList(
KEY_EXPOSURE_SOURCE_LIST,
ArrayList(exposureEvent.source)
)
jumpActivity(context, bundle)
}
@ -1206,7 +1337,13 @@ object DirectUtils {
* 跳转到问题标签详情
*/
@JvmStatic
fun directAskColumnLabelDetail(context: Context, tag: String, community: CommunityEntity, entrance: String? = null, path: String? = "") {
fun directAskColumnLabelDetail(
context: Context,
tag: String,
community: CommunityEntity,
entrance: String? = null,
path: String? = ""
) {
// val bundle = Bundle()
// bundle.putString(KEY_TO, AskColumnDetailActivity::class.java.name)
// bundle.putString(KEY_ASK_TAG, tag)
@ -1220,7 +1357,13 @@ object DirectUtils {
* 跳转到专栏详情
*/
@JvmStatic
fun directAskColumnDetail(context: Context, columnId: String, community: CommunityEntity, entrance: String? = null, path: String? = "") {
fun directAskColumnDetail(
context: Context,
columnId: String,
community: CommunityEntity,
entrance: String? = null,
path: String? = ""
) {
// if (columnId.isEmpty()) return
// val bundle = Bundle()
// bundle.putString(KEY_TO, AskColumnDetailActivity::class.java.name)
@ -1430,7 +1573,13 @@ object DirectUtils {
Constants.COMMODITY_DETAIL_ADDRESS
}
url = String.format(Locale.CHINA, "%s&shopid=%s&timestamp=%d", url, commodityId, (Date().time / 1000 / 1000.toFloat()).roundToInt())
url = String.format(
Locale.CHINA,
"%s&shopid=%s&timestamp=%d",
url,
commodityId,
(Date().time / 1000 / 1000.toFloat()).roundToInt()
)
directToFullScreenWebPage(context, url, true)
}
@ -1450,7 +1599,13 @@ object DirectUtils {
Constants.ENERGY_RECORD_ADDRESS
}
url = String.format(Locale.CHINA, "%s&position=%s&timestamp=%d", url, position, (Date().time / 1000 / 1000.toFloat()).roundToInt())
url = String.format(
Locale.CHINA,
"%s&position=%s&timestamp=%d",
url,
position,
(Date().time / 1000 / 1000.toFloat()).roundToInt()
)
directToFullScreenWebPage(context, url, true)
}
@ -1478,7 +1633,13 @@ object DirectUtils {
Constants.ORDER_DETAIL_ADDRESS
}
url = String.format(Locale.CHINA, "%s&order_id=%s&timestamp=%d", url, orderId, (Date().time / 1000 / 1000.toFloat()).roundToInt())
url = String.format(
Locale.CHINA,
"%s&order_id=%s&timestamp=%d",
url,
orderId,
(Date().time / 1000 / 1000.toFloat()).roundToInt()
)
directToFullScreenWebPage(context, url, true)
}

View File

@ -0,0 +1,36 @@
package com.gh.gamecenter.discovery
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
/**
* 猜你喜欢-发现页
*/
class DiscoveryActivity : ToolBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateStatusBarColor(R.color.background_white, R.color.background_white)
setNavigationTitle("发现")
}
override fun isAutoResetViewBackgroundEnabled(): Boolean = true
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
companion object {
fun getIntent(context: Context, entrance: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
return getTargetIntent(context, DiscoveryActivity::class.java, DiscoveryFragment::class.java, bundle)
}
}
}

View File

@ -0,0 +1,262 @@
package com.gh.gamecenter.discovery
import android.content.Context
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
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.DialogUtils
import com.gh.common.util.DirectUtils
import com.gh.common.util.DownloadItemUtils
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.ItemViewType
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.ifLogin
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.viewholder.FooterViewHolder
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.databinding.ItemRecommendInterestBinding
import com.gh.gamecenter.databinding.ItemRecommendInterestFooterBinding
import com.gh.gamecenter.databinding.ItemRecommendInterestImageBinding
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.fragment.MainWrapperFragment
import com.gh.gamecenter.game.GameItemViewHolder
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.EventBus
class DiscoveryAdapter(
context: Context,
val mViewModel: DiscoveryViewModel,
val entrance: String
) : ListAdapter<DiscoveryItemData>(context), IExposable {
private val mExposureEventSparseArray: SparseArray<ExposureEvent> = SparseArray()
override fun getItemViewType(position: Int): Int {
when (position) {
itemCount - 2 -> {
return ITEM_DIRECT_GAME_BLOCK
}
itemCount - 1 -> {
return ItemViewType.ITEM_FOOTER
}
else -> {
val itemData = mEntityList[position]
return when {
itemData.interestCardLabels != null -> {
ITEM_RECOMMEND_INTEREST
}
itemData.interestImageCardLabel != null -> {
ITEM_RECOMMEND_INTEREST_IMAGE
}
else -> {
ItemViewType.GAME_NORMAL
}
}
}
}
}
override fun areItemsTheSame(oldItem: DiscoveryItemData?, newItem: DiscoveryItemData?): Boolean {
return oldItem?.gameEntity?.id == newItem?.gameEntity?.id
|| oldItem?.interestCardLabels?.firstOrNull()?.id == newItem?.interestCardLabels?.firstOrNull()?.id
|| oldItem?.interestImageCardLabel?.id == newItem?.interestImageCardLabel?.id
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ItemViewType.GAME_NORMAL -> {
GameItemViewHolder(parent.toBinding())
}
ItemViewType.ITEM_FOOTER -> {
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
}
ITEM_DIRECT_GAME_BLOCK -> {
DirectGameBlockViewHolder(parent.toBinding())
}
ITEM_RECOMMEND_INTEREST -> {
RecommendInterestViewHolder(parent.toBinding())
}
ITEM_RECOMMEND_INTEREST_IMAGE -> {
RecommendInterestImageViewHolder(parent.toBinding())
}
else -> {
throw NullPointerException()
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is GameItemViewHolder -> {
val itemData = mEntityList[position]
val gameEntity = itemData.gameEntity!!
holder.bindGameItem(gameEntity)
val exposureSources = ArrayList<ExposureSource>()
val event = ExposureEvent.createEvent(gameEntity, exposureSources, null, ExposureType.EXPOSURE)
mExposureEventSparseArray.put(position, event)
holder.itemView.setOnClickListener {
GameDetailActivity.startGameDetailActivity(
mContext,
gameEntity,
StringUtils.buildString("(${entrance}", "-列表[", (position).toString(), "])"),
traceEvent = event
)
}
holder.itemView.setOnLongClickListener {
discoveryFeedback(position, gameEntity)
true
}
DownloadItemUtils.setOnClickListener(
mContext,
holder.binding.downloadBtn,
gameEntity,
position,
this,
StringUtils.buildString("(${entrance}", "-列表[", (position).toString(), "])"),
StringUtils.buildString(entrance, ":", gameEntity.name),
event
)
DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding), true)
holder.itemView.setPadding(16f.dip2px(), 8f.dip2px(), 16f.dip2px(), 8f.dip2px())
}
is RecommendInterestViewHolder -> {
val labels = mEntityList[position].interestCardLabels ?: return
holder.binding.labelTv1.goneIf(labels.size < 1) {
holder.binding.labelTv1.text = labels[0].title
holder.binding.labelTv1.setOnClickListener {
DirectUtils.directToLinkPage(holder.binding.root.context, labels[0], entrance, "")
}
}
holder.binding.labelTv2.goneIf(labels.size < 2) {
holder.binding.labelTv2.text = labels[1].title
holder.binding.labelTv2.setOnClickListener {
DirectUtils.directToLinkPage(holder.binding.root.context, labels[1], entrance, "")
}
}
holder.binding.labelTv3.goneIf(labels.size < 3) {
holder.binding.labelTv3.text = labels[2].title
holder.binding.labelTv3.setOnClickListener {
DirectUtils.directToLinkPage(holder.binding.root.context, labels[2], entrance, "")
}
}
}
is RecommendInterestImageViewHolder -> {
val label = mEntityList[position].interestImageCardLabel ?: return
holder.binding.root.setOnClickListener {
DirectUtils.directToLinkPage(holder.binding.root.context, label, entrance, "")
}
}
is DirectGameBlockViewHolder -> {
holder.binding.root.setOnClickListener {
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_GAME))
}
}
is FooterViewHolder -> {
holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver)
}
}
}
override fun getItemCount(): Int {
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 2
}
private fun discoveryFeedback(position: Int, gameEntity: GameEntity) {
mContext.ifLogin(entrance) {
DialogUtils.showReportReasonDialog(
mContext,
Constants.FEEDBACK_REASON_LIST.toList() as ArrayList<String>,
"不喜欢的原因"
) { reason, desc ->
mViewModel.discoveryFeedback(gameEntity.id, reason, gameEntity.type ?: "") {
mEntityList.removeAt(position)
notifyItemRemoved(position)
}
}
}
}
fun notifyItemByDownload(download: DownloadEntity) {
val positionAndPackageMap = mViewModel.positionAndPackageMap
for (key in positionAndPackageMap.keys) {
if (key.contains(download.packageName)) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].gameEntity?.let {
it.getEntryMap()[download.platform] = download
}
notifyItemChanged(position)
}
}
}
}
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
val positionAndPackageMap = mViewModel.positionAndPackageMap
for (key in positionAndPackageMap.keys) {
if (key.contains(status.packageName)) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].gameEntity?.let {
it.getEntryMap().remove(status.platform)
}
notifyItemChanged(position)
}
}
}
}
fun clearPositionAndPackageMap() {
mViewModel.positionAndPackageMap.clear()
}
override fun getEventByPosition(pos: Int): ExposureEvent? {
return mExposureEventSparseArray.get(pos)
}
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
return null
}
class RecommendInterestViewHolder(val binding: ItemRecommendInterestBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
}
class RecommendInterestImageViewHolder(val binding: ItemRecommendInterestImageBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
}
class DirectGameBlockViewHolder(val binding: ItemRecommendInterestFooterBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
}
companion object {
const val ITEM_RECOMMEND_INTEREST = 200
const val ITEM_RECOMMEND_INTEREST_IMAGE = 201
const val ITEM_DIRECT_GAME_BLOCK = 202
}
}

View File

@ -0,0 +1,158 @@
package com.gh.gamecenter.discovery
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.RelativeLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import com.ethanhua.skeleton.Skeleton
import com.gh.common.util.DialogUtils
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.common.baselist.LazyListFragment
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.getBitmapFromView
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.LayoutDiscoveryGuideBinding
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class DiscoveryFragment : LazyListFragment<DiscoveryItemData, DiscoveryViewModel>() {
private var mAdapter: DiscoveryAdapter? = null
private val dataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) {
showUnzipFailureDialog(downloadEntity)
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
}
}
override fun initRealView() {
super.initRealView()
mSkeletonScreen = Skeleton.bind(mSkeletonScreenView)
.shimmer(true)
.angle(Constants.SHIMMER_ANGLE)
.color(R.color.skeleton_shimmer_color)
.duration(Constants.SHIMMER_DURATION)
.maskWidth(Constants.MASK_WIDTH)
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
.apply {
load(R.layout.fragment_discovery_skeleton)
}
.show()
}
override fun provideListAdapter(): ListAdapter<*> {
return mAdapter ?: DiscoveryAdapter(requireContext(), mListViewModel,"发现页").also { mAdapter = it }
}
override fun onFragmentResume() {
super.onFragmentResume()
DownloadManager.getInstance().addObserver(dataWatcher)
}
override fun onFragmentPause() {
super.onFragmentPause()
DownloadManager.getInstance().removeObserver(dataWatcher)
}
override fun getItemDecoration(): RecyclerView.ItemDecoration? = null
private fun showUnzipFailureDialog(downloadEntity: DownloadEntity) {
val data = mListViewModel.positionAndPackageMap
for (gameAndPosition in data) {
if (gameAndPosition.key.contains(downloadEntity.packageName)) {
val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value)
if (targetView != null) {
DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity)
return
}
}
}
}
override fun onChanged(ts: MutableList<DiscoveryItemData>?) {
super.onChanged(ts)
val isFirstGuide = SPUtils.getBoolean(Constants.SP_DISCOVERY_GUIDE, true)
if (!isFirstGuide) return
AppExecutor.uiExecutor.executeWithDelay({
showGuideView()
SPUtils.setBoolean(Constants.SP_DISCOVERY_GUIDE, false)
}, 800)
}
private fun showGuideView() {
if (!isSupportVisible) return
val firstView = mListRv.layoutManager?.findViewByPosition(0)
if (firstView != null && requireActivity() is AppCompatActivity) {
val location = IntArray(2)
firstView.getLocationInWindow(location)
val decorView = requireActivity().window.decorView as? FrameLayout
val guideViewBinding =
LayoutDiscoveryGuideBinding.inflate(LayoutInflater.from(requireContext()), decorView, true)
val originalBackground = firstView.background
firstView.setBackgroundColor(
if (mIsDarkModeOn) R.color.black.toColor(requireContext()) else R.color.white.toColor(
requireContext()
)
)
guideViewBinding.guideImageContainer.setCardBackgroundColor(
if (mIsDarkModeOn) R.color.black.toColor(requireContext()) else R.color.white.toColor(
requireContext()
)
)
val snapshotBitmap = firstView.getBitmapFromView(
firstView.width - 16f.dip2px(),
firstView.height,
-8f.dip2px().toFloat(),
0f
)
firstView.background = originalBackground
guideViewBinding.guideImage.setImageBitmap(snapshotBitmap)
(guideViewBinding.guideImageContainer.layoutParams as RelativeLayout.LayoutParams).run {
topMargin = location[1]
guideViewBinding.guideImageContainer.layoutParams = this
}
guideViewBinding.root.setOnClickListener {
decorView?.removeView(it)
}
}
}
override fun isAutomaticLoad(): Boolean = false
// 下载被删除事件
@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()
}
}
}

View File

@ -0,0 +1,12 @@
package com.gh.gamecenter.discovery
import androidx.annotation.Keep
import com.gh.gamecenter.entity.DiscoveryGameCardLabel
import com.gh.gamecenter.entity.GameEntity
@Keep
data class DiscoveryItemData(
val gameEntity: GameEntity? = null,
val interestCardLabels: ArrayList<DiscoveryGameCardLabel>? = null,
val interestImageCardLabel: DiscoveryGameCardLabel? = null
)

View File

@ -0,0 +1,159 @@
package com.gh.gamecenter.discovery
import android.annotation.SuppressLint
import android.app.Application
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.EnvHelper
import com.gh.gamecenter.common.utils.observableToMain
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.entity.DiscoveryGameCardLabel
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.entity.TagEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
import okhttp3.ResponseBody
import retrofit2.HttpException
class DiscoveryViewModel(application: Application) : ListViewModel<GameEntity, DiscoveryItemData>(application) {
val positionAndPackageMap = HashMap<String, Int>()
private val mApi = RetrofitManager.getInstance().api
private var mGameTags: ArrayList<LinkEntity>? = null
private var mDiscoveryGameCardLabels: ArrayList<DiscoveryGameCardLabel>? = null
private var mDiscoveryGameCardLabelMap: LinkedHashMap<String, ArrayList<DiscoveryGameCardLabel>> = linkedMapOf()
init {
getCardLabels()
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) {
val itemDataList = arrayListOf<DiscoveryItemData>()
it.forEachIndexed { index, gameEntity ->
itemDataList.add(DiscoveryItemData(gameEntity))
addGamePositionAndPackage(index, gameEntity)
//第6、12、18、24个游戏后面固定插入兴趣推荐卡片
when (index) {
5 -> {
itemDataList.add(DiscoveryItemData(interestCardLabels = mDiscoveryGameCardLabelMap["卡片一"]))
}
11 -> {
itemDataList.add(
DiscoveryItemData(
interestImageCardLabel = mDiscoveryGameCardLabelMap["卡片二"]?.get(0)
)
)
}
17 -> {
itemDataList.add(DiscoveryItemData(interestCardLabels = mDiscoveryGameCardLabelMap["卡片三"]))
}
23 -> {
itemDataList.add(DiscoveryItemData(interestCardLabels = mDiscoveryGameCardLabelMap["卡片四"]))
}
else -> {
//do nothing
}
}
}
mResultLiveData.postValue(itemDataList)
}
}
override fun provideDataObservable(page: Int): Observable<MutableList<GameEntity>>? {
return mApi.getDiscoveryGames(page).map {
if (page == 1) {
mGameTags = it.gameTags
groupingDiscoveryLabel()
}
it.games
}
}
//按照card字段分组
private fun groupingDiscoveryLabel() {
mDiscoveryGameCardLabelMap.clear()
var tagIndex = 0
mDiscoveryGameCardLabels?.forEach {
if (mDiscoveryGameCardLabelMap.contains(it.card)) {
mDiscoveryGameCardLabelMap[it.card]?.add(it)
} else {
mDiscoveryGameCardLabelMap[it.card] = arrayListOf(it)
}
}
//如果it.link为空需要在mGameTags中依次获取
mDiscoveryGameCardLabelMap.keys.forEach {
val labels = mDiscoveryGameCardLabelMap[it]
labels?.forEach { label ->
if (!mGameTags.isNullOrEmpty() && mGameTags!!.size > tagIndex && label.link.isNullOrEmpty()) {
val gameTag = mGameTags!![tagIndex]
label.link = gameTag.link
label.type = gameTag.type
label.text = gameTag.linkText
label.title = gameTag.linkText
tagIndex++
}
}
}
}
private fun getCardLabels() {
mApi.cardLabels
.compose(observableToMain())
.subscribe(object : Response<ArrayList<DiscoveryGameCardLabel>>() {
override fun onResponse(response: ArrayList<DiscoveryGameCardLabel>?) {
super.onResponse(response)
mDiscoveryGameCardLabels = response
initLoadParams()
loadData()
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
mLoadStatusLiveData.value = LoadStatus.INIT_FAILED
}
})
}
@SuppressLint("CheckResult")
fun discoveryFeedback(gameId: String, reason: String, type: String, callback: () -> Unit) {
val paramsMap = mapOf(
"reason" to reason,
"type" to type
)
mApi.discorveryFeedback(gameId, paramsMap.toRequestBody())
.compose(singleToMain())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
callback.invoke()
}
})
}
private fun addGamePositionAndPackage(position: Int, game: GameEntity) {
var packages = ""
for (apkEntity in game.getApk()) {
packages += apkEntity.packageName
}
positionAndPackageMap[packages + (position)] = position
game.gameLocation = GameEntity.GameLocation.INDEX
game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name))
}
override fun load(loadType: LoadType?) {
if (loadType == LoadType.REFRESH) {
getCardLabels()
} else {
super.load(loadType)
}
}
}

View File

@ -0,0 +1,14 @@
package com.gh.gamecenter.entity
import com.gh.gamecenter.common.entity.LinkEntity
import com.google.gson.annotations.SerializedName
data class DiscoveryGameCardEntity(
val more: Int,
@SerializedName("game_tags")
val gameTags: ArrayList<LinkEntity> = arrayListOf(),
@SerializedName("data")
val games: ArrayList<GameEntity> = arrayListOf()
)

View File

@ -0,0 +1,11 @@
package com.gh.gamecenter.entity
import com.gh.gamecenter.common.entity.LinkEntity
import com.google.gson.annotations.SerializedName
data class DiscoveryGameCardLabel(
@SerializedName("_id")
val id: String = "",
val card: String = "",
val order: Int = 0,
) : LinkEntity()

View File

@ -291,6 +291,9 @@ data class GameEntity(
var welcomeDialogId: String? = null,
var welcomeDialogTitle: String? = null,
@SerializedName("column_rank")
var columnRank: ColumnRank? = null,//榜单详情
// 专题id用于曝光使用
var subjectId: String? = null,
// 专题名字,用于曝光使用
@ -836,6 +839,12 @@ data class GameEntity(
var url: String = ""
) : Parcelable
}
@Parcelize
data class ColumnRank(
val name: String = "",
val position: Int = 0
) : Parcelable
}
@Parcelize

View File

@ -25,6 +25,7 @@ import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.FragmentMainHomeWrapperBinding
import com.gh.gamecenter.databinding.TabItemMainBinding
import com.gh.gamecenter.discovery.DiscoveryFragment
import com.gh.gamecenter.entity.SubjectData
import com.gh.gamecenter.entity.SubjectRecommendEntity
import com.gh.gamecenter.game.GameFragment
@ -207,7 +208,10 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
if (isContentStyleChanged) {
updateIndicatorDrawable()
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDisplayingLightContent && !mIsDarkModeOn)
DisplayUtils.setLightStatusBar(
requireActivity(),
!mIsDisplayingLightContent && !mIsDarkModeOn
)
}
mTabTitleList[position].run {
@ -277,10 +281,12 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
tab.customView = tabViewBinding.root
tab.view.setPadding(0, 0, 0, 0)
if (i == 0) {
tab.view.layoutParams = (tab.view.layoutParams as LinearLayout.LayoutParams).apply { setMargins(10F.dip2px(), 0, 0, 0) }
tab.view.layoutParams =
(tab.view.layoutParams as LinearLayout.LayoutParams).apply { setMargins(10F.dip2px(), 0, 0, 0) }
}
if (i == mBinding?.tabLayout?.tabCount!! - 1) {
tab.view.layoutParams = (tab.view.layoutParams as LinearLayout.LayoutParams).apply { setMargins(0, 0, 10F.dip2px(), 0) }
tab.view.layoutParams =
(tab.view.layoutParams as LinearLayout.LayoutParams).apply { setMargins(0, 0, 10F.dip2px(), 0) }
}
}
}
@ -307,7 +313,8 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
currentTab?.offsetRatio = offset / totalHeight.toFloat()
if ((currentTab?.isTopViewShow == true && offset >= totalHeight)
|| currentTab?.isSlideEmpty == true) {
|| currentTab?.isSlideEmpty == true
) {
currentTab.isTopViewShow = false
currentTab.primaryColor = backgroundWhiteColor
currentTab.useLightStyle = false
@ -374,7 +381,11 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
currentTab.currentSelectColor = color
if (currentTab.isTopViewShow) {
val colorInBetween =
ColorUtils.blendARGB(color, R.color.background_white.toColor(requireContext()), currentTab.offsetRatio)
ColorUtils.blendARGB(
color,
R.color.background_white.toColor(requireContext()),
currentTab.offsetRatio
)
currentTab.primaryColor = colorInBetween
updateAppBarStyle(colorInBetween, colorInBetween != R.color.background_white.toColor(requireContext()))
}
@ -476,6 +487,9 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
putString(EntranceConsts.KEY_COLLECTION_ID, tab.link)
putString(EntranceConsts.KEY_COLUMNNAME, tab.text)
})
"explore_column" -> DiscoveryFragment().with(Bundle().apply {
putString(EntranceConsts.KEY_ENTRANCE, "首页")
})
"bbs" -> Fragment()
else -> Fragment()
}

View File

@ -2987,4 +2987,22 @@ public interface ApiService {
*/
@POST("mobile_auth/{user_id}:unbind")
Single<ResponseBody> unBindPhone();
/**
* 获取发现页游戏列表
*/
@GET("home/explore/games")
Observable<DiscoveryGameCardEntity> getDiscoveryGames(@Query("page") int page);
/**
* 获取发现页卡片列表
*/
@GET("home/explore/cards")
Observable<ArrayList<DiscoveryGameCardLabel>> getCardLabels();
/**
* 反馈游戏
*/
@POST("home/explore/games/{game_id}/feedback")
Single<ResponseBody> discorveryFeedback(@Path("game_id") String gameId, @Body RequestBody body);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

View File

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

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_margin="16dp"
android:layout_height="88dp"
android:background="@drawable/bg_skeleton_radius_8">
<View
android:layout_width="112dp"
android:layout_height="16dp"
android:layout_margin="12dp"
android:background="@drawable/bg_skeleton_child_radius_4" />
</RelativeLayout>
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
<include layout="@layout/item_skeleton_discovery_list" />
</LinearLayout>

View File

@ -0,0 +1,100 @@
<?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="@color/background_white">
<View
android:id="@+id/backgroundView"
android:layout_width="0dp"
android:layout_height="88dp"
android:layout_margin="16dp"
android:background="@drawable/bg_shape_space_radius_8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/interestTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginTop="12dp"
android:drawableLeft="@drawable/ic_interest"
android:drawablePadding="4dp"
android:includeFontPadding="false"
android:text="猜你感兴趣的内容"
android:textColor="@color/text_title"
android:textSize="@dimen/secondary_size"
app:layout_constraintStart_toStartOf="@+id/backgroundView"
app:layout_constraintTop_toTopOf="@+id/backgroundView" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginTop="12dp"
android:layout_marginRight="12dp"
android:orientation="horizontal"
android:weightSum="3"
app:layout_constraintEnd_toEndOf="@+id/backgroundView"
app:layout_constraintStart_toStartOf="@+id/backgroundView"
app:layout_constraintTop_toBottomOf="@+id/interestTv">
<TextView
android:id="@+id/labelTv1"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:background="@drawable/bg_shape_white_radius_4"
android:drawableRight="@drawable/ic_interest_arrow"
android:drawablePadding="8dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:text="解谜"
android:textColor="@color/text_subtitle"
android:textSize="@dimen/secondary_size" />
<TextView
android:id="@+id/labelTv2"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:background="@drawable/bg_shape_white_radius_4"
android:drawableRight="@drawable/ic_interest_arrow"
android:drawablePadding="8dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:text="角色扮演"
android:textColor="@color/text_subtitle"
android:textSize="@dimen/secondary_size" />
<TextView
android:id="@+id/labelTv3"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:background="@drawable/bg_shape_white_radius_4"
android:drawableRight="@drawable/ic_interest_arrow"
android:drawablePadding="8dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:text="Steam移植"
android:textColor="@color/text_subtitle"
android:textSize="@dimen/secondary_size" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

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="wrap_content"
android:background="@color/background_white"
android:padding="16dp">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:src="@drawable/bg_recommend_interest_footer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="w,328:56"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

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="wrap_content"
android:background="@color/background_white"
android:padding="16dp">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:src="@drawable/bg_recommend_interest"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="w,328:80"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="8dp">
<View
android:layout_width="64dp"
android:layout_height="64dp"
android:background="@drawable/bg_skeleton_radius_14" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_weight="1"
android:layout_marginLeft="12dp"
android:orientation="vertical">
<View
android:layout_width="72dp"
android:layout_height="16dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="160dp"
android:layout_height="12dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="32dp"
android:layout_height="12dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_skeleton_radius_4" />
</LinearLayout>
<View
android:layout_width="56dp"
android:layout_height="28dp"
android:layout_gravity="center_vertical"
android:background="@drawable/bg_skeleton_radius_4" />
</LinearLayout>

View File

@ -0,0 +1,34 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_alpha_40"
android:clickable="true"
android:focusable="true">
<androidx.cardview.widget.CardView
android:id="@+id/guideImageContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/guideImage"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.cardview.widget.CardView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/guideImageContainer"
android:layout_alignParentRight="true"
android:layout_marginTop="32dp"
android:layout_marginRight="24dp"
android:src="@drawable/ic_discovery_guide" />
</RelativeLayout>

View File

@ -2,6 +2,7 @@ package com.gh.gamecenter.common.baselist;
import android.view.View;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
@ -55,6 +56,8 @@ public abstract class LazyListFragment<T, VM extends BaseListViewModel /* 该泛
@Nullable
protected SkeletonScreen mSkeletonScreen;
@Nullable
protected FrameLayout mSkeletonScreenView;
private int[] lastPositions;
private int lastVisibleItemPosition;
@ -145,6 +148,7 @@ public abstract class LazyListFragment<T, VM extends BaseListViewModel /* 该泛
mReuseNoConn = mCachedView.findViewById(R.id.reuse_no_connection);
mReuseNoData = mCachedView.findViewById(R.id.reuse_none_data);
mDataExceptionView = mCachedView.findViewById(R.id.reuse_data_exception);
mSkeletonScreenView = mCachedView.findViewById(R.id.skeleton);
if (mListRefresh != null) {
mListRefresh.setColorSchemeResources(R.color.theme);

View File

@ -412,6 +412,7 @@ public class Constants {
public static final int PACKAGES_CD = 60 * 1000;
public static final String[] REPORT_LIST = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息", "违法有害信息", "其他原因"};
public static final String[] FEEDBACK_REASON_LIST = new String[]{"重复推荐", "不感兴趣", "游戏太旧", "已经玩过了", "游戏质量差", "与推荐描述不符"};
public static final String ENTRANCE_UNKNOWN = "(unknown)";
@ -442,6 +443,8 @@ public class Constants {
public static final String SP_FOLLOW_SYSTEM_DARK_MODE_ENABLED = "follow_system_dark_mode_enabled";
public static final String SP_TEST_FLAVOR_CHANNEL = "sp_test_flavor_channel";
//发现页引导
public static final String SP_DISCOVERY_GUIDE = "sp_discovery_guide";
// 登入标识
public static final String LOGIN_TAG = "login_tag";

View File

@ -1210,6 +1210,14 @@ fun View.getBitmapFromView(): Bitmap? {
return bitmap
}
fun View.getBitmapFromView(width: Int, height: Int, offsetX: Float, offsetY: Float): Bitmap? {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.translate(offsetX, offsetY)
this.draw(canvas)
return bitmap
}
fun View.setRootBackgroundColor(@ColorRes res: Int) {
if (this.id == View.NO_ID) {
this.id = R.id.root_container

View File

@ -4,6 +4,11 @@
android:layout_height = "match_parent"
android:background="@color/background">
<FrameLayout
android:id="@+id/skeleton"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id = "@+id/list_refresh"
android:layout_width = "match_parent"