Compare commits
17 Commits
pack/updat
...
feat/GHZSC
| Author | SHA1 | Date | |
|---|---|---|---|
| 1613fe00ed | |||
| e955920f7e | |||
| d7f307603b | |||
| 25c60c61d8 | |||
| 7a30967ec6 | |||
| 33aecc1f71 | |||
| c7b3b98ed0 | |||
| 883e250538 | |||
| 0b8cc539da | |||
| 1b63ae871a | |||
| 37d37374b6 | |||
| 23abf14e32 | |||
| fd114f13aa | |||
| b13b196639 | |||
| 5302bc7972 | |||
| 61bab61ccf | |||
| 60635e748f |
@ -72,6 +72,7 @@ android_build:
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- feat/GHZSCY-5098
|
||||
|
||||
# 代码检查
|
||||
sonarqube_analysis:
|
||||
@ -103,6 +104,7 @@ sonarqube_analysis:
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- feat/GHZSCY-5098
|
||||
|
||||
## 发送简易检测结果报告
|
||||
send_sonar_report:
|
||||
@ -121,6 +123,7 @@ send_sonar_report:
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- feat/GHZSCY-5098
|
||||
|
||||
oss-upload&send-email:
|
||||
tags:
|
||||
@ -152,4 +155,5 @@ oss-upload&send-email:
|
||||
- /usr/local/bin/python /ci-android-mail-jira-comment.py
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- release
|
||||
- feat/GHZSCY-5098
|
||||
@ -569,7 +569,16 @@ andResGuard {
|
||||
"R.id.cardMask",
|
||||
"R.id.cardGradientMask",
|
||||
"R.id.gameIconIv",
|
||||
"R.id.titleContainer"
|
||||
"R.id.titleContainer",
|
||||
"R.id.v_login_background",
|
||||
"R.id.tv_login_tips",
|
||||
"R.id.tv_login",
|
||||
"R.id.v_code_background",
|
||||
"R.id.tv_code_tips",
|
||||
"R.id.tv_code",
|
||||
"R.id.tv_copy",
|
||||
"R.id.v_indicator_background",
|
||||
"R.id.v_indicator"
|
||||
]
|
||||
compressFilePattern = [
|
||||
"*.png",
|
||||
|
||||
@ -770,8 +770,19 @@
|
||||
android:name="com.gh.gamecenter.wrapper.ToolbarWrapperActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity android:name=".forum.home.CommunityActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name=".forum.home.CommunityActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".forum.home.follow.FollowDynamicActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.Transparent" />
|
||||
|
||||
<activity
|
||||
android:name=".forum.home.follow.AllFollowedActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppCompatTheme.APP" />
|
||||
|
||||
<!-- <activity-->
|
||||
<!-- android:name="${applicationId}.douyinapi.DouYinEntryActivity"-->
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
package com.gh.common.provider
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.ConcernContentUtils
|
||||
import com.gh.common.view.ImageContainerView
|
||||
import com.gh.gamecenter.ImageViewerActivity.Companion.getIntent
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.common.utils.toArrayList
|
||||
import com.gh.gamecenter.core.provider.IConcernArticleUtilsProvider
|
||||
import com.gh.gamecenter.databinding.RecyclerGameArticleBinding
|
||||
import com.gh.gamecenter.message.R
|
||||
|
||||
@Route(path = RouteConsts.provider.concernContentUtils, name = "ConcernContentUtils暴露服务")
|
||||
class ConcernArticleUtilsProviderImpl : IConcernArticleUtilsProvider {
|
||||
override fun addContentPic(
|
||||
context: Context,
|
||||
linearLayout: LinearLayout,
|
||||
list: List<String>,
|
||||
entrance: String,
|
||||
width: Int
|
||||
) {
|
||||
ConcernContentUtils.addContentPic(context, linearLayout, list, entrance, width)
|
||||
}
|
||||
|
||||
override fun createArticleBinding(parent: ViewGroup): ViewBinding {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return RecyclerGameArticleBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
|
||||
override fun initArticleStyle(binding: ViewBinding) {
|
||||
if (binding is RecyclerGameArticleBinding) {
|
||||
val context = binding.root.context
|
||||
binding.root
|
||||
.setBackground(ContextCompat.getDrawable(context, R.drawable.reuse_listview_item_style))
|
||||
binding.tvGameName.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
|
||||
binding.tvTime.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
|
||||
binding.tvTitle.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
|
||||
binding.tvDescription.setTextColor(ContextCompat.getColor(context, R.color.text_secondary))
|
||||
binding.tvComment.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
|
||||
binding.tvShare.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIvIcon(binding: ViewBinding): View =
|
||||
(binding as RecyclerGameArticleBinding).ivIcon
|
||||
|
||||
override fun getTvGameName(binding: ViewBinding): TextView =
|
||||
(binding as RecyclerGameArticleBinding).tvGameName
|
||||
|
||||
override fun getTvTime(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvTime
|
||||
|
||||
override fun getTvTitle(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvTitle
|
||||
|
||||
override fun getTvDescription(binding: ViewBinding): TextView =
|
||||
(binding as RecyclerGameArticleBinding).tvDescription
|
||||
|
||||
override fun bindImgs(binding: ViewBinding, img: List<String>) {
|
||||
if (binding is RecyclerGameArticleBinding) {
|
||||
val images = img.map {
|
||||
ImageContainerView.ImageContainerData.ImageInfo(it, 3, 2)
|
||||
}
|
||||
val imageContainerData =
|
||||
ImageContainerView.ImageContainerData("", false, images, show = images.isNotEmpty())
|
||||
binding.ivImagesContainer.bindData(imageContainerData,
|
||||
object : ImageContainerView.OnImageContainerEventListener {
|
||||
override fun onImageClick(
|
||||
images: List<String>,
|
||||
position: Int,
|
||||
imageViewList: ArrayList<SimpleDraweeView>
|
||||
) {
|
||||
val checkIntent = getIntent(
|
||||
binding.root.context,
|
||||
images.toArrayList(),
|
||||
position,
|
||||
imageViewList,
|
||||
""
|
||||
)
|
||||
binding.root.context.startActivity(checkIntent)
|
||||
}
|
||||
|
||||
override fun onVideoCLick(videoId: String) = Unit
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTvComment(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvComment
|
||||
|
||||
override fun getTvShare(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvShare
|
||||
|
||||
override fun init(context: Context?) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package com.gh.common.provider
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.LinearLayout
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.gh.common.util.ConcernContentUtils
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.core.provider.IConcernContentUtilsProvider
|
||||
|
||||
@Route(path = RouteConsts.provider.concernContentUtils, name = "ConcernContentUtils暴露服务")
|
||||
class ConcernContentUtilsProviderImpl : IConcernContentUtilsProvider {
|
||||
override fun addContentPic(
|
||||
context: Context,
|
||||
linearLayout: LinearLayout,
|
||||
list: List<String>,
|
||||
entrance: String,
|
||||
width: Int
|
||||
) {
|
||||
ConcernContentUtils.addContentPic(context, linearLayout, list, entrance, width)
|
||||
}
|
||||
|
||||
|
||||
override fun init(context: Context?) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.gh.common.provider
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.core.provider.IConcernGiftPackUtilsProvider
|
||||
import com.gh.gamecenter.databinding.RecyclerGiftPackBinding
|
||||
|
||||
@Route(path = RouteConsts.provider.concernGiftPackUtils, name = "ConcernGiftPackUtils暴露服务")
|
||||
class ConcernGiftPackUtilsProviderImpl : IConcernGiftPackUtilsProvider {
|
||||
override fun createBinding(parent: ViewGroup): ViewBinding {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return RecyclerGiftPackBinding.inflate(inflater, parent, false)
|
||||
.also {
|
||||
it.gCode.goneIf(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initStyle(viewBinding: ViewBinding) {
|
||||
with(viewBinding as RecyclerGiftPackBinding) {
|
||||
val context = root.context
|
||||
tvGameName.setTextColor(R.color.text_primary.toColor(context))
|
||||
tvTime.setTextColor(R.color.text_tertiary.toColor(context))
|
||||
tvGiftPackName.setTextColor(R.color.text_primary.toColor(context))
|
||||
tvGiftPackContent.setTextColor(R.color.text_secondary.toColor(context))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIvGameIcon(viewBinding: ViewBinding) =
|
||||
(viewBinding as RecyclerGiftPackBinding).ivIcon
|
||||
|
||||
override fun getTvGameName(viewBinding: ViewBinding) =
|
||||
(viewBinding as RecyclerGiftPackBinding).tvGameName
|
||||
|
||||
override fun getTvTime(viewBinding: ViewBinding) =
|
||||
(viewBinding as RecyclerGiftPackBinding).tvTime
|
||||
|
||||
override fun getTvGiftPackName(viewBinding: ViewBinding) =
|
||||
(viewBinding as RecyclerGiftPackBinding).tvGiftPackName
|
||||
|
||||
override fun getTvGiftPackContent(viewBinding: ViewBinding) =
|
||||
(viewBinding as RecyclerGiftPackBinding).tvGiftPackContent
|
||||
|
||||
override fun init(context: Context?) {
|
||||
// Do Nothing
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.gh.common.provider
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.core.provider.IConcernShareNewsProvider
|
||||
import com.gh.gamecenter.newsdetail.NewsShareDialog
|
||||
|
||||
@Route(path = RouteConsts.provider.concernShareNews, name = "ConcernShareNews暴露服务")
|
||||
class ConcernShareNewsProviderImpl : IConcernShareNewsProvider {
|
||||
override fun share(activity: AppCompatActivity, shortId: String?, id: String?, gameIcon: String?, title: String?) {
|
||||
NewsShareDialog.show(activity, shortId, id, gameIcon, title)
|
||||
}
|
||||
|
||||
override fun init(context: Context?) {
|
||||
// Do Nothing
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,8 @@ import android.widget.LinearLayout;
|
||||
import com.facebook.drawee.drawable.ScalingUtils;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.gh.gamecenter.ImageViewerActivity;
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils;
|
||||
import com.gh.gamecenter.common.utils.ImageUtils;
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -121,5 +121,4 @@ public class ConcernContentUtils {
|
||||
}
|
||||
return imageView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@ import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
|
||||
|
||||
object SyncDataBetweenPageHelper {
|
||||
|
||||
private const val REQUEST_CODE_TAG = "REQUEST_CODE_TAG"
|
||||
private const val DATA_POSITION_TAG = "DATA_POSITION_TAG"
|
||||
const val REQUEST_CODE_TAG = "REQUEST_CODE_TAG"
|
||||
const val DATA_POSITION_TAG = "DATA_POSITION_TAG"
|
||||
private const val DEFAULT_NUMBER = -1111
|
||||
|
||||
fun startActivityForResult(context: Context, intent: Intent, requestCode: Int, dataPosition: Int) {
|
||||
|
||||
@ -6,25 +6,21 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.get
|
||||
import com.facebook.drawee.drawable.ScalingUtils
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.debounceActionWithInterval
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.toResString
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.core.utils.TopCutProcess
|
||||
import com.gh.gamecenter.databinding.ItemCommunityImageBinding
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.CommunityVideoEntity
|
||||
import com.gh.gamecenter.feature.entity.ImageInfo
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
|
||||
class ImageContainerView : LinearLayout {
|
||||
private var mAnswerEntity: AnswerEntity? = null
|
||||
|
||||
private var data: ImageContainerData? = null
|
||||
|
||||
//三图默认宽度
|
||||
private var mDefaultWidth = 0f
|
||||
@ -41,9 +37,6 @@ class ImageContainerView : LinearLayout {
|
||||
//长图比例
|
||||
private var mLongPictureRatio = 9 / 18f
|
||||
|
||||
private var mEntrance = ""
|
||||
private var mPath = ""
|
||||
|
||||
//图片之间的间距
|
||||
private val mItemSpace = 4f.dip2px()
|
||||
private var mOffset = 0
|
||||
@ -51,6 +44,8 @@ class ImageContainerView : LinearLayout {
|
||||
|
||||
private val imageViewList = arrayListOf<SimpleDraweeView>()
|
||||
|
||||
private var onImageContainerEventListener: OnImageContainerEventListener? = null
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
@ -75,40 +70,34 @@ class ImageContainerView : LinearLayout {
|
||||
calculateWidth()
|
||||
}
|
||||
|
||||
fun bindData(entity: AnswerEntity, entrance: String = "", path: String = "", imageClick: (() -> Unit)? = null) {
|
||||
fun bindData(
|
||||
data: ImageContainerData,
|
||||
listener: OnImageContainerEventListener? = null
|
||||
) {
|
||||
this.data = data
|
||||
onImageContainerEventListener = listener
|
||||
imageViewList.clear()
|
||||
if (entity.id != mAnswerEntity?.id) {
|
||||
removeAllViews()
|
||||
}
|
||||
mAnswerEntity = entity
|
||||
mEntrance = entrance
|
||||
mPath = path
|
||||
removeAllViews()
|
||||
index = 0
|
||||
if ((entity.user.id == UserManager.getInstance().userId && entity.videos.isNotEmpty()) ||
|
||||
(entity.user.id != UserManager.getInstance().userId && entity.getPassVideos().isNotEmpty()) ||
|
||||
entity.images.isNullOrEmpty()
|
||||
) {
|
||||
if (!data.show) {
|
||||
visibility = View.GONE
|
||||
return
|
||||
}
|
||||
visibility = View.VISIBLE
|
||||
if (mAnswerEntity?.type == "community_article") {
|
||||
if (data.isPostCard) {
|
||||
//若文章内容含有图片及视频,则信息流卡片,仅展示图片,且标题后带有‘有视频’标签
|
||||
//若文章内容仅含有图片,则信息流卡片,仅展示图片,无标签
|
||||
//若文章内容仅含有视频,则信息流卡片,仅展示视频,无标签
|
||||
when {
|
||||
entity.images.isNotEmpty() -> {
|
||||
val imagesInfo = entity.imagesInfo
|
||||
val images = entity.images.take(3)
|
||||
images.forEachIndexed { index, url ->
|
||||
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
|
||||
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
|
||||
bindImage(url, width, height, images.size == 1, imageClick)
|
||||
}
|
||||
data.images.isNotEmpty() -> {
|
||||
data.images.take(3)
|
||||
.forEach {
|
||||
bindImage(it.url, it.width, it.height, data.images.size == 1)
|
||||
}
|
||||
}
|
||||
|
||||
entity.getPassVideos().isNotEmpty() -> {
|
||||
val video = entity.getPassVideos()[0]
|
||||
data.video != null -> {
|
||||
val video = data.video
|
||||
bindVideo(video, video.width, video.height, true)
|
||||
}
|
||||
|
||||
@ -120,28 +109,22 @@ class ImageContainerView : LinearLayout {
|
||||
//若问答内容含有图片及视频,则信息流卡片,同时展示图片及视频,且参考以往排序逻辑(视频优先放置第一位),无标签
|
||||
//若问答内容仅含有图片,则信息流卡片,仅展示图片,无标签
|
||||
//若问答内容仅含有视频,则信息流卡片,仅展示视频,无标签
|
||||
if (entity.getPassVideos().isNotEmpty()) {
|
||||
val video = entity.getPassVideos()[0]
|
||||
bindVideo(video, video.width, video.height, mAnswerEntity?.images.isNullOrEmpty())
|
||||
entity.images.take(2).forEachIndexed { index, url ->
|
||||
val imagesInfo = entity.imagesInfo
|
||||
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
|
||||
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
|
||||
bindImage(url, width, height, false, imageClick)
|
||||
if (data.video != null) {
|
||||
val video = data.video
|
||||
bindVideo(video, video.width, video.height, data.images.isNullOrEmpty())
|
||||
data.images.take(2).forEach {
|
||||
bindImage(it.url, it.width, it.height, false)
|
||||
}
|
||||
} else {
|
||||
val images = entity.images.take(3)
|
||||
images.forEachIndexed { index, url ->
|
||||
val imagesInfo = entity.imagesInfo
|
||||
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
|
||||
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
|
||||
bindImage(url, width, height, images.size == 1, imageClick)
|
||||
}
|
||||
data.images.take(3)
|
||||
.forEach {
|
||||
bindImage(it.url, it.width, it.height, data.images.size == 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindVideo(video: CommunityVideoEntity, width: Int, height: Int, isChangeRatio: Boolean) {
|
||||
private fun bindVideo(video: ImageContainerData.VideoInfo, width: Int, height: Int, isChangeRatio: Boolean) {
|
||||
val oldView = if (childCount == 0 || index >= childCount) null else getChildAt(index)
|
||||
val binding = if (oldView != null) {
|
||||
ItemCommunityImageBinding.bind(oldView)
|
||||
@ -158,16 +141,7 @@ class ImageContainerView : LinearLayout {
|
||||
displayImage(binding, video.poster, width.toFloat(), height.toFloat(), isChangeRatio, true)
|
||||
binding.root.setOnClickListener {
|
||||
debounceActionWithInterval(it.id, 1000) {
|
||||
if (mAnswerEntity == null) return@debounceActionWithInterval
|
||||
val videoEntity = mAnswerEntity!!.getPassVideos().firstOrNull()
|
||||
DirectUtils.directToVideoDetail(
|
||||
context,
|
||||
videoEntity?.id ?: "",
|
||||
VideoDetailContainerViewModel.Location.VIDEO_HOT.value,
|
||||
showComment = false,
|
||||
entrance = mEntrance,
|
||||
path = mPath
|
||||
)
|
||||
onImageContainerEventListener?.onVideoCLick(video.id)
|
||||
}
|
||||
}
|
||||
index++
|
||||
@ -177,8 +151,7 @@ class ImageContainerView : LinearLayout {
|
||||
url: String,
|
||||
width: Int,
|
||||
height: Int,
|
||||
isChangeRatio: Boolean,
|
||||
imageClick: (() -> Unit)?
|
||||
isChangeRatio: Boolean
|
||||
) {
|
||||
val oldView = if (childCount == 0 || index >= childCount) null else getChildAt(index)
|
||||
val binding = if (oldView != null) {
|
||||
@ -195,25 +168,21 @@ class ImageContainerView : LinearLayout {
|
||||
binding.videoPlay.visibility = View.GONE
|
||||
displayImage(binding, url, width.toFloat(), height.toFloat(), isChangeRatio)
|
||||
binding.root.setOnClickListener {
|
||||
if (mAnswerEntity?.status == "pending" || mAnswerEntity?.status == "fail") return@setOnClickListener
|
||||
imageClick?.invoke()
|
||||
if (data?.status == "pending" || data?.status == "fail") return@setOnClickListener
|
||||
debounceActionWithInterval(it.id, 1000) {
|
||||
if (mAnswerEntity == null) return@debounceActionWithInterval
|
||||
val position = if (mAnswerEntity?.type == "community_article") {
|
||||
binding.root.tag as Int
|
||||
} else {
|
||||
if (mAnswerEntity!!.getPassVideos()
|
||||
.isNullOrEmpty()
|
||||
) binding.root.tag as Int else (binding.root.tag as Int) - 1
|
||||
data?.run {
|
||||
val position = if (isPostCard) {
|
||||
binding.root.tag as Int
|
||||
} else {
|
||||
if (video == null) binding.root.tag as Int else (binding.root.tag as Int) - 1
|
||||
}
|
||||
onImageContainerEventListener?.onImageClick(
|
||||
images.map(ImageContainerData.ImageInfo::url),
|
||||
position,
|
||||
imageViewList
|
||||
)
|
||||
}
|
||||
if (mAnswerEntity?.communityId.isNullOrEmpty()) {
|
||||
mAnswerEntity?.communityId = mAnswerEntity?.bbs?.id
|
||||
}
|
||||
val intent = ImageViewerActivity.getIntent(
|
||||
context, mAnswerEntity!!.images as ArrayList<String>, position, imageViewList,
|
||||
if (mAnswerEntity?.type == "community_article") mAnswerEntity else null, mEntrance, true
|
||||
)
|
||||
context.startActivity(intent)
|
||||
|
||||
}
|
||||
}
|
||||
index++
|
||||
@ -279,7 +248,7 @@ class ImageContainerView : LinearLayout {
|
||||
}
|
||||
|
||||
binding.pendingView.run {
|
||||
when (mAnswerEntity?.status) {
|
||||
when (data?.status) {
|
||||
"pending" -> {
|
||||
visibility = View.VISIBLE
|
||||
text = R.string.pending_status.toResString()
|
||||
@ -297,7 +266,7 @@ class ImageContainerView : LinearLayout {
|
||||
}
|
||||
|
||||
|
||||
val imageCount = mAnswerEntity?.images?.size ?: 0
|
||||
val imageCount = data?.images?.size ?: 0
|
||||
if (!isVideo && index == 2 && imageCount > 3) {
|
||||
binding.labelIcon.visibility = View.GONE
|
||||
binding.durationOrNumTv.visibility = View.VISIBLE
|
||||
@ -308,4 +277,80 @@ class ImageContainerView : LinearLayout {
|
||||
if (index != 0) params.leftMargin = mItemSpace
|
||||
binding.root.layoutParams = params
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val COMMUNITY_ARTICLE = "community_article"
|
||||
|
||||
fun AnswerEntity.toImageContainerData(): ImageContainerData {
|
||||
val imageInfoList = arrayListOf<ImageContainerData.ImageInfo>()
|
||||
images.forEachIndexed { index, url ->
|
||||
if (index < 3) {
|
||||
imageInfoList.add(ImageContainerData.ImageInfo(url))
|
||||
}
|
||||
}
|
||||
imageInfoList.forEachIndexed { index, imageInfo ->
|
||||
val item = imagesInfo.getOrNull(index)
|
||||
if (item != null) {
|
||||
imageInfo.width = item.width
|
||||
imageInfo.height = item.height
|
||||
}
|
||||
}
|
||||
val video =
|
||||
getPassVideos().firstOrNull()?.let {
|
||||
ImageContainerData.VideoInfo(
|
||||
it.id,
|
||||
it.duration,
|
||||
it.poster,
|
||||
it.width,
|
||||
it.height
|
||||
)
|
||||
}
|
||||
val show = !((user.id == UserManager.getInstance().userId && videos.isNotEmpty())
|
||||
|| (user.id != UserManager.getInstance().userId && getPassVideos().isNotEmpty())
|
||||
|| images.isNullOrEmpty())
|
||||
return ImageContainerData(
|
||||
status = status,
|
||||
isPostCard = type == COMMUNITY_ARTICLE,
|
||||
images = imageInfoList,
|
||||
video = video,
|
||||
show
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class ImageContainerData(
|
||||
val status: String,
|
||||
val isPostCard: Boolean, // 是否是帖子卡片
|
||||
val images: List<ImageInfo>,
|
||||
val video: VideoInfo? = null,
|
||||
val show: Boolean
|
||||
) {
|
||||
|
||||
data class ImageInfo(
|
||||
val url: String,
|
||||
var width: Int = 0,
|
||||
var height: Int = 0
|
||||
)
|
||||
|
||||
data class VideoInfo(
|
||||
val id: String,
|
||||
val duration: String,
|
||||
val poster: String,
|
||||
var width: Int = 0,
|
||||
var height: Int = 0,
|
||||
)
|
||||
}
|
||||
|
||||
interface OnImageContainerEventListener {
|
||||
|
||||
fun onImageClick(
|
||||
images: List<String>,
|
||||
position: Int,
|
||||
imageViewList: ArrayList<SimpleDraweeView>
|
||||
)
|
||||
|
||||
fun onVideoCLick(videoId: String)
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.utils.formatTime
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.databinding.ItemArchiveLimitBinding
|
||||
import com.gh.gamecenter.entity.ArchiveEntity
|
||||
@ -29,10 +30,7 @@ class ArchiveLimitAdapter(context: Context) : ListAdapter<ArchiveLimitAdapter.Ar
|
||||
if (holder is ArchiveLimitViewHolder) {
|
||||
val item = mEntityList[position]
|
||||
holder.binding.tvTitle.text = item.data.name
|
||||
val timeLong = item.data.time.create
|
||||
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA)
|
||||
val date = Date(timeLong)
|
||||
holder.binding.tvTime.text = sdf.format(date)
|
||||
holder.binding.tvTime.text = item.data.time.create.formatTime("yyyy-MM-dd HH:mm")
|
||||
|
||||
val resId = if (item.isChecked) R.drawable.ic_selector_selected else R.drawable.ic_selector_default
|
||||
holder.binding.ivSelector.setImageResource(resId)
|
||||
@ -53,10 +51,10 @@ class ArchiveLimitAdapter(context: Context) : ListAdapter<ArchiveLimitAdapter.Ar
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any?>) {
|
||||
if(holder is ArchiveLimitViewHolder){
|
||||
if(payloads.isEmpty()){
|
||||
if (holder is ArchiveLimitViewHolder) {
|
||||
if (payloads.isEmpty()) {
|
||||
onBindViewHolder(holder, position)
|
||||
}else{
|
||||
} else {
|
||||
val item = mEntityList[position]
|
||||
val resId = if (item.isChecked) R.drawable.ic_selector_selected else R.drawable.ic_selector_default
|
||||
holder.binding.ivSelector.setImageResource(resId)
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FollowCommonContentCollection(
|
||||
@SerializedName("title")
|
||||
private val _title: String? = null,
|
||||
@SerializedName("link")
|
||||
private val _link: LinkEntity? = null,
|
||||
@SerializedName("link_common_collection")
|
||||
private val _linkCommonCollection: CustomPageData.CommonContentCollection? = null
|
||||
) {
|
||||
val title: String
|
||||
get() = _title ?: ""
|
||||
|
||||
val link: LinkEntity
|
||||
get() = _link ?: LinkEntity()
|
||||
|
||||
val linkCommonCollection: CustomPageData.CommonContentCollection
|
||||
get() = _linkCommonCollection ?: CustomPageData.CommonContentCollection()
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.gh.gamecenter.feature.entity.*
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class FollowDynamicEntity(
|
||||
@SerializedName("type")
|
||||
private val _type: String? = null,
|
||||
@SerializedName("libao")
|
||||
val libao: LibaoEntity? = null,
|
||||
@SerializedName("libao_exchange")
|
||||
val libaoExchange: LibaoEntity? = null,
|
||||
@SerializedName("game")
|
||||
private val _game: GameEntity? = null,
|
||||
@SerializedName("me")
|
||||
private val _me: MeEntity? = null,
|
||||
@SerializedName("time")
|
||||
private val _time: Long? = null,
|
||||
@SerializedName("article")
|
||||
val article: Article? = null,
|
||||
@SerializedName("user_post")
|
||||
val userPost: ArticleEntity? = null
|
||||
) : Parcelable {
|
||||
|
||||
val type: String
|
||||
get() = _type ?: ""
|
||||
|
||||
val game: GameEntity
|
||||
get() = _game ?: GameEntity()
|
||||
|
||||
val me: MeEntity
|
||||
get() = _me ?: MeEntity()
|
||||
|
||||
val time: Long
|
||||
get() = _time ?: 0L
|
||||
|
||||
companion object {
|
||||
|
||||
const val FOLLOW_UPDATE_TYPE_LIBAO = "libao"
|
||||
const val FOLLOW_UPDATE_TYPE_LIBAO_EXCHANGE = "libao_exchange"
|
||||
const val FOLLOW_UPDATE_TYPE_ARTICLE = "article"
|
||||
const val FOLLOW_UPDATE_TYPE_USER_POST = "user_post"
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Article(
|
||||
@SerializedName("_id")
|
||||
private val _id: String? = null,
|
||||
@SerializedName("title")
|
||||
private val _title: String? = null,
|
||||
@SerializedName("content")
|
||||
private val _content: String? = null,
|
||||
@SerializedName("img")
|
||||
private val _img: List<String>? = null,
|
||||
@SerializedName("count")
|
||||
private val _count: Count? = null,
|
||||
@SerializedName("game")
|
||||
private val _game: GameEntity? = null,
|
||||
@SerializedName("time")
|
||||
private val _time: Long? = null,
|
||||
@SerializedName("_seq")
|
||||
private val _shortId: String? = null
|
||||
) : Parcelable {
|
||||
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
val title: String
|
||||
get() = _title ?: ""
|
||||
|
||||
val content: String
|
||||
get() = _content ?: ""
|
||||
|
||||
val img: List<String>
|
||||
get() = _img ?: listOf()
|
||||
|
||||
val count: Count
|
||||
get() = _count ?: Count()
|
||||
|
||||
val game: GameEntity
|
||||
get() = _game ?: GameEntity()
|
||||
|
||||
val time: Long
|
||||
get() = _time ?: 0L
|
||||
|
||||
val shortId: String
|
||||
get() = _shortId ?: ""
|
||||
|
||||
@Parcelize
|
||||
data class Count(
|
||||
@SerializedName("comment")
|
||||
private val _comment: Int? = null
|
||||
) : Parcelable {
|
||||
|
||||
val comment: Int
|
||||
get() = _comment ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class LibaoExchange(
|
||||
@SerializedName("_id")
|
||||
private val _id: String? = null,
|
||||
@SerializedName("libao_id")
|
||||
private val _libaoId: String? = null,
|
||||
@SerializedName("libao_code")
|
||||
private val _libaoCode: String? = null,
|
||||
@SerializedName("name")
|
||||
private val _name: String? = null,
|
||||
@SerializedName("content")
|
||||
private val _content: String? = null,
|
||||
@SerializedName("active")
|
||||
private val _active: Boolean? = null
|
||||
) : Parcelable {
|
||||
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
val libaoId: String
|
||||
get() = _libaoId ?: ""
|
||||
|
||||
val libaoCode: String
|
||||
get() = _libaoCode ?: ""
|
||||
|
||||
val name: String
|
||||
get() = _name ?: ""
|
||||
|
||||
val content: String
|
||||
get() = _content ?: ""
|
||||
|
||||
val active: Boolean
|
||||
get() = _active ?: false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
data class FollowOperateTopRequest(
|
||||
val _id: String,
|
||||
val order: Int
|
||||
) {
|
||||
}
|
||||
159
app/src/main/java/com/gh/gamecenter/entity/FollowUserEntity.kt
Normal file
159
app/src/main/java/com/gh/gamecenter/entity/FollowUserEntity.kt
Normal file
@ -0,0 +1,159 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.User
|
||||
import com.gh.gamecenter.feature.entity.UserEntity
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.Objects
|
||||
|
||||
@Parcelize
|
||||
data class FollowUserEntity(
|
||||
@SerializedName("_id")
|
||||
private val _id: String? = null,
|
||||
@SerializedName("type")
|
||||
private val _type: String? = null, // user:用户、bbs:论坛
|
||||
@SerializedName("user")
|
||||
private val _user: UserEntity? = null,
|
||||
@SerializedName("bbs")
|
||||
private var _bbs: Bbs? = null,
|
||||
@SerializedName("is_top")
|
||||
private val _isTop: Int? = null,
|
||||
@SerializedName("is_show_tip")
|
||||
private val _isShowTip: Int? = null,
|
||||
) : Parcelable {
|
||||
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
val type: String
|
||||
get() = _type ?: ""
|
||||
|
||||
val user: UserEntity
|
||||
get() = _user ?: UserEntity()
|
||||
|
||||
val isTop: Boolean
|
||||
get() = _isTop == 1
|
||||
|
||||
val isShowTip: Boolean
|
||||
get() = _isShowTip == 1
|
||||
|
||||
val bbs: Bbs
|
||||
get() = _bbs ?: Bbs()
|
||||
|
||||
val isUser: Boolean
|
||||
get() = type == TYPE_USER
|
||||
|
||||
val name: String
|
||||
get() = if (isUser) {
|
||||
user.name ?: ""
|
||||
} else {
|
||||
bbs.name
|
||||
}
|
||||
|
||||
val icon: String
|
||||
get() = when {
|
||||
type == TYPE_USER -> user.icon ?: ""
|
||||
bbs.type == BBS_TYPE_GAME -> bbs.game.icon ?: ""
|
||||
else -> bbs.icon
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is FollowUserEntity
|
||||
&& id == other.id
|
||||
&& type == other.type
|
||||
&& user.id == other.user.id
|
||||
&& user.name == other.user.name
|
||||
&& bbs.id == other.bbs.id
|
||||
&& bbs.name == other.bbs.name
|
||||
&& bbs.type == other.bbs.type
|
||||
&& bbs.icon == other.bbs.icon
|
||||
&& bbs.game.icon == other.bbs.game.icon
|
||||
&& isTop == other.isTop
|
||||
&& isShowTip == other.isShowTip
|
||||
&& icon == other.icon
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(
|
||||
_id,
|
||||
type,
|
||||
user.id,
|
||||
user.name,
|
||||
bbs.id,
|
||||
bbs.name,
|
||||
bbs.type,
|
||||
bbs.icon,
|
||||
bbs.game.icon,
|
||||
isTop,
|
||||
isShowTip,
|
||||
icon
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TYPE_USER = "user"
|
||||
private const val BBS_TYPE_GAME = "game_bbs"
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Bbs(
|
||||
@SerializedName("_id")
|
||||
private val _id: String? = null,
|
||||
@SerializedName("name")
|
||||
private val _name: String? = null,
|
||||
@SerializedName("type")
|
||||
private val _type: String? = null,
|
||||
@SerializedName("icon")
|
||||
private val _icon: String? = null,
|
||||
@SerializedName("game")
|
||||
private val _game: GameEntity? = null
|
||||
) : Parcelable {
|
||||
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
val name: String
|
||||
get() = _name ?: ""
|
||||
|
||||
val type: String
|
||||
get() = _type ?: ""
|
||||
|
||||
val icon: String
|
||||
get() = _icon ?: ""
|
||||
|
||||
val game: GameEntity
|
||||
get() = _game ?: GameEntity()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Game(
|
||||
@SerializedName("_id")
|
||||
private val _id: String? = null,
|
||||
@SerializedName("name")
|
||||
private val _name: String? = null,
|
||||
@SerializedName("icon")
|
||||
private val _icon: String? = null,
|
||||
@SerializedName("ori_icon")
|
||||
private val _oriIcon: String? = null,
|
||||
@SerializedName("active")
|
||||
private val _active: Boolean? = null
|
||||
) : Parcelable {
|
||||
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
val name: String
|
||||
get() = _name ?: ""
|
||||
|
||||
val icon: String
|
||||
get() = _icon ?: ""
|
||||
|
||||
val oriIcon: String
|
||||
get() = _oriIcon ?: ""
|
||||
|
||||
val active: Boolean
|
||||
get() = _active ?: false
|
||||
}
|
||||
}
|
||||
@ -163,6 +163,14 @@ data class PersonalHistoryEntity(
|
||||
get() = community.name
|
||||
set(_) {}
|
||||
|
||||
override fun vote(isVote: Boolean) {
|
||||
_count.vote = if (isVote) {
|
||||
_count.vote + 1
|
||||
} else {
|
||||
_count.vote - 1
|
||||
}
|
||||
}
|
||||
|
||||
fun getPassVideos(): List<CommunityVideoEntity> {
|
||||
val passVideos = arrayListOf<CommunityVideoEntity>()
|
||||
for (video in videos) {
|
||||
|
||||
@ -4,10 +4,7 @@ import android.os.Parcelable
|
||||
import com.alibaba.android.arouter.launcher.ARouter
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.core.provider.IConfigProvider
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.MeEntity
|
||||
import com.gh.gamecenter.feature.entity.SourceEntity
|
||||
import com.gh.gamecenter.feature.entity.UserEntity
|
||||
import com.gh.gamecenter.feature.entity.*
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@ -62,6 +59,16 @@ open class VideoEntity(
|
||||
@SerializedName("_source")
|
||||
val ipSource: SourceEntity? = null,
|
||||
|
||||
// 关注首页新增字段
|
||||
@SerializedName("choiceness")
|
||||
private val _choiceness: Boolean? = null,
|
||||
@SerializedName("video_info")
|
||||
private val _videoInfo: VideoInfo? = null,
|
||||
@SerializedName("show_me_only")
|
||||
private val _showMeOnly: Boolean? = null,
|
||||
@SerializedName("count")
|
||||
private val _count: Count? = null,
|
||||
|
||||
//本地数据
|
||||
@IgnoredOnParcel
|
||||
var videoIsMuted: Boolean = false,//是否静音标记
|
||||
@ -73,6 +80,18 @@ open class VideoEntity(
|
||||
val gameName: String
|
||||
get() = mGameName.removeSuffix(".")
|
||||
|
||||
val choiceness: Boolean
|
||||
get() = _choiceness ?: false
|
||||
|
||||
val videoInfo: VideoInfo
|
||||
get() = _videoInfo ?: VideoInfo()
|
||||
|
||||
val showMeOnly: Boolean
|
||||
get() = _showMeOnly ?: false
|
||||
|
||||
val count: Count
|
||||
get() = _count ?: Count()
|
||||
|
||||
fun getThumb(): String {
|
||||
val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
|
||||
return if (!configProvider?.getVideoSnapshotSuffix().isNullOrEmpty()) {
|
||||
|
||||
@ -42,6 +42,10 @@ class ForumArticleAskListAdapter(
|
||||
private var mVideoOrderList = listOf("推荐", "发布")
|
||||
private var mFilterPosition = if (path == "视频") 1 else 0
|
||||
|
||||
override fun setListData(updateData: MutableList<AnswerEntity>?) {
|
||||
super.setListData(updateData)
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
}
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
package com.gh.gamecenter.forum.home
|
||||
|
||||
import android.app.Activity
|
||||
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.ShareUtils
|
||||
import com.gh.gamecenter.common.utils.isPublishEnv
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
|
||||
|
||||
class AnswerArticleVideoViewEventHelper(
|
||||
private val entity: ForumVideoEntity,
|
||||
private val orientationUtils: OrientationUtils
|
||||
) : ArticleItemVideoView.OnArticleItemVideoViewEventListener {
|
||||
override fun onTrackVideoStartPlaying() {
|
||||
SensorsBridge.trackVideoStartPlaying(
|
||||
articleId = entity.id,
|
||||
bbsId = entity.bbs?.id ?: "",
|
||||
bbsType = entity.bbs?.typeChinese ?: "综合论坛",
|
||||
customerType = entity.user.auth?.text ?: "",
|
||||
videoId = entity.id,
|
||||
playType = "视频贴",
|
||||
gameForumType = entity.bbs?.game?.categoryChinese ?: "",
|
||||
activityTag = entity.tagActivityName,
|
||||
articleType = entity.typeChinese
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTrackVideoEndPlaying(maxPlayedProgress: Int) {
|
||||
SensorsBridge.trackVideoEndPlaying(
|
||||
articleId = entity.id,
|
||||
bbsId = entity.bbs?.id ?: "",
|
||||
bbsType = entity.bbs?.typeChinese ?: "综合论坛",
|
||||
customerType = entity.user.auth?.text ?: "",
|
||||
videoId = entity.id,
|
||||
playType = "视频贴",
|
||||
gameForumType = entity.bbs?.game?.categoryChinese ?: "",
|
||||
activityTag = entity.tagActivityName,
|
||||
articleType = entity.typeChinese,
|
||||
result = if (maxPlayedProgress >= 95) "是" else "否"
|
||||
)
|
||||
}
|
||||
|
||||
override fun onShare(videoView: ArticleItemVideoView) {
|
||||
val shareIcon = entity.poster
|
||||
val shareUrl = if (isPublishEnv()) {
|
||||
"https://m.ghzs666.com/video/${entity.id}"
|
||||
} else {
|
||||
"https://resource.ghzs.com/page/video_play/video/video.html?video=${entity.id}"
|
||||
}
|
||||
val additionalParams = AdditionalParamsEntity().apply {
|
||||
contentType = "视频帖"
|
||||
contentId = entity.id ?: ""
|
||||
bbsId = entity.bbs?.id ?: ""
|
||||
bbsType = entity.bbs?.typeChinese ?: "综合论坛"
|
||||
customerType = entity.user.auth?.text ?: ""
|
||||
activityTagName = entity.tagActivityName ?: ""
|
||||
gameForumType = entity.bbs?.game?.categoryChinese ?: ""
|
||||
refUserId = UserManager.getInstance().userId
|
||||
}
|
||||
val activity = (videoView.context as? Activity) ?: return
|
||||
ShareUtils.getInstance(videoView.context).showShareWindowsCallback(activity,
|
||||
videoView,
|
||||
shareUrl,
|
||||
shareIcon,
|
||||
entity.title,
|
||||
entity.des,
|
||||
ShareUtils.ShareEntrance.video,
|
||||
entity.id,
|
||||
additionalParams,
|
||||
object : ShareUtils.ShareCallBack {
|
||||
override fun onSuccess(label: String) = Unit
|
||||
|
||||
override fun onCancel() = Unit
|
||||
})
|
||||
}
|
||||
|
||||
override fun onMutedChanged(isMuted: Boolean) {
|
||||
entity.videoIsMuted = isMuted
|
||||
}
|
||||
|
||||
override fun onStartWindowFullscreen() {
|
||||
orientationUtils.resolveByClick()
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
package com.gh.gamecenter.forum.home
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
@ -12,11 +13,9 @@ import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.video.detail.CustomManager
|
||||
import com.lightgame.utils.Utils
|
||||
import com.shuyu.gsyvideoplayer.utils.CommonUtil
|
||||
@ -25,13 +24,12 @@ import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge
|
||||
import io.reactivex.disposables.Disposable
|
||||
import java.util.*
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
|
||||
class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
StandardGSYVideoPlayer(context, attrs) {
|
||||
|
||||
private var mVideoEntity: ForumVideoEntity? = null
|
||||
var data: ArticleVideoData? = null
|
||||
|
||||
private var mMuteDisposable: Disposable? = null
|
||||
private var mIsAutoPlay = false
|
||||
var uuid = UUID.randomUUID().toString()
|
||||
@ -53,6 +51,8 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
|
||||
private val mTrackHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
private var onEventListener: OnArticleItemVideoViewEventListener? = null
|
||||
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.layout_article_item_video
|
||||
}
|
||||
@ -81,7 +81,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
fun startPlayLogic(isAutoPlay: Boolean) {
|
||||
mIsAutoPlay = isAutoPlay
|
||||
if (mIsAutoPlay) {
|
||||
val seekTime = ForumScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(mVideoEntity?.url))
|
||||
val seekTime = ForumScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(data?.url))
|
||||
seekOnStart = seekTime
|
||||
}
|
||||
startPlayLogic()
|
||||
@ -137,7 +137,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
}
|
||||
|
||||
override fun onClickUiToggle(e: MotionEvent?) {
|
||||
if (mVideoEntity?.status != "pending" && mVideoEntity?.status != "fail") {
|
||||
if (data?.status != "pending" && data?.status != "fail") {
|
||||
if (mCurrentState == CURRENT_STATE_PLAYING) {
|
||||
if (mStartButton.visibility == View.VISIBLE) {
|
||||
changeUiToPlayingClear()
|
||||
@ -156,7 +156,6 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
super.onSurfaceUpdated(surface)
|
||||
if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) {
|
||||
mThumbImageViewLayout.visibility = View.INVISIBLE
|
||||
uploadVideoStreamingPlaying("开始播放")
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,14 +168,9 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
// }
|
||||
|
||||
override fun releaseVideos() {
|
||||
uploadVideoStreamingPlaying("结束播放")
|
||||
CustomManager.releaseAllVideos(getKey())
|
||||
}
|
||||
|
||||
override fun onVideoPause() {
|
||||
super.onVideoPause()
|
||||
uploadVideoStreamingPlaying("暂停播放")
|
||||
}
|
||||
|
||||
// 重载以减少横竖屏切换的时间
|
||||
override fun checkoutState() {
|
||||
@ -206,9 +200,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
private fun showBackBtn() {
|
||||
mTopContainer.background = ContextCompat.getDrawable(context, R.drawable.video_title_bg)
|
||||
back.visibility = View.VISIBLE
|
||||
mVideoEntity?.run {
|
||||
titleTv.text = title
|
||||
}
|
||||
data?.title?.let(titleTv::setText)
|
||||
}
|
||||
|
||||
private fun hideBackBtn() {
|
||||
@ -217,19 +209,32 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
titleTv.text = ""
|
||||
}
|
||||
|
||||
fun updateThumb(url: String) {
|
||||
ImageUtils.display(thumbImage, url)
|
||||
}
|
||||
|
||||
fun updateDurationTv(duration: String) {
|
||||
durationTv.text = duration
|
||||
}
|
||||
|
||||
fun updateVideoData(video: ForumVideoEntity) {
|
||||
fun updateVideoData(video: ArticleVideoData, listener: OnArticleItemVideoViewEventListener) {
|
||||
this.onEventListener = listener
|
||||
this.mStopTrackRunnable = null
|
||||
|
||||
mVideoEntity = video
|
||||
data = video
|
||||
titleTv.text = if (mIfCurrentIsFullscreen) video.title else ""
|
||||
|
||||
ImageUtils.display(thumbImage, video.poster)
|
||||
|
||||
if (!mIfCurrentIsFullscreen) {
|
||||
durationTv.text = video.duration
|
||||
setVideoStatus(video.status)
|
||||
fullscreenButton.setOnClickListener {
|
||||
val horizontalVideoView =
|
||||
startWindowFullscreen(context, true, true) as? ArticleItemVideoView
|
||||
if (horizontalVideoView == null) {
|
||||
toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤")
|
||||
return@setOnClickListener
|
||||
}
|
||||
onEventListener?.onStartWindowFullscreen()
|
||||
horizontalVideoView.uuid = uuid
|
||||
horizontalVideoView.updateVideoData(video, listener)
|
||||
horizontalVideoView.violenceUpdateMuteStatus()
|
||||
horizontalVideoView.setFullViewStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setViewShowState(view: View?, visibility: Int) {
|
||||
@ -266,25 +271,16 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
override fun setStateAndUi(state: Int) {
|
||||
if (state != currentState
|
||||
&& state == CURRENT_STATE_PLAYING
|
||||
&& currentState != CURRENT_STATE_PLAYING_BUFFERING_START) {// 上报开始视频播放埋点
|
||||
&& currentState != CURRENT_STATE_PLAYING_BUFFERING_START
|
||||
) {// 上报开始视频播放埋点
|
||||
// 视频停止播放后再恢复播放的间隔时间未超过3秒,则取消上报结束视频播放埋点
|
||||
mStopTrackRunnable?.let { runnable ->
|
||||
mTrackHandler.removeCallbacks(runnable)
|
||||
}
|
||||
|
||||
mVideoEntity?.let {
|
||||
data?.let {
|
||||
val startTrackRunnable = Runnable {
|
||||
SensorsBridge.trackVideoStartPlaying(
|
||||
articleId = it.id,
|
||||
bbsId = it.bbs?.id ?: "",
|
||||
bbsType = it.bbs?.typeChinese ?: "综合论坛",
|
||||
customerType = it.user.auth?.text ?: "",
|
||||
videoId = it.id,
|
||||
playType = "视频贴",
|
||||
gameForumType = it.bbs?.game?.categoryChinese ?: "",
|
||||
activityTag = it.tagActivityName,
|
||||
articleType = it.typeChinese
|
||||
)
|
||||
onEventListener?.onTrackVideoStartPlaying()
|
||||
mStartTrackRunnable = null
|
||||
}.also {
|
||||
mStartTrackRunnable = it
|
||||
@ -303,20 +299,9 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
mTrackHandler.removeCallbacks(it)
|
||||
mStartTrackRunnable = null
|
||||
} ?: let {
|
||||
mVideoEntity?.let {
|
||||
data?.let {
|
||||
val stopTrackRunnable = Runnable {
|
||||
SensorsBridge.trackVideoEndPlaying(
|
||||
articleId = it.id,
|
||||
bbsId = it.bbs?.id ?: "",
|
||||
bbsType = it.bbs?.typeChinese ?: "综合论坛",
|
||||
customerType = it.user.auth?.text ?: "",
|
||||
videoId = it.id,
|
||||
playType = "视频贴",
|
||||
gameForumType = it.bbs?.game?.categoryChinese ?: "",
|
||||
activityTag = it.tagActivityName,
|
||||
articleType = it.typeChinese,
|
||||
result = if (mMaxPlayedProgress >= 95) "是" else "否"
|
||||
)
|
||||
onEventListener?.onTrackVideoEndPlaying(mMaxPlayedProgress)
|
||||
}.also {
|
||||
this.mStopTrackRunnable = it
|
||||
}
|
||||
@ -342,10 +327,9 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
replayTv.setOnClickListener {
|
||||
startButton.performClick()
|
||||
violenceUpdateMuteStatus()
|
||||
uploadVideoStreamingPlaying("重新播放")
|
||||
}
|
||||
shareTv.setOnClickListener {
|
||||
share()
|
||||
onEventListener?.onShare(this)
|
||||
}
|
||||
} else {
|
||||
setViewShowState(completeContainer, View.GONE)
|
||||
@ -363,88 +347,33 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
}
|
||||
|
||||
private fun toggleMute() {
|
||||
if (mVideoEntity?.videoIsMuted == true) {
|
||||
unMute(true)
|
||||
if (data?.videoIsMuted == true) {
|
||||
unMute()
|
||||
} else {
|
||||
mute(true)
|
||||
mute()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMuteStatus() {
|
||||
if (mVideoEntity?.videoIsMuted == true) {
|
||||
private fun updateMuteStatus() {
|
||||
if (data?.videoIsMuted == true) {
|
||||
mute()
|
||||
} else {
|
||||
unMute()
|
||||
}
|
||||
}
|
||||
|
||||
fun mute(isManual: Boolean = false) {
|
||||
mVideoEntity?.videoIsMuted = true
|
||||
private fun mute() {
|
||||
onEventListener?.onMutedChanged(true)
|
||||
data?.videoIsMuted = true
|
||||
volume.setImageResource(R.drawable.ic_article_video_volume_off)
|
||||
CustomManager.getCustomManager(getKey()).isNeedMute = true
|
||||
if (isManual) {
|
||||
// Utils.toast(context, "当前处于静音状态")
|
||||
uploadVideoStreamingPlaying("点击静音")
|
||||
}
|
||||
}
|
||||
|
||||
fun unMute(isManual: Boolean = false) {
|
||||
mVideoEntity?.videoIsMuted = false
|
||||
private fun unMute() {
|
||||
onEventListener?.onMutedChanged(false)
|
||||
data?.videoIsMuted = false
|
||||
volume.setImageResource(R.drawable.ic_article_video_volume_on)
|
||||
CustomManager.getCustomManager(getKey()).isNeedMute = false
|
||||
if (isManual) {
|
||||
uploadVideoStreamingPlaying("取消静音")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutoCompletion() {
|
||||
super.onAutoCompletion()
|
||||
uploadVideoStreamingPlaying("播放完毕")
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {
|
||||
super.onStopTrackingTouch(seekBar)
|
||||
uploadVideoStreamingPlaying("拖动")
|
||||
}
|
||||
|
||||
private fun share() {
|
||||
mVideoEntity?.let {
|
||||
val shareIcon = it.poster
|
||||
val shareUrl = if (isPublishEnv()) {
|
||||
"https://m.ghzs666.com/video/${it.id}"
|
||||
} else {
|
||||
"https://dev-and-static.ghzs66.com/page/video_play/video/video.html?video=${it.id}"
|
||||
}
|
||||
val additionalParams = AdditionalParamsEntity().apply {
|
||||
contentType = "视频帖"
|
||||
contentId = mVideoEntity?.id ?: ""
|
||||
bbsId = mVideoEntity?.bbs?.id ?: ""
|
||||
bbsType = mVideoEntity?.bbs?.typeChinese ?: "综合论坛"
|
||||
customerType = mVideoEntity?.user?.auth?.text ?: ""
|
||||
activityTagName = mVideoEntity?.tagActivityName ?: ""
|
||||
gameForumType = mVideoEntity?.bbs?.game?.categoryChinese ?: ""
|
||||
refUserId = UserManager.getInstance().userId
|
||||
}
|
||||
ShareUtils.getInstance(context).showShareWindowsCallback(context as Activity,
|
||||
this,
|
||||
shareUrl,
|
||||
shareIcon,
|
||||
it.title,
|
||||
it.des,
|
||||
ShareUtils.ShareEntrance.video, it.id, additionalParams, object : ShareUtils.ShareCallBack {
|
||||
override fun onSuccess(label: String) {
|
||||
// if ("短信" == label || "复制链接" == label) viewModel?.shareVideoStatistics(it)
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
uploadVideoStreamingPlaying("取消分享")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun uploadVideoStreamingPlaying(action: String) {
|
||||
|
||||
}
|
||||
|
||||
fun setFullViewStatus() {
|
||||
@ -456,7 +385,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
durationTv.visibility = View.GONE
|
||||
}
|
||||
|
||||
fun setVideoStatus(status: String) {
|
||||
private fun setVideoStatus(status: String) {
|
||||
if (status == "pending" || status == "fail") {
|
||||
pendingView.text = if (status == "pending") "审核中...请耐心等待" else "审核不通过"
|
||||
pendingView.visibility = View.VISIBLE
|
||||
@ -494,4 +423,32 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
|
||||
super.onDetachedFromWindow()
|
||||
mMuteDisposable?.dispose()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun ForumVideoEntity.toArticleVideoData() =
|
||||
ArticleVideoData(url, title, status, poster, duration, videoIsMuted)
|
||||
}
|
||||
|
||||
data class ArticleVideoData(
|
||||
val url: String,
|
||||
val title: String,
|
||||
val status: String,
|
||||
val poster: String,
|
||||
val duration: String,
|
||||
var videoIsMuted: Boolean
|
||||
)
|
||||
|
||||
interface OnArticleItemVideoViewEventListener {
|
||||
|
||||
fun onTrackVideoStartPlaying()
|
||||
|
||||
fun onTrackVideoEndPlaying(maxPlayedProgress: Int)
|
||||
|
||||
fun onShare(videoView: ArticleItemVideoView)
|
||||
|
||||
fun onMutedChanged(isMuted: Boolean)
|
||||
|
||||
fun onStartWindowFullscreen()
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@ -11,11 +12,16 @@ import android.view.*
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.airbnb.lottie.LottieProperty
|
||||
import com.airbnb.lottie.SimpleColorFilter
|
||||
import com.airbnb.lottie.model.KeyPath
|
||||
import com.airbnb.lottie.value.LottieValueCallback
|
||||
import com.gh.common.browse.BrowseTimer
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.NewFlatLogUtils
|
||||
@ -30,6 +36,7 @@ import com.gh.gamecenter.common.constant.EntranceConsts.IS_DETAIL_PAGE
|
||||
import com.gh.gamecenter.common.retrofit.ApiResponse
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.AvatarBorderView
|
||||
import com.gh.gamecenter.core.iinterface.IScrollable
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
@ -41,8 +48,11 @@ import com.gh.gamecenter.eventbus.EBSkip
|
||||
import com.gh.gamecenter.eventbus.EBTypeChange
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.forum.home.follow.FollowHomeFilterPopWindow
|
||||
import com.gh.gamecenter.forum.home.follow.fragment.FollowHomeFragment
|
||||
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
|
||||
import com.gh.gamecenter.login.entity.UserInfoEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.login.user.UserRepository
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailWebCacheManager
|
||||
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
|
||||
@ -52,6 +62,7 @@ import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.gh.gamecenter.wrapper.MainWrapperViewModel
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
||||
import com.halo.assistant.HaloApp
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
@ -64,7 +75,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
private var mViewModel: CommunityHomeViewModel? = null
|
||||
private var mMainWrapperViewModel: MainWrapperViewModel? = null
|
||||
private var mFragmentList = arrayListOf<Fragment>()
|
||||
private var mTitleList = arrayListOf("推荐", "论坛", "活动")
|
||||
private var mTitleList = arrayListOf("关注", "推荐", "论坛", "活动")
|
||||
private var mTabList = arrayListOf<Any>()
|
||||
private var mDefaultSelectedTab = -1
|
||||
private var mNavigationBitmap: Bitmap? = null
|
||||
@ -76,6 +87,42 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
SensorsBridge.trackCommunityBrowsingDuration(it / 1000.0)
|
||||
}
|
||||
|
||||
private val followFilterPopWindow by lazy {
|
||||
FollowHomeFilterPopWindow.create(requireContext()).apply {
|
||||
setOnDismissListener {
|
||||
resetFollowTab()
|
||||
}
|
||||
setFilterListener(object : FollowHomeFilterPopWindow.OnFilteredChangedListener {
|
||||
override fun filterFollowed(position: Int) {
|
||||
mViewModel?.filterFollowed(position)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private val obTabSelectedListener = object : OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||
if (tab?.position == TAB_FOLLOW_INDEX) {
|
||||
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_down)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab?) {
|
||||
if (tab?.position == TAB_FOLLOW_INDEX) {
|
||||
mViewModel?.updateFilterResId(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||
if (mViewModel?.followFilterStatus?.value == R.drawable.ic_follow_arrow_down) {
|
||||
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_up)
|
||||
mBinding?.tabLayout?.let(followFilterPopWindow::showAsDropDown)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getRealLayoutId(): Int {
|
||||
return R.layout.fragment_community_home
|
||||
}
|
||||
@ -94,6 +141,19 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
super.onFragmentFirstVisible()
|
||||
|
||||
initViewPager()
|
||||
|
||||
mViewModel?.followFilterStatus?.observe(viewLifecycleOwner, Observer { resId ->
|
||||
val tabLayout = mBinding?.tabLayout ?: return@Observer
|
||||
val tab = tabLayout.getTabAt(TAB_FOLLOW_INDEX)
|
||||
val tvTitle = tab?.customView?.findViewById<TextView>(R.id.tab_title)
|
||||
if (resId != null) {
|
||||
tvTitle?.setDrawableEnd(resId, 8F.dip2px(), 8F.dip2px())
|
||||
} else {
|
||||
tvTitle?.setDrawableEnd(null, 8F.dip2px(), 8F.dip2px())
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun initRealView() {
|
||||
@ -144,7 +204,13 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
topBg.visibleIf(!mIsDarkModeOn)
|
||||
|
||||
videoLottie.setOnClickListener {
|
||||
DirectUtils.directToLegacyVideoDetail(requireContext(), "", VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value, referer = "视频流-社区右上角", isHomeVideo = true)
|
||||
DirectUtils.directToLegacyVideoDetail(
|
||||
requireContext(),
|
||||
"",
|
||||
VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value,
|
||||
referer = "视频流-社区右上角",
|
||||
isHomeVideo = true
|
||||
)
|
||||
}
|
||||
searchIconIv.setOnClickListener {
|
||||
NewLogUtils.logCommunitySearchClick()
|
||||
@ -234,7 +300,12 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
mTabList.clear()
|
||||
mFragmentList.clear()
|
||||
val tag = "android:switcher:${viewPager.id}:"
|
||||
val forumArticleListFragment = childFragmentManager.findFragmentByTag("${tag}0")
|
||||
|
||||
val followFragment = childFragmentManager.findFragmentByTag("$tag$TAB_FOLLOW_INDEX")
|
||||
?: FollowHomeFragment()
|
||||
mFragmentList.add(followFragment)
|
||||
|
||||
val forumArticleListFragment = childFragmentManager.findFragmentByTag("$tag$TAB_RECOMMEND_INDEX")
|
||||
?: ForumArticleListFragment().with(
|
||||
bundleOf(
|
||||
EntranceConsts.KEY_ENTRANCE to "社区",
|
||||
@ -243,11 +314,11 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
)
|
||||
mFragmentList.add(forumArticleListFragment)
|
||||
|
||||
val forumFragment = childFragmentManager.findFragmentByTag("${tag}1")
|
||||
val forumFragment = childFragmentManager.findFragmentByTag("${tag}$TAB_FORUM_INDEX")
|
||||
?: ForumFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "社区"))
|
||||
mFragmentList.add(forumFragment)
|
||||
|
||||
val activityFragment = childFragmentManager.findFragmentByTag("${tag}2")
|
||||
val activityFragment = childFragmentManager.findFragmentByTag("${tag}$TAB_ACTIVITY_INDEX")
|
||||
?: ForumActivityFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "活动"))
|
||||
mFragmentList.add(activityFragment)
|
||||
|
||||
@ -261,8 +332,16 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
adapter = FragmentAdapter(childFragmentManager, mFragmentList, mTitleList)
|
||||
doOnScroll(
|
||||
onPageSelected = { position ->
|
||||
communityEditBtn.goneIf(position != 0)
|
||||
communityEditBtn.goneIf(position != TAB_RECOMMEND_INDEX && position != TAB_FOLLOW_INDEX)
|
||||
when (position) {
|
||||
TAB_FOLLOW_INDEX -> {
|
||||
root.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
|
||||
topBg.translationY = 0F
|
||||
changeNavigationBg()
|
||||
NewLogUtils.logCommunityHomeEvent("click_for_you_tab")
|
||||
SensorsBridge.trackCommunityTopTabSelected("关注")
|
||||
}
|
||||
|
||||
TAB_RECOMMEND_INDEX -> {
|
||||
root.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
|
||||
topBg.translationY = 0F
|
||||
@ -273,7 +352,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
|
||||
TAB_FORUM_INDEX -> {
|
||||
root.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
(mFragmentList[1] as ForumFragment).translationY.run {
|
||||
(mFragmentList[2] as ForumFragment).translationY.run {
|
||||
topBg.translationY = -this.toFloat()
|
||||
changeNavigationBg(this)
|
||||
}
|
||||
@ -283,7 +362,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
|
||||
TAB_ACTIVITY_INDEX -> {
|
||||
root.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
|
||||
(mFragmentList[2] as ForumActivityFragment).translationY.run {
|
||||
(mFragmentList[3] as ForumActivityFragment).translationY.run {
|
||||
topBg.translationY = -this.toFloat()
|
||||
changeNavigationBg(this)
|
||||
}
|
||||
@ -295,8 +374,6 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
onPageScrolled = { position, positionOffset, _ ->
|
||||
if (position + 1 != mTabList.size) {
|
||||
(mTabList[position] as TextView).run {
|
||||
layoutParams.width =
|
||||
(DEFAULT_TAB_TEXT_WIDTH + ((1 - positionOffset) * 4F.dip2px())).roundToInt()
|
||||
textSize = (DEFAULT_TAB_TEXT_SIZE + ((1 - positionOffset) * 4)).roundTo(1)
|
||||
setTextColor(
|
||||
ColorUtils.blendARGB(
|
||||
@ -308,8 +385,6 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
if (mTabList[position + 1] is TextView) {
|
||||
(mTabList[position + 1] as TextView).run {
|
||||
layoutParams.width =
|
||||
(DEFAULT_TAB_TEXT_WIDTH + ((positionOffset) * 4F.dip2px())).roundToInt()
|
||||
textSize = (DEFAULT_TAB_TEXT_SIZE + ((positionOffset) * 4)).roundTo(1)
|
||||
setTextColor(
|
||||
ColorUtils.blendARGB(
|
||||
@ -321,13 +396,12 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
} else {
|
||||
(mTabList[position + 1] as TabItemCommunityBinding).run {
|
||||
tabTitle.layoutParams.width =
|
||||
(DEFAULT_TAB_TEXT_WIDTH + ((positionOffset) * 4F.dip2px())).roundToInt()
|
||||
tabTitle.textSize = (DEFAULT_TAB_TEXT_SIZE + ((positionOffset) * 4)).roundTo(1)
|
||||
tabImg.scaleX =
|
||||
(DEFAULT_TAB_IMG_WIDTH + ((positionOffset) * 8)).roundTo(1) / DEFAULT_TAB_IMG_WIDTH
|
||||
tabImg.scaleY =
|
||||
(DEFAULT_TAB_IMG_HEIGHT + ((positionOffset) * 4)).roundTo(1) / DEFAULT_TAB_IMG_HEIGHT
|
||||
val layoutParams = tabImg.layoutParams
|
||||
layoutParams.width =
|
||||
(DEFAULT_TAB_IMG_WIDTH + 8F.dip2px() * positionOffset).roundToInt()
|
||||
layoutParams.height =
|
||||
((DEFAULT_TAB_IMG_HEIGHT) + 4F.dip2px() * positionOffset).roundToInt()
|
||||
tabImg.layoutParams = layoutParams
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
tabImg.imageTintList =
|
||||
ColorStateList.valueOf(
|
||||
@ -338,6 +412,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,20 +431,26 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
tabLayout.addOnTabSelectedListener(obTabSelectedListener)
|
||||
tabLayout.setupWithViewPager(viewPager)
|
||||
indicatorView.run {
|
||||
setupWithTabLayout(tabLayout)
|
||||
setupWithViewPager(viewPager)
|
||||
setIndicatorWidth(18)
|
||||
}
|
||||
|
||||
val selectedPosition = if (UserManager.getInstance().isLoggedIn) {
|
||||
TAB_FOLLOW_INDEX
|
||||
} else {
|
||||
TAB_RECOMMEND_INDEX
|
||||
}
|
||||
for (i in 0 until tabLayout.tabCount) {
|
||||
val tab = tabLayout.getTabAt(i) ?: continue
|
||||
val tabTitle = if (tab.text != null) tab.text.toString() else ""
|
||||
val tabViewBinding = generateTabView(tabTitle, i)
|
||||
val tabViewBinding = generateTabView(tabTitle, i, selectedPosition)
|
||||
tab.customView = tabViewBinding.root
|
||||
tab.view.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
viewPager.setCurrentItem(selectedPosition, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,7 +473,6 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
if (index == selectedPosition) {
|
||||
if (positionOffset == 0F) {
|
||||
titleView.setTextColor(TAB_SELECTED_COLOR.toColor(requireContext()))
|
||||
titleView.setTypeface(null, Typeface.NORMAL)
|
||||
titleView.setTypeface(titleView.typeface, Typeface.BOLD)
|
||||
} else if (positionOffset > 0F) {
|
||||
titleView.setTypeface(null, Typeface.NORMAL)
|
||||
@ -426,9 +506,6 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
} else {
|
||||
tabImg.setImageResource(R.drawable.ic_tab_activity_active)
|
||||
}
|
||||
tabTitle.layoutParams.width = 64F.dip2px()
|
||||
tabImg.scaleX = SCALE_TAB_IMG_WIDTH / DEFAULT_TAB_IMG_WIDTH
|
||||
tabImg.scaleY = SCALE_TAB_IMG_HEIGHT / DEFAULT_TAB_IMG_HEIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -442,30 +519,22 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
tabImg.imageTintList = ColorStateList.valueOf(TAB_DEFAULT_COLOR.toColor(requireContext()))
|
||||
}
|
||||
tabTitle.layoutParams.width = DEFAULT_TAB_TEXT_WIDTH
|
||||
tabImg.scaleX = 1F
|
||||
tabImg.scaleY = 1F
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateTabView(title: String, index: Int): TabItemCommunityBinding {
|
||||
private fun generateTabView(title: String, index: Int, selectedPosition: Int): TabItemCommunityBinding {
|
||||
val binding = TabItemCommunityBinding.inflate(LayoutInflater.from(requireContext()))
|
||||
binding.run {
|
||||
if (index == TAB_ACTIVITY_INDEX) {
|
||||
mTabList.add(binding)
|
||||
tabTitle.visibility = View.INVISIBLE
|
||||
tabTitle.visibility = View.GONE
|
||||
tabImg.visibility = View.VISIBLE
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
tabImg.setImageResource(R.drawable.ic_tab_activity_svg)
|
||||
} else {
|
||||
tabImg.setImageResource(R.drawable.ic_tab_activity_default)
|
||||
}
|
||||
tabTitle.run {
|
||||
text = title
|
||||
// textSize = DEFAULT_TAB_TEXT_SIZE
|
||||
// setTextColor(TAB_DEFAULT_COLOR.toColor(requireContext()))
|
||||
}
|
||||
} else {
|
||||
mTabList.add(tabTitle)
|
||||
tabTitle.visibility = View.VISIBLE
|
||||
@ -474,13 +543,12 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
text = title
|
||||
textSize = DEFAULT_TAB_TEXT_SIZE
|
||||
setTextColor(TAB_DEFAULT_COLOR.toColor(requireContext()))
|
||||
if (index == TAB_FOLLOW_INDEX && selectedPosition == TAB_FOLLOW_INDEX) {
|
||||
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_down)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// binding.invisibleTabTitle.run {
|
||||
// text = title
|
||||
// textSize = DEFAULT_TAB_TEXT_SIZE
|
||||
// }
|
||||
return binding
|
||||
}
|
||||
|
||||
@ -568,6 +636,18 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetFollowTab() {
|
||||
mBinding?.tabLayout?.run {
|
||||
if (selectedTabPosition == TAB_FOLLOW_INDEX) {
|
||||
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_down)
|
||||
} else {
|
||||
mViewModel?.updateFilterResId(null)
|
||||
}
|
||||
}
|
||||
mBinding?.tabLayout?.removeOnTabSelectedListener(obTabSelectedListener)
|
||||
mBinding?.tabLayout?.addOnTabSelectedListener(obTabSelectedListener)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
@ -594,13 +674,13 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
|
||||
private fun insertDataToRecommendTab(entity: ArticleEntity) {
|
||||
(mFragmentList[0] as? ForumArticleListFragment)?.insertDataToFirstIndex(entity)
|
||||
(mFragmentList[TAB_RECOMMEND_INDEX] as? ForumArticleListFragment)?.insertDataToFirstIndex(entity)
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
mBinding?.viewPager?.run {
|
||||
if (currentItem == 0) {
|
||||
return (mFragmentList[0] as ForumArticleListFragment).onBackPressed()
|
||||
if (currentItem == 1) {
|
||||
return (mFragmentList[1] as ForumArticleListFragment).onBackPressed()
|
||||
}
|
||||
}
|
||||
return super.onBackPressed()
|
||||
@ -608,7 +688,10 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(status: EBTypeChange) {
|
||||
if (mBinding?.viewPager?.currentItem != 0) return
|
||||
val currentPosition = mBinding?.viewPager?.currentItem ?: 0
|
||||
if (currentPosition != TAB_FOLLOW_INDEX && currentPosition != TAB_RECOMMEND_INDEX) {
|
||||
return
|
||||
}
|
||||
|
||||
if (status.type == EB_SHOW_QUESTION_BUTTON) {
|
||||
setPutQuestionButtonStatus(View.VISIBLE)
|
||||
@ -634,8 +717,14 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
|
||||
private fun scrollToTop() {
|
||||
if (mFragmentList.isEmpty()) return
|
||||
(mFragmentList[0] as ForumArticleListFragment).scrollToTop()
|
||||
setPutQuestionButtonStatus(View.VISIBLE)
|
||||
val currentPosition = mBinding?.viewPager?.currentItem ?: 0
|
||||
val fragment = mFragmentList.getOrNull(currentPosition)
|
||||
if (fragment is IScrollable) {
|
||||
fragment.scrollToTop()
|
||||
}
|
||||
if (currentPosition == TAB_FOLLOW_INDEX || currentPosition == TAB_RECOMMEND_INDEX) {
|
||||
setPutQuestionButtonStatus(View.VISIBLE)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@ -669,7 +758,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
} else if (!mIsDarkModeOn && navigationBg.drawable != null) {
|
||||
navigationBg.setBackgroundColor(R.color.transparent.toColor(requireContext()))
|
||||
navigationBg.setImageDrawable(null)
|
||||
} else if (viewPager.currentItem == TAB_RECOMMEND_INDEX) {
|
||||
} else if (viewPager.currentItem == TAB_RECOMMEND_INDEX || viewPager.currentItem == TAB_FOLLOW_INDEX) {
|
||||
navigationBg.setBackgroundColor(
|
||||
if (mIsDarkModeOn && y > 0) R.color.ui_surface.toColor(requireContext()) else if (mIsDarkModeOn && y == 0) R.color.ui_background.toColor(
|
||||
requireContext()
|
||||
@ -690,7 +779,20 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
for (i in 0 until tabCount) {
|
||||
val tab: TabLayout.Tab? = getTabAt(i)
|
||||
if (tab != null) {
|
||||
updateTabStyle(mBinding?.viewPager?.currentItem ?: 0, 0f)
|
||||
val tvTitle = tab.customView?.findViewById<TextView>(R.id.tab_title)
|
||||
if (tvTitle != null && tvTitle.visibility == View.VISIBLE) {
|
||||
tvTitle.post {
|
||||
if (i == TAB_FOLLOW_INDEX && tab.isSelected) {
|
||||
val resId = mViewModel?.followFilterStatus?.value
|
||||
if (resId != null) {
|
||||
mViewModel?.updateFilterResId(resId)
|
||||
}
|
||||
}
|
||||
tvTitle.setTextColor(
|
||||
ColorStateList.valueOf(R.color.text_primary.toColor(requireContext()))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -715,7 +817,21 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
searchIconIv.setImageResource(R.drawable.ic_column_search)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
searchIconIv.imageTintList = ColorStateList.valueOf(R.color.text_primary.toColor(requireContext()))
|
||||
}
|
||||
|
||||
val csl = AppCompatResources.getColorStateList(requireContext(), R.color.text_primary)
|
||||
val filter = SimpleColorFilter(csl.defaultColor)
|
||||
val keyPath = KeyPath("**")
|
||||
val callback = LottieValueCallback<ColorFilter>(filter)
|
||||
videoLottie.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback)
|
||||
}
|
||||
|
||||
followFilterPopWindow.onDarkModeChanged()
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun getTopBgView() = mBinding?.topBg
|
||||
@ -723,16 +839,14 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
companion object {
|
||||
var TAB_SELECTED_COLOR: Int = R.color.text_primary
|
||||
var TAB_DEFAULT_COLOR: Int = R.color.community_forum_more
|
||||
var DEFAULT_TAB_TEXT_SIZE = 18F
|
||||
var DEFAULT_TAB_TEXT_WIDTH = 60F.dip2px()
|
||||
var DEFAULT_TAB_IMG_WIDTH = 34F
|
||||
var DEFAULT_TAB_IMG_HEIGHT = 18F
|
||||
var SCALE_TAB_IMG_WIDTH = 42F
|
||||
var SCALE_TAB_IMG_HEIGHT = 22F
|
||||
var DEFAULT_TAB_TEXT_SIZE = 16F
|
||||
var DEFAULT_TAB_IMG_WIDTH = 34F.dip2px()
|
||||
var DEFAULT_TAB_IMG_HEIGHT = 16F.dip2px()
|
||||
const val EB_TAB = "forum_tab"
|
||||
const val TAB_RECOMMEND_INDEX = 0
|
||||
const val TAB_FORUM_INDEX = 1
|
||||
const val TAB_ACTIVITY_INDEX = 2
|
||||
const val TAB_FOLLOW_INDEX = 0
|
||||
const val TAB_RECOMMEND_INDEX = 1
|
||||
const val TAB_FORUM_INDEX = 2
|
||||
const val TAB_ACTIVITY_INDEX = 3
|
||||
const val ARTICLE_REQUEST_CODE = 200
|
||||
const val QUESTION_REQUEST_CODE = 201
|
||||
const val VIDEO_REQUEST_CODE = 202
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
package com.gh.gamecenter.forum.home
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.common.entity.CommunityEntity
|
||||
import com.gh.gamecenter.common.utils.clearHtmlFormatCompletely
|
||||
import com.gh.gamecenter.common.utils.observableToMain
|
||||
import com.gh.gamecenter.common.utils.removeInsertedContent
|
||||
import com.gh.gamecenter.common.utils.removeVideoContent
|
||||
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
import com.gh.gamecenter.feature.entity.TimeEntity
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.feature.entity.TimeEntity
|
||||
import com.gh.gamecenter.feature.entity.UserEntity
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
|
||||
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
|
||||
class CommunityHomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
@ -131,4 +132,64 @@ class CommunityHomeViewModel(application: Application) : AndroidViewModel(applic
|
||||
|
||||
return articleEntity
|
||||
}
|
||||
|
||||
private val _hasFollowedUsers = MutableLiveData<Boolean>()
|
||||
val hasFollowedUser: LiveData<Boolean> = _hasFollowedUsers
|
||||
fun updateHasFollowUser(show: Boolean) {
|
||||
_hasFollowedUsers.value = show
|
||||
}
|
||||
|
||||
private val filterDrawableResId = MutableLiveData<Int?>()
|
||||
fun updateFilterResId(resId: Int?) {
|
||||
filterDrawableResId.value = resId
|
||||
}
|
||||
|
||||
val followFilterStatus = MediatorLiveData<Int?>().apply {
|
||||
addSource(hasFollowedUser) {
|
||||
if (it) {
|
||||
value = filterDrawableResId.value
|
||||
} else {
|
||||
value = null
|
||||
}
|
||||
}
|
||||
|
||||
addSource(filterDrawableResId) { resId ->
|
||||
// 当 hasFollowedUser.value == null 时,不需要调用setValue
|
||||
if (hasFollowedUser.value == true) {
|
||||
value = resId
|
||||
} else if (hasFollowedUser.value == false) {
|
||||
value = null
|
||||
}
|
||||
when {
|
||||
resId == null ||
|
||||
hasFollowedUser.value == false -> {
|
||||
// 当resId == null 时,表示已切换到别的tab 直接setValue
|
||||
// 当 resId != null && hasFollowedUser,value 为false时,说明关注页数据已加载完毕,并且没有关注任何用户,直接setValue
|
||||
value = null
|
||||
}
|
||||
|
||||
hasFollowedUser.value == true -> {
|
||||
// 当 resId != null 且 有关注用户,显示当前 resId
|
||||
value = resId
|
||||
}
|
||||
|
||||
else -> {
|
||||
// do Nothing 当resId != null ,但是hasFollowedUser.value == null,说明关注页数据正在loading,等待,无需setValue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val _filterFollowedAction = MutableLiveData<Event<Int>>()
|
||||
val filterFollowedAction: LiveData<Event<Int>> = _filterFollowedAction
|
||||
fun filterFollowed(position: Int) {
|
||||
val filterName = when (position) {
|
||||
0 -> "全部"
|
||||
1 -> "关注的人"
|
||||
else -> "关注的游戏"
|
||||
}
|
||||
SensorsBridge.trackFollowTabFilterOptionClick(filterName)
|
||||
_filterFollowedAction.value = Event(position)
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.SpacingItemDecoration
|
||||
import com.gh.gamecenter.core.iinterface.IScrollable
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.databinding.FragmentForumActivityBinding
|
||||
import com.gh.gamecenter.databinding.LayoutForumActivityCategoryItemBinding
|
||||
@ -20,7 +21,7 @@ import com.gh.gamecenter.entity.ForumActivityCategoryEntity
|
||||
import com.gh.gamecenter.entity.ForumActivityEntity
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
|
||||
class ForumActivityFragment : LazyListFragment<ForumActivityEntity, ForumActivityViewModel>() {
|
||||
class ForumActivityFragment : LazyListFragment<ForumActivityEntity, ForumActivityViewModel>(), IScrollable {
|
||||
|
||||
private var mBinding: FragmentForumActivityBinding? = null
|
||||
private var mAdapter: ForumActivityAdapter? = null
|
||||
@ -200,4 +201,12 @@ class ForumActivityFragment : LazyListFragment<ForumActivityEntity, ForumActivit
|
||||
}
|
||||
changeCategoryBg()
|
||||
}
|
||||
|
||||
override fun scrollToTop() {
|
||||
mBinding?.listRv?.let {
|
||||
if (it.visibility == View.VISIBLE) {
|
||||
it.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,13 +6,18 @@ import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.View
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.common.view.ImageContainerView
|
||||
import com.gh.common.view.ImageContainerView.Companion.toImageContainerData
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.callback.ConfirmListener
|
||||
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
|
||||
import com.gh.gamecenter.common.entity.CommunityEntity
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
@ -28,6 +33,7 @@ import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.CommunityItemData
|
||||
import com.gh.gamecenter.forum.home.ArticleItemVideoView.Companion.toArticleVideoData
|
||||
import com.gh.gamecenter.forum.search.CommunitySearchEventListener
|
||||
import com.gh.gamecenter.forum.search.CommunitySearchEventListener.Companion.SEARCH_BUTTON_COMMENT
|
||||
import com.gh.gamecenter.forum.search.CommunitySearchEventListener.Companion.SEARCH_BUTTON_DISLIKE
|
||||
@ -42,6 +48,7 @@ import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity
|
||||
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
|
||||
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
|
||||
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
|
||||
@ -148,15 +155,46 @@ class ForumArticleAskItemViewHolder(
|
||||
}
|
||||
}
|
||||
|
||||
binding.imageContainer.bindData(entity, entrance, path) {
|
||||
clickListener?.onItemChildClick(
|
||||
entity.id,
|
||||
entity.type,
|
||||
htmlToString(entity.questions.title),
|
||||
position,
|
||||
SEARCH_BUTTON_VIEW_IMAGE
|
||||
)
|
||||
}
|
||||
binding.imageContainer.bindData(
|
||||
entity.toImageContainerData(),
|
||||
object : ImageContainerView.OnImageContainerEventListener {
|
||||
override fun onImageClick(
|
||||
images: List<String>,
|
||||
position: Int,
|
||||
imageViewList: ArrayList<SimpleDraweeView>,
|
||||
) {
|
||||
|
||||
clickListener?.onItemChildClick(
|
||||
entity.id,
|
||||
entity.type,
|
||||
htmlToString(entity.questions.title),
|
||||
position,
|
||||
SEARCH_BUTTON_VIEW_IMAGE,
|
||||
)
|
||||
|
||||
if (entity.communityId.isNullOrEmpty()) {
|
||||
entity.communityId = entity.bbs.id
|
||||
}
|
||||
val intent = ImageViewerActivity.getIntent(
|
||||
itemView.context, entity.images as ArrayList<String>, position, imageViewList,
|
||||
if (entity.type == "community_article") entity else null, entrance, true,
|
||||
)
|
||||
itemView.context.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onVideoCLick(videoId: String) {
|
||||
DirectUtils.directToVideoDetail(
|
||||
itemView.context,
|
||||
videoId,
|
||||
VideoDetailContainerViewModel.Location.VIDEO_HOT.value,
|
||||
showComment = false,
|
||||
entrance = entrance,
|
||||
path = path
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
)
|
||||
bindVideoData(entity.transformForumVideoEntity())
|
||||
|
||||
val user = entity.user
|
||||
@ -315,34 +353,13 @@ class ForumArticleAskItemViewHolder(
|
||||
.setVideoAllCallBack(object : GSYSampleCallBack() {
|
||||
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
|
||||
orientationUtils.backToProtVideo()
|
||||
visibleView.uploadVideoStreamingPlaying("退出全屏")
|
||||
}
|
||||
})
|
||||
.build(visibleView)
|
||||
visibleView.run {
|
||||
updateVideoData(entity)
|
||||
updateThumb(entity.poster)
|
||||
updateDurationTv(entity.duration)
|
||||
|
||||
setVideoStatus(entity.status)
|
||||
|
||||
fullscreenButton.setOnClickListener {
|
||||
val horizontalVideoView =
|
||||
startWindowFullscreen(itemView.context, true, true) as? ArticleItemVideoView
|
||||
if (horizontalVideoView == null) {
|
||||
toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤")
|
||||
return@setOnClickListener
|
||||
}
|
||||
orientationUtils.resolveByClick()
|
||||
horizontalVideoView.uuid = uuid
|
||||
horizontalVideoView.updateVideoData(entity)
|
||||
horizontalVideoView.updateThumb(entity.poster)
|
||||
horizontalVideoView.violenceUpdateMuteStatus()
|
||||
horizontalVideoView.setFullViewStatus()
|
||||
uploadVideoStreamingPlaying("开始播放")
|
||||
uploadVideoStreamingPlaying("点击全屏")
|
||||
}
|
||||
}
|
||||
visibleView.updateVideoData(
|
||||
entity.toArticleVideoData(),
|
||||
AnswerArticleVideoViewEventHelper(entity, orientationUtils)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -382,7 +399,13 @@ class ForumArticleAskItemViewHolder(
|
||||
SEARCH_BUTTON_VIEW_FORUM_DETAIL
|
||||
)
|
||||
MtaHelper.onEvent(getEventId(entrance), getKey(entrance), entity.community.name)
|
||||
itemView.context.startActivity(ForumDetailActivity.getIntent(itemView.context, entity.community.id, entrance))
|
||||
itemView.context.startActivity(
|
||||
ForumDetailActivity.getIntent(
|
||||
itemView.context,
|
||||
entity.community.id,
|
||||
entrance
|
||||
)
|
||||
)
|
||||
LogUtils.uploadAccessToBbs(entity.community.id, "文章外所属论坛")
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import com.gh.gamecenter.common.base.fragment.LazyFragment
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
|
||||
import com.gh.gamecenter.core.iinterface.IScrollable
|
||||
import com.gh.gamecenter.databinding.ForumBannerIndicatorItemBinding
|
||||
import com.gh.gamecenter.databinding.FragmentForumBinding
|
||||
import com.gh.gamecenter.entity.ForumBannerEntity
|
||||
@ -34,7 +35,7 @@ import com.halo.assistant.HaloApp
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class ForumFragment : LazyFragment(), SwipeRefreshLayout.OnRefreshListener {
|
||||
class ForumFragment : LazyFragment(), SwipeRefreshLayout.OnRefreshListener, IScrollable {
|
||||
|
||||
private var mBinding: FragmentForumBinding? = null
|
||||
private var mViewModel: ForumViewModel? = null
|
||||
@ -513,4 +514,12 @@ class ForumFragment : LazyFragment(), SwipeRefreshLayout.OnRefreshListener {
|
||||
super.onDarkModeChanged()
|
||||
mBinding?.hotForumRv?.adapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }
|
||||
}
|
||||
|
||||
override fun scrollToTop() {
|
||||
mBinding?.contentContainer?.let {
|
||||
if (it.visibility == View.VISIBLE) {
|
||||
it.scrollTo(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.forum.home.follow.fragment.AllFollowedFragment
|
||||
|
||||
class AllFollowedActivity : BaseActivity() {
|
||||
override fun getLayoutId(): Int = R.layout.activity_all_followed
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
DisplayUtils.setLightStatusBar(this, true)
|
||||
setStatusBarColor(Color.TRANSPARENT)
|
||||
val containerFragment = supportFragmentManager.findFragmentByTag(AllFollowedFragment::class.java.name)
|
||||
?: AllFollowedFragment().with(intent.extras)
|
||||
// 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.layout_activity_content, containerFragment, AllFollowedFragment::class.java.name)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun getIntent(context: Context): Intent = Intent(context, AllFollowedActivity::class.java)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
|
||||
abstract class AllFollowedItem() {
|
||||
|
||||
abstract val itemType: Int
|
||||
|
||||
open fun areItemsTheSame(other: AllFollowedItem): Boolean {
|
||||
return other.itemType == itemType && doAreItemsTheSame(other)
|
||||
}
|
||||
|
||||
open fun areContentsTheSame(other: AllFollowedItem): Boolean {
|
||||
return other.itemType == itemType && doAreContentsTheSame(other)
|
||||
}
|
||||
|
||||
open fun doAreItemsTheSame(other: AllFollowedItem): Boolean = true
|
||||
|
||||
open fun doAreContentsTheSame(other: AllFollowedItem): Boolean = this == other
|
||||
|
||||
companion object {
|
||||
|
||||
const val ALL_FOLLOWED_ITEM_TYPE_NO_TOP = 0
|
||||
const val ALL_FOLLOWED_ITEM_TYPE_NORMAL = 1
|
||||
const val ALL_FOLLOWED_ITEM_TYPE_MY_FOLLOW_HEADER = 2
|
||||
}
|
||||
}
|
||||
|
||||
class AllFollowedNoTopItem : AllFollowedItem() {
|
||||
override val itemType: Int
|
||||
get() = ALL_FOLLOWED_ITEM_TYPE_NO_TOP
|
||||
|
||||
}
|
||||
|
||||
data class AllFollowedMyFollowedHeaderItem(val isTop: Boolean) : AllFollowedItem() {
|
||||
override val itemType: Int
|
||||
get() = ALL_FOLLOWED_ITEM_TYPE_MY_FOLLOW_HEADER
|
||||
}
|
||||
|
||||
data class AllFollowedNormalItem(
|
||||
val data: FollowUserEntity,
|
||||
var isTop: Boolean
|
||||
) : AllFollowedItem() {
|
||||
override val itemType: Int
|
||||
get() = ALL_FOLLOWED_ITEM_TYPE_NORMAL
|
||||
|
||||
override fun doAreItemsTheSame(other: AllFollowedItem): Boolean {
|
||||
return other is AllFollowedNormalItem && data.id == other.data.id
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,306 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.ActivityResultRegistry
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.gh.common.util.SyncDataBetweenPageHelper
|
||||
import com.gh.gamecenter.common.entity.CommunityEntity
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.CommentEntity
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.personalhome.home.UserHistoryFragment
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
|
||||
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
|
||||
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
|
||||
|
||||
class FollowActivityResultLauncher(
|
||||
private val registry: ActivityResultRegistry,
|
||||
private val onResultListener: OnResultListener
|
||||
) : DefaultLifecycleObserver {
|
||||
|
||||
private lateinit var articleDetailLauncher: ActivityResultLauncher<LauncherDestination>
|
||||
|
||||
private lateinit var commentEntityLauncher: ActivityResultLauncher<LauncherDestination>
|
||||
|
||||
private lateinit var questionsDetailLauncher: ActivityResultLauncher<LauncherDestination>
|
||||
|
||||
private lateinit var forumVideoLauncher: ActivityResultLauncher<LauncherDestination>
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
super.onCreate(owner)
|
||||
articleDetailLauncher = registry.register(
|
||||
KEY_ARTICLE_DETAIL_LAUNCHER,
|
||||
owner,
|
||||
object : ActivityResultContract<LauncherDestination, ArticleDetailResult?>() {
|
||||
override fun createIntent(context: Context, input: LauncherDestination): Intent {
|
||||
val intent =
|
||||
if (input.historyEntity != null) {
|
||||
ArticleDetailActivity.getIntent(
|
||||
context,
|
||||
input.historyEntity.community,
|
||||
input.historyEntity.id,
|
||||
"",
|
||||
UserHistoryFragment.PATH_USER_QUESTION_ANSWER,
|
||||
sourceEntrance = "关注-用户动态"
|
||||
)
|
||||
} else {
|
||||
ArticleDetailActivity.getIntent(
|
||||
context,
|
||||
CommunityEntity(input.answerEntity?.bbs?.id ?: ""),
|
||||
input.answerEntity?.id ?: "",
|
||||
"",
|
||||
"",
|
||||
sourceEntrance = "关注-论坛动态",
|
||||
)
|
||||
}
|
||||
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
|
||||
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 101)
|
||||
return intent
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): ArticleDetailResult? {
|
||||
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
|
||||
val resultData =
|
||||
intent?.getParcelableExtra<ArticleDetailEntity>(ArticleDetailEntity::class.java.simpleName)
|
||||
if (position != null && resultData != null) {
|
||||
return ArticleDetailResult(position, resultData)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
it?.run {
|
||||
onResultListener.onArticleDetailResult(position, articleDetailEntity)
|
||||
}
|
||||
}
|
||||
|
||||
commentEntityLauncher = registry.register(
|
||||
KEY_COMMENT_LAUNCHER,
|
||||
owner,
|
||||
object : ActivityResultContract<LauncherDestination, CommentResult?>() {
|
||||
override fun createIntent(context: Context, input: LauncherDestination): Intent {
|
||||
val intent = NewQuestionDetailActivity.getSpecifiedCommentIntent(
|
||||
context,
|
||||
input.historyEntity?.question?.id ?: "",
|
||||
input.historyEntity?.id ?: "",
|
||||
"",
|
||||
UserHistoryFragment.PATH_USER_QUESTION_ANSWER,
|
||||
sourceEntrance = "关注-动态"
|
||||
)
|
||||
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
|
||||
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 102)
|
||||
return intent
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): CommentResult? {
|
||||
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
|
||||
val commentEntity = intent?.getParcelableExtra<CommentEntity>(CommentEntity::class.java.simpleName)
|
||||
if (position != null && commentEntity != null) {
|
||||
return CommentResult(position, commentEntity)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
it?.run {
|
||||
onResultListener.onCommentResult(position, commentEntity)
|
||||
}
|
||||
}
|
||||
|
||||
questionsDetailLauncher = registry.register(
|
||||
KEY_QUESTION_DETAIL_LAUNCHER,
|
||||
owner,
|
||||
object : ActivityResultContract<LauncherDestination, QuestionsDetailResult?>() {
|
||||
override fun createIntent(context: Context, input: LauncherDestination): Intent {
|
||||
val intent = if (input.historyEntity != null) {
|
||||
NewQuestionDetailActivity.getIntent(
|
||||
context,
|
||||
input.historyEntity.id,
|
||||
"",
|
||||
UserHistoryFragment.PATH_USER_QUESTION_ANSWER
|
||||
)
|
||||
} else {
|
||||
if (input.answerEntity?.type == "question") {
|
||||
NewQuestionDetailActivity.getIntent(
|
||||
context, input.answerEntity.id,
|
||||
"",
|
||||
"",
|
||||
sourceEntrance = "关注-论坛动态"
|
||||
)
|
||||
} else {
|
||||
NewQuestionDetailActivity.getCommentIntent(
|
||||
context,
|
||||
input.answerEntity?.questions?.id ?: "",
|
||||
input.answerEntity?.answerId ?: "",
|
||||
"",
|
||||
""
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
|
||||
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 103)
|
||||
return intent
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): QuestionsDetailResult? {
|
||||
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
|
||||
val questionsDetailEntity =
|
||||
intent?.getParcelableExtra<QuestionsDetailEntity>(QuestionsDetailEntity::class.java.simpleName)
|
||||
return if (position != null && questionsDetailEntity != null) {
|
||||
QuestionsDetailResult(position, questionsDetailEntity)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
) {
|
||||
it?.run {
|
||||
onResultListener.onQuestionsDetailResult(position, questionsDetailEntity)
|
||||
}
|
||||
}
|
||||
|
||||
forumVideoLauncher = registry.register(
|
||||
KEY_FORUM_VIDEO_LAUNCHER,
|
||||
owner,
|
||||
object : ActivityResultContract<LauncherDestination, ForumVideoResult?>() {
|
||||
override fun createIntent(context: Context, input: LauncherDestination): Intent {
|
||||
val intent = if (input.historyEntity != null) {
|
||||
ForumVideoDetailActivity.getIntent(
|
||||
context,
|
||||
input.historyEntity.id,
|
||||
input.historyEntity.community.id,
|
||||
sourceEntrance = "关注-个人动态"
|
||||
)
|
||||
} else {
|
||||
ForumVideoDetailActivity.getIntent(
|
||||
context,
|
||||
input.answerEntity?.id ?: "",
|
||||
input.answerEntity?.bbs?.id ?: "",
|
||||
"关注-论坛动态"
|
||||
)
|
||||
}
|
||||
|
||||
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
|
||||
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 104)
|
||||
return intent
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): ForumVideoResult? {
|
||||
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
|
||||
val forumVideoEntity =
|
||||
intent?.getParcelableExtra<ForumVideoEntity>(ForumVideoEntity::class.java.simpleName)
|
||||
return if (position != null && forumVideoEntity != null) {
|
||||
ForumVideoResult(position, forumVideoEntity)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
{
|
||||
it?.run {
|
||||
onResultListener.onForumVideoResult(position, forumVideoEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onPersonItemClick(position: Int, entity: PersonalHistoryEntity) {
|
||||
when {
|
||||
entity.type == "community_article"
|
||||
|| entity.type == "community_article_vote" -> {
|
||||
articleDetailLauncher.launch(LauncherDestination(position, historyEntity = entity))
|
||||
}
|
||||
|
||||
entity.type.contains("video") -> {
|
||||
forumVideoLauncher.launch(LauncherDestination(position, historyEntity = entity))
|
||||
}
|
||||
|
||||
entity.type.contains("question") -> {
|
||||
questionsDetailLauncher.launch(LauncherDestination(position, historyEntity = entity))
|
||||
}
|
||||
|
||||
else -> {
|
||||
commentEntityLauncher.launch(LauncherDestination(position, historyEntity = entity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onBbsItemClick(position: Int, answerEntity: AnswerEntity) {
|
||||
when (answerEntity.type) {
|
||||
"community_article" -> {
|
||||
articleDetailLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
|
||||
}
|
||||
|
||||
"video" -> {
|
||||
forumVideoLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
|
||||
}
|
||||
|
||||
"question" -> {
|
||||
questionsDetailLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
questionsDetailLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_ARTICLE_DETAIL_LAUNCHER = "key_article_detail_launcher"
|
||||
private const val KEY_COMMENT_LAUNCHER = "key_comment_launcher"
|
||||
private const val KEY_QUESTION_DETAIL_LAUNCHER = "key_question_detail_launcher"
|
||||
private const val KEY_FORUM_VIDEO_LAUNCHER = "KEY_forum_video_LAUNCHER"
|
||||
}
|
||||
|
||||
private data class LauncherDestination(
|
||||
val position: Int,
|
||||
val historyEntity: PersonalHistoryEntity? = null,
|
||||
val answerEntity: AnswerEntity? = null
|
||||
)
|
||||
|
||||
private data class ArticleDetailResult(
|
||||
val position: Int,
|
||||
val articleDetailEntity: ArticleDetailEntity
|
||||
)
|
||||
|
||||
private data class CommentResult(
|
||||
val position: Int,
|
||||
val commentEntity: CommentEntity
|
||||
)
|
||||
|
||||
private data class QuestionsDetailResult(
|
||||
val position: Int,
|
||||
val questionsDetailEntity: QuestionsDetailEntity
|
||||
)
|
||||
|
||||
private data class ForumVideoResult(
|
||||
val position: Int,
|
||||
val forumVideoEntity: ForumVideoEntity
|
||||
)
|
||||
|
||||
interface OnResultListener {
|
||||
|
||||
fun onArticleDetailResult(position: Int, articleDetailEntity: ArticleDetailEntity)
|
||||
|
||||
fun onCommentResult(position: Int, commentEntity: CommentEntity)
|
||||
|
||||
fun onQuestionsDetailResult(position: Int, questionsDetailEntity: QuestionsDetailEntity)
|
||||
|
||||
fun onForumVideoResult(position: Int, forumVideoEntity: ForumVideoEntity)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Path
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
|
||||
class FollowClippedView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : androidx.appcompat.widget.AppCompatImageView(context, attrs, defStyleAttr) {
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val path = Path()
|
||||
path.addRect(
|
||||
0f,
|
||||
0f,
|
||||
width.toFloat(),
|
||||
(DisplayUtils.getStatusBarHeight(resources) + 52f.dip2px() + 56f.dip2px()).toFloat(),
|
||||
Path.Direction.CW
|
||||
)
|
||||
canvas.clipPath(path)
|
||||
|
||||
canvas.drawColor(ContextCompat.getColor(context, R.color.ui_background))
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.utils.DarkModeUtils.isDarkModeOn
|
||||
import com.gh.gamecenter.common.utils.toArrayList
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.fragment.FollowDynamicFragment
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowDynamicViewModel
|
||||
|
||||
class FollowDynamicActivity : BaseActivity() {
|
||||
|
||||
private val viewModel by viewModels<FollowDynamicViewModel>()
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.activity_amway
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
DisplayUtils.transparentStatusAndNavigation(this)
|
||||
DisplayUtils.setLightStatusBar(this, true)
|
||||
val containerFragment = supportFragmentManager.findFragmentByTag(FollowDynamicFragment::class.java.name)
|
||||
?: FollowDynamicFragment().with(intent.extras)
|
||||
// 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.placeholder, containerFragment, FollowDynamicFragment::class.java.name)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
val tv = findViewById<TextView>(ID_NIGHT_INDICATOR)
|
||||
if (tv != null) {
|
||||
tv.text = if (isDarkModeOn(this)) "深色模式" else "浅色模式"
|
||||
tv.alpha = if (isDarkModeOn(this)) 0.8f else 0.15f
|
||||
}
|
||||
if (isAutoResetViewBackgroundEnabled) {
|
||||
updateStaticViewBackground(window.decorView)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
window.navigationBarColor = ContextCompat.getColor(this, R.color.ui_surface)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleBackPressed(): Boolean {
|
||||
viewModel.finish()
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val KEY_SELECTED_POSITION = "key_selected_position"
|
||||
const val KEY_USERS = "key_users"
|
||||
|
||||
fun start(context: Context, position: Int, followedList: List<FollowUserEntity>) {
|
||||
val intent = Intent(context, FollowDynamicActivity::class.java)
|
||||
intent.putParcelableArrayListExtra(KEY_USERS, followedList.toArrayList())
|
||||
intent.putExtra(KEY_SELECTED_POSITION, position)
|
||||
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import com.gh.gamecenter.entity.HistoryGameEntity
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
|
||||
abstract class FollowDynamicItem {
|
||||
|
||||
abstract val itemType: Int
|
||||
|
||||
fun areItemsTheSame(other: FollowDynamicItem) =
|
||||
itemType == other.itemType && doAreItemsTheSame(other)
|
||||
|
||||
|
||||
fun areContentsTheSame(other: FollowDynamicItem) =
|
||||
itemType == other.itemType && doAreContentsTheSame(other)
|
||||
|
||||
protected open fun doAreItemsTheSame(other: FollowDynamicItem) = true
|
||||
|
||||
protected open fun doAreContentsTheSame(other: FollowDynamicItem) = this == other
|
||||
|
||||
companion object {
|
||||
|
||||
const val FOLLOW_DYNAMIC_ITEM_TYPE_FOOTER = -1
|
||||
const val FOLLOW_DYNAMIC_ITEM_TYPE_PERSONAL = 0
|
||||
const val FOLLOW_DYNAMIC_ITEM_TYPE_BBS = 1
|
||||
}
|
||||
}
|
||||
|
||||
data class FollowDynamicPersonalItem(
|
||||
val data: PersonalHistoryEntity
|
||||
) : FollowDynamicItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_DYNAMIC_ITEM_TYPE_PERSONAL
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowDynamicItem): Boolean {
|
||||
return other is FollowDynamicPersonalItem
|
||||
&& data.id == other.data.id
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class FollowDynamicBbsItem(
|
||||
val data: AnswerEntity
|
||||
) : FollowDynamicItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_DYNAMIC_ITEM_TYPE_BBS
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowDynamicItem): Boolean {
|
||||
return other is FollowDynamicBbsItem
|
||||
&& data.id == other.data.id
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object FollowDynamicFooterItem : FollowDynamicItem() {
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_DYNAMIC_ITEM_TYPE_FOOTER
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.CheckedTextView
|
||||
import android.widget.PopupWindow
|
||||
import androidx.core.view.children
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.databinding.LayoutForumActivityCategoryItemBinding
|
||||
import com.gh.gamecenter.databinding.PopFollowFilterBinding
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
|
||||
class FollowHomeFilterPopWindow(
|
||||
private val binding: PopFollowFilterBinding
|
||||
) : PopupWindow(binding.root, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) {
|
||||
|
||||
private val data = arrayListOf("全部", "关注的人", "关注的游戏")
|
||||
|
||||
init {
|
||||
isOutsideTouchable = true
|
||||
isFocusable = true
|
||||
|
||||
val layoutParams = binding.ivTopBackground.layoutParams as MarginLayoutParams
|
||||
layoutParams.topMargin = -52F.dip2px() - DisplayUtils.getStatusBarHeight(binding.root.context.resources)
|
||||
binding.ivTopBackground.layoutParams = layoutParams
|
||||
|
||||
val inflater = LayoutInflater.from(binding.root.context)
|
||||
data.forEachIndexed { index, entity ->
|
||||
LayoutForumActivityCategoryItemBinding.inflate(inflater).apply {
|
||||
val params =
|
||||
FlexboxLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
||||
params.setMargins(0, 8F.dip2px(), 8F.dip2px(), 0)
|
||||
params.height = 24F.dip2px()
|
||||
root.layoutParams = params
|
||||
|
||||
name.text = entity
|
||||
name.isChecked = index == 0
|
||||
root.setOnClickListener {
|
||||
updateCategory(index)
|
||||
}
|
||||
binding.fblFilter.addView(root)
|
||||
}
|
||||
}
|
||||
binding.vBackground.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
if (DarkModeUtils.isDarkModeOn(binding.root.context)) {
|
||||
binding.clFilterContainer.setBackgroundColor(R.color.ui_background.toColor(binding.root.context))
|
||||
binding.ivTopBackground.goneIf(true)
|
||||
} else {
|
||||
binding.clFilterContainer.background = null
|
||||
binding.ivTopBackground.goneIf(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCategory(index: Int) {
|
||||
filteredListener?.filterFollowed(index)
|
||||
binding.fblFilter.children.forEachIndexed { position, child ->
|
||||
(child as? CheckedTextView)?.isChecked = (position == index)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private var filteredListener: OnFilteredChangedListener? = null
|
||||
fun setFilterListener(filteredListener: OnFilteredChangedListener) {
|
||||
this.filteredListener = filteredListener
|
||||
}
|
||||
|
||||
fun onDarkModeChanged() {
|
||||
if (DarkModeUtils.isDarkModeOn(binding.root.context)) {
|
||||
binding.clFilterContainer.setBackgroundColor(R.color.ui_background.toColor(binding.root.context))
|
||||
binding.ivTopBackground.goneIf(true)
|
||||
} else {
|
||||
binding.clFilterContainer.background = null
|
||||
binding.ivTopBackground.goneIf(false)
|
||||
}
|
||||
val context = binding.root.context
|
||||
binding.fblFilter.children.forEach {
|
||||
if (it is CheckedTextView) {
|
||||
val colorInt = R.color.forum_category_selector.toColor(context)
|
||||
it.setTextColor(ColorStateList.valueOf(colorInt))
|
||||
val drawable = R.drawable.selector_bg_forum_activity_category.toDrawable(context)
|
||||
it.background = drawable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(context: Context): FollowHomeFilterPopWindow {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = PopFollowFilterBinding.inflate(inflater, null, false)
|
||||
return FollowHomeFilterPopWindow(binding)
|
||||
}
|
||||
}
|
||||
|
||||
interface OnFilteredChangedListener {
|
||||
|
||||
fun filterFollowed(position: Int)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,201 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity
|
||||
import com.gh.gamecenter.feature.entity.*
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER
|
||||
|
||||
abstract class FollowItem {
|
||||
|
||||
abstract val itemType: Int
|
||||
|
||||
fun areItemsTheSame(other: FollowItem) =
|
||||
itemType == other.itemType && doAreItemsTheSame(other)
|
||||
|
||||
|
||||
fun areContentsTheSame(other: FollowItem) =
|
||||
itemType == other.itemType && doAreContentsTheSame(other)
|
||||
|
||||
protected open fun doAreItemsTheSame(other: FollowItem) = true
|
||||
|
||||
protected open fun doAreContentsTheSame(other: FollowItem) = this == other
|
||||
|
||||
companion object {
|
||||
|
||||
const val FOLLOW_ITEM_TYPE_INVALID = -4
|
||||
const val FOLLOW_ITEM_TYPE_EMPTY = -3
|
||||
const val FOLLOW_ITEM_TYPE_FOOTER = -2
|
||||
const val FOLLOW_ITEM_TYPE_HEADER = -1
|
||||
const val FOLLOW_ITEM_TYPE_RECOMMEND_USER = 0
|
||||
const val FOLLOW_ITEM_TYPE_COMMON_BANNER = 1
|
||||
const val FOLLOW_ITEM_TYPE_COMMON_BANNER_WITH_CARDS = 2
|
||||
const val FOLLOW_ITEM_TYPE_NAVIGATION = 3
|
||||
const val FOLLOW_ITEM_TYPE_RECOMMEND = 4
|
||||
const val FOLLOW_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE = 5
|
||||
const val FOLLOW_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS = 6
|
||||
const val FOLLOW_ITEM_TYPE_POST_CARD = 7
|
||||
const val FOLLOW_ITEM_TYPE_GIFT_PACK = 8
|
||||
const val FOLLOW_ITEM_TYPE_ARTICLE = 9
|
||||
|
||||
// 通用内容合集 样式
|
||||
val commonContentCollection: HashMap<Int, Int> = hashMapOf(
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_NAVIGATION to FOLLOW_ITEM_TYPE_NAVIGATION,
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_KING_KONG to FOLLOW_ITEM_TYPE_RECOMMEND,
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER to FOLLOW_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE,
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER to FOLLOW_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS,
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD to FOLLOW_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE,
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD to FOLLOW_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS,
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT to FOLLOW_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS,
|
||||
CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT to FOLLOW_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class FollowRecommendUsersItem(
|
||||
val data: List<UserEntity>
|
||||
) : FollowItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_RECOMMEND_USER
|
||||
}
|
||||
|
||||
class FollowCommonCollectionItem(
|
||||
val data: FollowCommonContentCollection
|
||||
) : FollowItem() {
|
||||
override val itemType: Int
|
||||
get() = if (data.linkCommonCollection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_BANNER) {
|
||||
// 轮播图右边卡片列表, 至少需要2个卡片才能显示
|
||||
if (data.linkCommonCollection.slides.subSlide.size < 2) {
|
||||
FOLLOW_ITEM_TYPE_COMMON_BANNER
|
||||
} else {
|
||||
FOLLOW_ITEM_TYPE_COMMON_BANNER_WITH_CARDS
|
||||
}
|
||||
} else {
|
||||
commonContentCollection[data.linkCommonCollection.layout] ?: FOLLOW_ITEM_TYPE_INVALID
|
||||
}
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowCommonCollectionItem
|
||||
&& data.linkCommonCollection.id == other.data.linkCommonCollection.id
|
||||
}
|
||||
|
||||
override fun doAreContentsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowCommonCollectionItem
|
||||
&& data.title == other.data.title
|
||||
&& data.linkCommonCollection == other.data.linkCommonCollection
|
||||
}
|
||||
}
|
||||
|
||||
data class FollowSplitCommonContentCollectionItem(
|
||||
val data: CustomPageData.CommonContentCollection,
|
||||
val leftPosition: Int,
|
||||
) : FollowItem() {
|
||||
|
||||
override val itemType
|
||||
get() = commonContentCollection[data.layout] ?: FOLLOW_ITEM_TYPE_INVALID
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowSplitCommonContentCollectionItem
|
||||
&& data.id == other.data.id
|
||||
}
|
||||
|
||||
override fun doAreContentsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowSplitCommonContentCollectionItem
|
||||
&& data == other.data
|
||||
&& data.data.getOrNull(leftPosition) == other.data.data.getOrNull(leftPosition)
|
||||
&& data.data.getOrNull(leftPosition + 1) == other.data.data.getOrNull(leftPosition + 1)
|
||||
}
|
||||
}
|
||||
|
||||
data class FollowPostCardItem(
|
||||
val data: ArticleEntity
|
||||
) : FollowItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_POST_CARD
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowPostCardItem
|
||||
&& data.id == other.data.id
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class FollowArticleItem(
|
||||
val data: FollowDynamicEntity.Article
|
||||
) : FollowItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_ARTICLE
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowArticleItem
|
||||
&& data.id == other.data.id
|
||||
}
|
||||
}
|
||||
|
||||
data class FollowGiftPackItem(
|
||||
val libao: LibaoEntity,
|
||||
val time: Long
|
||||
) : FollowItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_GIFT_PACK
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowGiftPackItem
|
||||
&& libao.id == other.libao.id
|
||||
}
|
||||
|
||||
override fun doAreContentsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowGiftPackItem
|
||||
&& time == other.time
|
||||
&& libao.name == other.libao.name
|
||||
&& libao.des == other.libao.des
|
||||
&& libao.content == other.libao.content
|
||||
&& libao.code == other.libao.code
|
||||
&& libao.game?.mIcon == other.libao.game?.mIcon
|
||||
&& libao.game?.iconFloat == other.libao.game?.iconFloat
|
||||
&& libao.game?.iconSubscript == other.libao.game?.iconSubscript
|
||||
&& libao.game?.name == other.libao.game?.name
|
||||
}
|
||||
}
|
||||
|
||||
data class FollowUserItem(
|
||||
val isLogin: Boolean,
|
||||
val data: List<FollowUserEntity>
|
||||
) : FollowItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_HEADER
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowItem): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun doAreContentsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowUserItem
|
||||
&& isLogin == other.isLogin
|
||||
&& data == other.data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object FollowEmptyItem : FollowItem() {
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_EMPTY
|
||||
}
|
||||
|
||||
object FollowFooterItem : FollowItem() {
|
||||
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_FOOTER
|
||||
}
|
||||
|
||||
object InvalidItem : FollowItem() {
|
||||
override val itemType: Int
|
||||
get() = FOLLOW_ITEM_TYPE_INVALID
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
class FollowPageConfiguration(
|
||||
val entrance: String,
|
||||
val path: String
|
||||
)
|
||||
@ -0,0 +1,51 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.utils.DarkModeUtils
|
||||
|
||||
class ResetDataChangeHelper<T>(
|
||||
private val context: Context,
|
||||
private val adapter: RecyclerView.Adapter<*>
|
||||
) {
|
||||
|
||||
private var darkMode = DarkModeUtils.isDarkModeOn(context)
|
||||
|
||||
private val _dataList = arrayListOf<T>()
|
||||
val dataList: List<T> = _dataList
|
||||
|
||||
val dataCount: Int
|
||||
get() = dataList.size
|
||||
|
||||
fun getItem(position: Int) = dataList[position]
|
||||
|
||||
private var _countAndKey: Pair<Int, String>? = null
|
||||
|
||||
fun submitList(data: List<T>?, getKey: (T) -> String) {
|
||||
_dataList.clear()
|
||||
data?.let(_dataList::addAll)
|
||||
|
||||
if (data.isNullOrEmpty()) {
|
||||
_countAndKey = null
|
||||
adapter.notifyDataSetChanged()
|
||||
return
|
||||
}
|
||||
val countAndKey = _countAndKey ?: Pair(0, "")
|
||||
val (oldSize, oldKeys) = countAndKey
|
||||
|
||||
val newSize = data.size
|
||||
var newKeys = ""
|
||||
data.forEach {
|
||||
newKeys += getKey(it)
|
||||
}
|
||||
if (oldSize == newSize) {
|
||||
if (oldKeys != newKeys || darkMode != DarkModeUtils.isDarkModeOn(context)) { // 数量不变,内容发生变化 || 切换浅色模式
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount, "")
|
||||
}
|
||||
} else {// 数量发生变化
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
darkMode = DarkModeUtils.isDarkModeOn(context)
|
||||
_countAndKey = Pair(newSize, newKeys)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,209 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.consume
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.databinding.RecyclerAllFollowMyFollowHeaderBinding
|
||||
import com.gh.gamecenter.databinding.RecyclerAllFollowedBinding
|
||||
import com.gh.gamecenter.databinding.RecyclerAllFollowedNoTopBinding
|
||||
import com.gh.gamecenter.entity.FollowOperateTopRequest
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedItem
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedItem.Companion.ALL_FOLLOWED_ITEM_TYPE_MY_FOLLOW_HEADER
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedItem.Companion.ALL_FOLLOWED_ITEM_TYPE_NORMAL
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedItem.Companion.ALL_FOLLOWED_ITEM_TYPE_NO_TOP
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedMyFollowedHeaderItem
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedNoTopItem
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedNormalItem
|
||||
import com.gh.gamecenter.forum.home.follow.viewholder.FollowInvalidViewHolder
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.AllFollowedViewModel
|
||||
|
||||
class AllFollowedAdapter(
|
||||
private val viewModel: AllFollowedViewModel,
|
||||
private val startDrag: (ViewHolder) -> Unit,
|
||||
) : ListAdapter<AllFollowedItem, ViewHolder>(createDiffItemCallBack()) {
|
||||
|
||||
private var _isEdit = false
|
||||
val isEdit: Boolean
|
||||
get() = _isEdit
|
||||
|
||||
// 置顶需要移动到的位置
|
||||
private var _topItemCount = 0
|
||||
val topItemCount: Int
|
||||
get() = _topItemCount
|
||||
|
||||
// 已关注用户的数量(排除header footer等其它类型的item)
|
||||
private var _followedCount = 0
|
||||
val followedCount: Int
|
||||
get() = _followedCount
|
||||
|
||||
private val _dataList = arrayListOf<AllFollowedNormalItem>()
|
||||
val dataList: List<AllFollowedNormalItem>
|
||||
get() = _dataList
|
||||
|
||||
fun setData(data: List<AllFollowedItem>) {
|
||||
_dataList.clear()
|
||||
_dataList.addAll(data.filterIsInstance<AllFollowedNormalItem>())
|
||||
_followedCount = dataList.size
|
||||
_topItemCount = dataList.count(AllFollowedNormalItem::isTop)
|
||||
|
||||
val dataWithHeader = arrayListOf<AllFollowedItem>()
|
||||
data.forEachIndexed { index, item ->
|
||||
if (index == 0) {
|
||||
dataWithHeader.add(AllFollowedMyFollowedHeaderItem(true))
|
||||
}
|
||||
if (topItemCount == 0 && index == 0) {
|
||||
// 暂时没有置顶的关注,需要显示提示
|
||||
dataWithHeader.add(AllFollowedNoTopItem())
|
||||
dataWithHeader.add(AllFollowedMyFollowedHeaderItem(false))
|
||||
} else if (topItemCount == index) {
|
||||
dataWithHeader.add(AllFollowedMyFollowedHeaderItem(false))
|
||||
}
|
||||
dataWithHeader.add(item)
|
||||
}
|
||||
super.submitList(dataWithHeader)
|
||||
}
|
||||
|
||||
public override fun getItem(position: Int): AllFollowedItem {
|
||||
return super.getItem(position)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int = currentList[position].itemType
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
|
||||
when (viewType) {
|
||||
ALL_FOLLOWED_ITEM_TYPE_NO_TOP -> NoTopViewHolder(parent.toBinding())
|
||||
ALL_FOLLOWED_ITEM_TYPE_NORMAL -> AllFollowedViewHolder(parent.toBinding())
|
||||
ALL_FOLLOWED_ITEM_TYPE_MY_FOLLOW_HEADER -> MyFollowerHeaderViewHolder(parent.toBinding())
|
||||
else -> FollowInvalidViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
if (holder is AllFollowedViewHolder) {
|
||||
val itemBinding = holder.binding
|
||||
val item = getItem(position) as AllFollowedNormalItem
|
||||
val data = item.data
|
||||
if (isEdit) {
|
||||
itemBinding.ivUpOrDown.goneIf(false)
|
||||
itemBinding.ivDrag.goneIf(!item.isTop)
|
||||
itemBinding.vNewTips.goneIf(true)
|
||||
} else {
|
||||
itemBinding.ivUpOrDown.goneIf(true)
|
||||
itemBinding.ivDrag.goneIf(true)
|
||||
itemBinding.vNewTips.goneIf(!data.isShowTip)
|
||||
}
|
||||
val editResourceId = if (item.isTop) R.drawable.ic_all_followed_down else R.drawable.ic_all_followed_up
|
||||
itemBinding.ivUpOrDown.setImageResource(editResourceId)
|
||||
itemBinding.tvName.text = data.name
|
||||
if (data.isUser) {
|
||||
itemBinding.ivUserIcon.goneIf(false) {
|
||||
itemBinding.ivUserIcon.displayGameIcon(data.icon, null)
|
||||
}
|
||||
itemBinding.ivGameIcon.goneIf(true)
|
||||
} else {
|
||||
itemBinding.ivUserIcon.goneIf(true)
|
||||
itemBinding.ivGameIcon.goneIf(false) {
|
||||
itemBinding.ivGameIcon.displayGameIcon(data.icon, null)
|
||||
}
|
||||
}
|
||||
|
||||
itemBinding.ivUpOrDown.setOnClickListener {
|
||||
if (item.isTop) {
|
||||
cancelToTop(data.id)
|
||||
} else {
|
||||
toTop(data.id)
|
||||
}
|
||||
}
|
||||
|
||||
itemBinding.ivDrag.setOnTouchListener { _, event ->
|
||||
consume {
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
startDrag(holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrayOf(itemBinding.ivGameIcon, itemBinding.ivUserIcon, itemBinding.tvName)
|
||||
.forEach {
|
||||
it.setOnClickListener {
|
||||
viewModel.navigateToDetailPage(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toTop(id: String) {
|
||||
if (topItemCount >= itemCount) {
|
||||
return
|
||||
}
|
||||
|
||||
val newList = arrayListOf<AllFollowedNormalItem>()
|
||||
val targetItem = dataList.find { it.data.id == id }
|
||||
dataList.forEach {
|
||||
if (newList.size == topItemCount && targetItem != null) {
|
||||
newList.add(targetItem.copy(isTop = true))
|
||||
}
|
||||
if (it.data.id != id) {
|
||||
newList.add(AllFollowedNormalItem(it.data, it.isTop))
|
||||
}
|
||||
}
|
||||
setData(newList)
|
||||
}
|
||||
|
||||
private fun cancelToTop(id: String) {
|
||||
if (topItemCount == 0) {
|
||||
return
|
||||
}
|
||||
val newList = arrayListOf<AllFollowedNormalItem>()
|
||||
val targetItem = dataList.find { it.data.id == id }
|
||||
dataList.forEach {
|
||||
if (topItemCount == newList.size && targetItem != null) {
|
||||
newList.add(targetItem.copy(isTop = false))
|
||||
}
|
||||
if (it.data.id != id) {
|
||||
newList.add(it.copy())
|
||||
}
|
||||
}
|
||||
setData(newList)
|
||||
}
|
||||
|
||||
fun changeToEditable() {
|
||||
_isEdit = !isEdit
|
||||
notifyItemRangeChanged(0, itemCount, "")
|
||||
}
|
||||
|
||||
fun getTopItem() =
|
||||
dataList.filter {
|
||||
it.isTop
|
||||
}.mapIndexed { index, item ->
|
||||
FollowOperateTopRequest(item.data.id, index + 1)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
fun createDiffItemCallBack() = object : ItemCallback<AllFollowedItem>() {
|
||||
override fun areItemsTheSame(oldItem: AllFollowedItem, newItem: AllFollowedItem): Boolean {
|
||||
return oldItem.areItemsTheSame(newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: AllFollowedItem, newItem: AllFollowedItem): Boolean {
|
||||
return oldItem.areContentsTheSame(newItem)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class NoTopViewHolder(val binding: RecyclerAllFollowedNoTopBinding) : ViewHolder(binding.root)
|
||||
|
||||
class MyFollowerHeaderViewHolder(val binding: RecyclerAllFollowMyFollowHeaderBinding) : ViewHolder(binding.root)
|
||||
|
||||
class AllFollowedViewHolder(val binding: RecyclerAllFollowedBinding) : ViewHolder(binding.root)
|
||||
}
|
||||
@ -0,0 +1,217 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.Questions
|
||||
import com.gh.gamecenter.forum.home.ForumArticleAskItemViewHolder
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicBbsItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicFooterItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicItem.Companion.FOLLOW_DYNAMIC_ITEM_TYPE_BBS
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicItem.Companion.FOLLOW_DYNAMIC_ITEM_TYPE_FOOTER
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicItem.Companion.FOLLOW_DYNAMIC_ITEM_TYPE_PERSONAL
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicPersonalItem
|
||||
import com.gh.gamecenter.forum.home.follow.viewholder.FollowFooterViewHolder
|
||||
import com.gh.gamecenter.forum.home.follow.viewholder.FollowInvalidViewHolder
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowDynamicListViewModel
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowDynamicViewModel
|
||||
import com.gh.gamecenter.forum.search.CommunitySearchEventListener
|
||||
import com.gh.gamecenter.personalhome.PersonalItemViewHolder
|
||||
|
||||
class FollowDynamicAdapter(
|
||||
private val context: Context,
|
||||
private val viewModel: FollowDynamicListViewModel
|
||||
) : ListAdapter<FollowDynamicItem, ViewHolder>(createDiffCallback()) {
|
||||
|
||||
private var _recyclerView: RecyclerView? = null
|
||||
private var loadStatus: LoadStatus? = null
|
||||
|
||||
fun setLoadStatus(status: LoadStatus) {
|
||||
loadStatus = status
|
||||
notifyItemChanged(itemCount - 1)
|
||||
}
|
||||
|
||||
override fun submitList(list: List<FollowDynamicItem>?) {
|
||||
val dataWithFooter = if (list.isNullOrEmpty()) {
|
||||
ArrayList(list)
|
||||
} else {
|
||||
list + FollowDynamicFooterItem
|
||||
}
|
||||
super.submitList(dataWithFooter)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return currentList[position].itemType
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return when (viewType) {
|
||||
FOLLOW_DYNAMIC_ITEM_TYPE_PERSONAL -> {
|
||||
FollowDynamicPersonalViewHolder(context, parent.toBinding()) { history, position ->
|
||||
viewModel.navigateToPersonDetailPage(history, position)
|
||||
}
|
||||
}
|
||||
|
||||
FOLLOW_DYNAMIC_ITEM_TYPE_BBS -> {
|
||||
FollowDynamicBbSViewHolder(parent.toBinding(), viewModel.bbsId) { position, answer ->
|
||||
viewModel.navigateToBbsDetailPage(position, answer)
|
||||
}
|
||||
}
|
||||
|
||||
FOLLOW_DYNAMIC_ITEM_TYPE_FOOTER ->
|
||||
FollowFooterViewHolder(parent.toBinding())
|
||||
|
||||
else -> {
|
||||
FollowInvalidViewHolder(parent.toBinding())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is FollowDynamicPersonalViewHolder -> {
|
||||
val item = getItem(position)
|
||||
if (item is FollowDynamicPersonalItem) {
|
||||
holder.onBind(item.data, "", position)
|
||||
}
|
||||
}
|
||||
|
||||
is FollowDynamicBbSViewHolder -> {
|
||||
val item = getItem(position)
|
||||
if (item is FollowDynamicBbsItem) {
|
||||
holder.onBind(item.data, "", "", position)
|
||||
}
|
||||
}
|
||||
|
||||
is FollowFooterViewHolder -> {
|
||||
holder.initFooterViewHolder(
|
||||
loadStatus == LoadStatus.LIST_LOADING,
|
||||
loadStatus == LoadStatus.LIST_FAILED,
|
||||
loadStatus == LoadStatus.LIST_OVER,
|
||||
R.string.load_over_with_click_hint
|
||||
) {
|
||||
if (loadStatus == LoadStatus.LIST_OVER) {
|
||||
_recyclerView?.scrollToPosition(0)
|
||||
} else if (loadStatus == LoadStatus.LIST_FAILED) {
|
||||
viewModel.loadMore()
|
||||
notifyItemChanged(itemCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
recyclerView.addOnScrollListener(onLoadMoreListener)
|
||||
_recyclerView = recyclerView
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
recyclerView.removeOnScrollListener(onLoadMoreListener)
|
||||
_recyclerView = null
|
||||
}
|
||||
|
||||
private val onLoadMoreListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
val layoutManger = recyclerView.layoutManager
|
||||
if (layoutManger is LinearLayoutManager && itemCount != 0) {
|
||||
val lastVisibleItemPosition = layoutManger.findLastVisibleItemPosition()
|
||||
if (lastVisibleItemPosition == itemCount - 1 && loadStatus != LoadStatus.LIST_OVER && loadStatus != LoadStatus.LIST_LOADING) {
|
||||
viewModel.loadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createDiffCallback() = object : ItemCallback<FollowDynamicItem>() {
|
||||
override fun areItemsTheSame(oldItem: FollowDynamicItem, newItem: FollowDynamicItem): Boolean {
|
||||
return oldItem.areItemsTheSame(newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: FollowDynamicItem, newItem: FollowDynamicItem): Boolean {
|
||||
return oldItem.areContentsTheSame(newItem)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class FollowDynamicPersonalViewHolder(
|
||||
private val context: Context,
|
||||
val binding: CommunityAnswerItemBinding,
|
||||
private val itemClickCallback: (historyEntity: PersonalHistoryEntity, position: Int) -> Unit
|
||||
) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
// 复用原有 viewHolder
|
||||
private val realViewHolder by lazy(LazyThreadSafetyMode.NONE) {
|
||||
PersonalItemViewHolder(context, binding, itemClickCallback)
|
||||
}
|
||||
|
||||
fun onBind(historyEntity: PersonalHistoryEntity, entrance: String, position: Int) {
|
||||
realViewHolder.bindPersonalItem(historyEntity, entrance, position)
|
||||
}
|
||||
}
|
||||
|
||||
class FollowDynamicBbSViewHolder(
|
||||
val binding: CommunityAnswerItemBinding,
|
||||
private val bbsId: String,
|
||||
private val onItemClick: (Int, AnswerEntity) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
private val viewHolder by lazy(LazyThreadSafetyMode.NONE) {
|
||||
ForumArticleAskItemViewHolder(binding)
|
||||
}
|
||||
|
||||
fun onBind(answer: AnswerEntity, entrance: String, path: String, position: Int) {
|
||||
val articlePosition = position - 1
|
||||
if (answer.type != "answer") {
|
||||
val questions = Questions()
|
||||
questions.id = answer.id
|
||||
questions.title = answer.title
|
||||
questions.description = answer.description
|
||||
questions.answerCount = answer.count.answer
|
||||
answer.questions = questions
|
||||
}
|
||||
if (path == "精华" && answer.type != "answer" && answer.type != "video") answer.type = "community_article"
|
||||
if (path == "问答") answer.type = "question"
|
||||
if (path == "视频") answer.type = "video"
|
||||
|
||||
if (answer.bbs.id.isEmpty()) answer.bbs.id = bbsId
|
||||
|
||||
binding.forumNameLl.visibility = View.GONE
|
||||
binding.topLine.goneIf(articlePosition == 0)
|
||||
|
||||
val params = binding.includeVoteAndComment.root.layoutParams as LinearLayout.LayoutParams
|
||||
params.width = LinearLayout.LayoutParams.MATCH_PARENT
|
||||
params.leftMargin = 0
|
||||
params.rightMargin = 0
|
||||
binding.includeVoteAndComment.root.layoutParams = params
|
||||
|
||||
viewHolder.bindForumAnswerItem(answer, entrance, path, position)
|
||||
viewHolder.itemView.setOnClickListener {
|
||||
onItemClick(position, answer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,504 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.syncpage.ISyncAdapterHandler
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.feature.entity.ConcernEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.LibaoEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.forum.home.follow.*
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_ARTICLE
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_COMMON_BANNER
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_COMMON_BANNER_WITH_CARDS
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_EMPTY
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_FOOTER
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_GIFT_PACK
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_HEADER
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_NAVIGATION
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_POST_CARD
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_RECOMMEND
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem.Companion.FOLLOW_ITEM_TYPE_RECOMMEND_USER
|
||||
import com.gh.gamecenter.forum.home.follow.viewholder.*
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowHomeViewModel
|
||||
import com.gh.gamecenter.home.custom.OnViewHolderAttachListener
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
|
||||
class FollowHomeAdapter(
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val viewModel: FollowHomeViewModel,
|
||||
private val pageConfiguration: FollowPageConfiguration
|
||||
) : ListAdapter<FollowItem, ViewHolder>(createItemCallback()), ISyncAdapterHandler {
|
||||
|
||||
private var loadStatus: LoadStatus? = null
|
||||
|
||||
private var _recyclerView: RecyclerView? = null
|
||||
|
||||
override fun submitList(list: List<FollowItem>?) {
|
||||
val dataWithFooter = if (list.isNullOrEmpty()) {
|
||||
ArrayList(list)
|
||||
} else {
|
||||
list + FollowFooterItem
|
||||
}
|
||||
super.submitList(dataWithFooter)
|
||||
}
|
||||
|
||||
fun setLoadStatus(status: LoadStatus) {
|
||||
loadStatus = status
|
||||
notifyItemChanged(itemCount - 1)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return currentList[position].itemType
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
var change: EBUserFollow? = null
|
||||
var readStatusUpdate: ReadStatusUpdate? = null
|
||||
payloads.forEach {
|
||||
if (it is EBUserFollow) {
|
||||
change = it
|
||||
}
|
||||
if (it is ReadStatusUpdate) {
|
||||
readStatusUpdate = it
|
||||
}
|
||||
|
||||
}
|
||||
when {
|
||||
change != null -> { // 局部刷新,关注/取消关注
|
||||
(holder as? FollowRecommendListViewHolder)?.updateFollowed(change!!)
|
||||
}
|
||||
|
||||
readStatusUpdate != null -> { // 局部刷新 关注用户是否有新消息
|
||||
(holder as? FollowHomeHeaderViewHolder)?.updateReadStatus(readStatusUpdate!!.ids)
|
||||
}
|
||||
|
||||
else -> {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
|
||||
when (viewType) {
|
||||
FOLLOW_ITEM_TYPE_HEADER -> {
|
||||
FollowHomeHeaderViewHolder(parent.toBinding(), object : FollowHomeHeaderViewHolder.OnEventListener {
|
||||
override fun onItemClick(position: Int, data: List<FollowUserEntity>) {
|
||||
viewModel.viewItemFollowed(position, data)
|
||||
}
|
||||
|
||||
override fun onAllClick() {
|
||||
viewModel.viewAllFollowed()
|
||||
}
|
||||
|
||||
override fun onLoginClick() {
|
||||
viewModel.login()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_RECOMMEND_USER -> {
|
||||
FollowRecommendListViewHolder(parent.toBinding(),
|
||||
object : FollowRecommendListAdapter.OnChildEventListener {
|
||||
override fun onItemClick(childPosition: Int, userId: String) {
|
||||
SensorsBridge.trackFollowPageRecommendedUserCardClick(userId, childPosition + 1)
|
||||
viewModel.navigateToUserHomePage(userId)
|
||||
}
|
||||
|
||||
override fun onFollowClick(childPosition: Int, userId: String, isFollow: Boolean) {
|
||||
SensorsBridge.trackRecommendedUserFollowButtonClick(userId, childPosition + 1)
|
||||
viewModel.followUser(userId, isFollow)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_COMMON_BANNER -> {
|
||||
FollowHomeSlideListViewHolder(
|
||||
parent.toBinding(),
|
||||
lifecycleOwner,
|
||||
object : FollowHomeSlideListViewHolder.OnChildEventListener {
|
||||
override fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
viewModel.navigateToLinkPageInCommonContent(position, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
override fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String) {
|
||||
viewModel.navigateToGameDetailPage(position, game, text)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_COMMON_BANNER_WITH_CARDS -> {
|
||||
FollowHomeSlideWithCardsViewHolder(
|
||||
parent.toBinding(),
|
||||
viewModel.commonContentCollectionUseCase,
|
||||
lifecycleOwner,
|
||||
object : FollowHomeSlideWithCardsViewHolder.OnChildEventListener {
|
||||
override fun navigateToGameDetailPage(
|
||||
childPosition: Int,
|
||||
gameEntity: GameEntity,
|
||||
text: String
|
||||
) {
|
||||
viewModel.navigateToGameDetailPage(childPosition, gameEntity, text)
|
||||
}
|
||||
|
||||
override fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
viewModel.navigateToLinkPageInCommonContent(position, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_NAVIGATION -> {
|
||||
FollowNavigationViewHolder(parent.toBinding(),
|
||||
object : FollowNavigationViewHolder.OnChildEventListener {
|
||||
override fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
viewModel.navigateToLinkPageInCommonContent(position, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_RECOMMEND -> {
|
||||
FollowHomeRecommendItemViewHolder(
|
||||
parent.toBinding(),
|
||||
object : FollowHomeRecommendItemViewHolder.OnChildEventListener {
|
||||
override fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
viewModel.navigateToLinkPageInCommonContent(position, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE ->
|
||||
FollowCommonCollectionViewHolder(
|
||||
parent.toBinding(),
|
||||
object : FollowCommonCollectionViewHolder.OnChildEventListener {
|
||||
override fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
viewModel.navigateToLinkPageInCommonContent(position, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection) {
|
||||
viewModel.navigateToCommonCollectionDetailPage(data)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
FOLLOW_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS ->
|
||||
FollowCommonCollection12ViewHolder(
|
||||
parent.toBinding(),
|
||||
object : FollowCommonCollection12ViewHolder.OnChildEventListener {
|
||||
override fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
viewModel.navigateToLinkPageInCommonContent(position, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection) {
|
||||
viewModel.navigateToCommonCollectionDetailPage(data)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
FOLLOW_ITEM_TYPE_POST_CARD -> {
|
||||
FollowPostCardViewHolder(
|
||||
parent.toBinding(),
|
||||
pageConfiguration.entrance,
|
||||
pageConfiguration.path
|
||||
)
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_GIFT_PACK -> {
|
||||
GiftPackViewHolder(parent.toBinding(), object : GiftPackViewHolder.OnChildEventListener {
|
||||
override fun onGameClick(game: GameEntity) {
|
||||
viewModel.navigateToGameDetailPage(0, game, "礼包")
|
||||
}
|
||||
|
||||
override fun onGiftPackageClick(libaoEntity: LibaoEntity) {
|
||||
SensorsBridge.trackFollowPageContentClick("游戏礼包", libaoEntity.name ?: "")
|
||||
viewModel.navigateToLibaoDetailPage(libaoEntity)
|
||||
}
|
||||
|
||||
|
||||
override fun onCopy(code: String) {
|
||||
viewModel.copyExchangeCode(code)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_ARTICLE -> {
|
||||
GameArticleViewHolder(parent.toBinding(), object : GameArticleViewHolder.OnChildEventListener {
|
||||
override fun onGameCLick(game: GameEntity) {
|
||||
viewModel.navigateToGameDetailPage(0, game, "关注-文章")
|
||||
}
|
||||
|
||||
override fun onArticleClick(article: FollowDynamicEntity.Article) {
|
||||
SensorsBridge.trackFollowPageContentClick("文章", article.title)
|
||||
viewModel.navigateToNewsDetailPage(article.id)
|
||||
}
|
||||
|
||||
override fun onComment(article: FollowDynamicEntity.Article) {
|
||||
SensorsBridge.trackFollowPageContentClick("文章", article.title)
|
||||
viewModel.navigateToArticleCommentDetailPage(article.id)
|
||||
}
|
||||
|
||||
override fun onShare(concernEntity: ConcernEntity) {
|
||||
viewModel.onShareArticle(concernEntity)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
FOLLOW_ITEM_TYPE_FOOTER -> FollowFooterViewHolder(parent.toBinding())
|
||||
|
||||
FOLLOW_ITEM_TYPE_EMPTY -> {
|
||||
FollowHomeEmptyViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
else -> FollowInvalidViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
|
||||
is FollowHomeHeaderViewHolder -> {
|
||||
getRealTypeItem<FollowUserItem>(position) {
|
||||
holder.bind(it.isLogin, it.data)
|
||||
}
|
||||
}
|
||||
|
||||
is FollowRecommendListViewHolder -> {
|
||||
getRealTypeItem<FollowRecommendUsersItem>(position) {
|
||||
holder.bindView(it.data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is FollowHomeSlideListViewHolder -> {
|
||||
getRealTypeItem<FollowCommonCollectionItem>(position) {
|
||||
holder.bindView(it.data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is FollowHomeSlideWithCardsViewHolder -> {
|
||||
getRealTypeItem<FollowCommonCollectionItem>(position) {
|
||||
holder.bindView(it.data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is FollowNavigationViewHolder -> {
|
||||
getRealTypeItem<FollowCommonCollectionItem>(position) {
|
||||
holder.bindView(it.data)
|
||||
}
|
||||
}
|
||||
|
||||
is FollowHomeRecommendItemViewHolder -> {
|
||||
getRealTypeItem<FollowCommonCollectionItem>(position) {
|
||||
holder.bindView(it.data)
|
||||
}
|
||||
}
|
||||
|
||||
is FollowCommonCollectionViewHolder -> {
|
||||
getRealTypeItem<FollowCommonCollectionItem>(position) {
|
||||
holder.bindView(it.data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is FollowCommonCollection12ViewHolder -> {
|
||||
getRealTypeItem<FollowSplitCommonContentCollectionItem>(position) {
|
||||
holder.bind(it.data, it.leftPosition)
|
||||
}
|
||||
}
|
||||
|
||||
is FollowPostCardViewHolder -> {
|
||||
getRealTypeItem<FollowPostCardItem>(position) {
|
||||
setItemViewBackground(holder)
|
||||
holder.bind(it.data, position)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is GameArticleViewHolder -> {
|
||||
getRealTypeItem<FollowArticleItem>(position) {
|
||||
setItemViewBackground(holder)
|
||||
holder.bind(it.data)
|
||||
}
|
||||
}
|
||||
|
||||
is GiftPackViewHolder -> {
|
||||
getRealTypeItem<FollowGiftPackItem>(position) {
|
||||
setItemViewBackground(holder)
|
||||
holder.bind(it.libao, it.time)
|
||||
}
|
||||
}
|
||||
|
||||
is FollowFooterViewHolder -> {
|
||||
holder.initFooterViewHolder(
|
||||
loadStatus == LoadStatus.LIST_LOADING,
|
||||
loadStatus == LoadStatus.LIST_FAILED,
|
||||
loadStatus == LoadStatus.LIST_OVER,
|
||||
R.string.load_over_with_click_hint
|
||||
) {
|
||||
if (loadStatus == LoadStatus.LIST_OVER) {
|
||||
_recyclerView?.scrollToPosition(0)
|
||||
} else if (loadStatus == LoadStatus.LIST_FAILED) {
|
||||
viewModel.loadMore()
|
||||
notifyItemChanged(itemCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
_recyclerView = recyclerView
|
||||
recyclerView.addOnScrollListener(onLoadMoreListener)
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
recyclerView.removeOnScrollListener(onLoadMoreListener)
|
||||
_recyclerView = null
|
||||
}
|
||||
|
||||
override fun onViewAttachedToWindow(holder: ViewHolder) {
|
||||
if (holder is OnViewHolderAttachListener) {
|
||||
holder.onViewAttach(_recyclerView)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(holder: ViewHolder) {
|
||||
if (holder is OnViewHolderAttachListener) {
|
||||
holder.onViewDetach(_recyclerView)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getSyncData(position: Int): Pair<String, Any>? {
|
||||
if (position >= itemCount) return null
|
||||
val item = getItem(position)
|
||||
return when {
|
||||
item is FollowPostCardItem -> {
|
||||
Pair(item.data.id, item.data.count)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> getRealTypeItem(position: Int, block: (T) -> Unit) {
|
||||
val item = getItem(position)
|
||||
(item as? T)?.let(block)
|
||||
}
|
||||
|
||||
fun notifyFollowUserChanged(change: EBUserFollow) {
|
||||
val position = currentList.indexOfFirst { it is FollowRecommendUsersItem }
|
||||
notifyItemChanged(position, change)
|
||||
}
|
||||
|
||||
fun notifyFollowUserReadStatus(ids: List<String>) {
|
||||
val position = currentList.indexOfFirst { it is FollowUserItem }
|
||||
notifyItemChanged(position, ReadStatusUpdate(ids))
|
||||
}
|
||||
|
||||
private val onLoadMoreListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
val layoutManger = recyclerView.layoutManager
|
||||
if (layoutManger is LinearLayoutManager) {
|
||||
val lastVisibleItemPosition = layoutManger.findLastVisibleItemPosition()
|
||||
if (lastVisibleItemPosition == itemCount - 1 && loadStatus != LoadStatus.LIST_OVER && loadStatus != LoadStatus.LIST_LOADING) {
|
||||
viewModel.loadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun createItemCallback() = object : ItemCallback<FollowItem>() {
|
||||
override fun areItemsTheSame(oldItem: FollowItem, newItem: FollowItem): Boolean {
|
||||
return oldItem.areItemsTheSame(newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: FollowItem, newItem: FollowItem): Boolean {
|
||||
return oldItem.areContentsTheSame(newItem)
|
||||
}
|
||||
}
|
||||
|
||||
fun setItemViewBackground(viewHolder: ViewHolder) {
|
||||
if (viewHolder.bindingAdapterPosition == 1) {
|
||||
viewHolder.itemView.setBackgroundResource(R.drawable.background_shape_white_radius_12_top_only)
|
||||
} else {
|
||||
viewHolder.itemView.setBackgroundColor(R.color.ui_surface.toColor(viewHolder.itemView.context))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ReadStatusUpdate(
|
||||
val ids: List<String>
|
||||
)
|
||||
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.debounceActionWithInterval
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.databinding.RecyclerFollowRecommendBinding
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.feature.entity.UserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.ResetDataChangeHelper
|
||||
|
||||
class FollowRecommendListAdapter(
|
||||
context: Context,
|
||||
private val listener: OnChildEventListener
|
||||
) : RecyclerView.Adapter<FollowRecommendListAdapter.RecommendViewHolder>() {
|
||||
|
||||
private val resetDataChangeHelper = ResetDataChangeHelper<UserEntity>(context, this)
|
||||
|
||||
fun submitList(data: List<UserEntity>) {
|
||||
resetDataChangeHelper.submitList(data) {
|
||||
it.id ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecommendViewHolder {
|
||||
return RecommendViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int =
|
||||
resetDataChangeHelper.dataList.size
|
||||
|
||||
override fun onBindViewHolder(holder: RecommendViewHolder, position: Int) {
|
||||
val item = resetDataChangeHelper.dataList[position]
|
||||
val context = holder.itemView.context
|
||||
|
||||
holder.binding.ivIcon.displayGameIcon(item.icon, null)
|
||||
|
||||
holder.binding.tvFollowerCount.text = if (item.count.fans > NUMBER_MAX) {
|
||||
context.getString(R.string.fans_with_number_max)
|
||||
} else {
|
||||
context.getString(R.string.fans_with_number, item.count.fans)
|
||||
}
|
||||
|
||||
val createCount = item.count.question + item.count.communityArticle + item.count.video
|
||||
holder.binding.tvArticleCount.text = if (createCount > NUMBER_MAX) {
|
||||
context.getString(R.string.creations_with_number_max)
|
||||
} else {
|
||||
context.getString(R.string.creations_with_number, createCount)
|
||||
}
|
||||
holder.binding.tvSign.text = if (item.introduce.isBlank()) {
|
||||
context.getString(R.string.user_default_introduce)
|
||||
} else {
|
||||
item.introduce
|
||||
}
|
||||
|
||||
holder.binding.tvFollow.text = if (item.isFollowed) {
|
||||
context.getString(R.string.concerned)
|
||||
} else {
|
||||
context.getString(R.string.menu_concern)
|
||||
}
|
||||
|
||||
holder.itemView.background = R.drawable.bg_shape_recycler_follow_recommend.toDrawable(context)
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
listener.onItemClick(position, item.id ?: "")
|
||||
}
|
||||
|
||||
holder.binding.tvFollow.setOnClickListener {
|
||||
debounceActionWithInterval(it.id, 1000) {
|
||||
listener.onFollowClick(position, item.id ?: "", !item.isFollowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFollowed(change: EBUserFollow) {
|
||||
val position = resetDataChangeHelper.dataList.indexOfFirst { it.id == change.userId }
|
||||
if (position != -1) {
|
||||
val item = resetDataChangeHelper.dataList[position]
|
||||
item.isFollowed = change.isFollow
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val NUMBER_MAX = 99
|
||||
}
|
||||
|
||||
class RecommendViewHolder(val binding: RecyclerFollowRecommendBinding) : ViewHolder(binding.root)
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun onItemClick(childPosition: Int, userId: String)
|
||||
|
||||
fun onFollowClick(childPosition: Int, userId: String, isFollow: Boolean)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,214 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.RecyclerFollowedListAllBinding
|
||||
import com.gh.gamecenter.databinding.RecyclerFollowedListBbsBinding
|
||||
import com.gh.gamecenter.databinding.RecyclerFollowedListPersonBinding
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.ResetDataChangeHelper
|
||||
import com.gh.gamecenter.forum.home.follow.model.FollowRepository.Companion.FOLLOW_UPDATE_TYPE_USER
|
||||
|
||||
class FollowedHeaderAdapter(
|
||||
private val context: Context,
|
||||
private val listener: OnChildEventListener
|
||||
) :
|
||||
RecyclerView.Adapter<ViewHolder>() {
|
||||
|
||||
/**
|
||||
* 1.当 _selectedPosition 为 -1 时
|
||||
* 不显示 viewAll 按钮
|
||||
* 显示选中框
|
||||
*
|
||||
* 2. 当_selectedPosition 为 -2 时
|
||||
* 不显示 viewAll 按钮
|
||||
* 暂无选中框
|
||||
*/
|
||||
private var _selectedPosition = -1
|
||||
|
||||
private val showAll: Boolean
|
||||
get() = _selectedPosition == -1
|
||||
|
||||
private val resetDataChangeHelper = ResetDataChangeHelper<FollowUserEntity>(context, this)
|
||||
|
||||
val dataList: List<FollowUserEntity>
|
||||
get() = resetDataChangeHelper.dataList
|
||||
|
||||
fun submitList(data: List<FollowUserEntity>, selectedPosition: Int = -1) {
|
||||
_selectedPosition = selectedPosition
|
||||
resetDataChangeHelper.submitList(data) {
|
||||
"${it.id}-${it.type}-${it.isShowTip}"
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int =
|
||||
if (showAll) resetDataChangeHelper.dataCount + 1 else resetDataChangeHelper.dataCount
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (showAll) {
|
||||
if (position < resetDataChangeHelper.dataCount) {
|
||||
if (resetDataChangeHelper.getItem(position).type == FOLLOW_UPDATE_TYPE_USER) {
|
||||
VIEW_TYPE_BBS
|
||||
} else {
|
||||
VIEW_TYPE_GAME
|
||||
}
|
||||
} else {
|
||||
VIEW_TYPE_VIEW_ALL
|
||||
}
|
||||
} else {
|
||||
if (resetDataChangeHelper.getItem(position).type == FOLLOW_UPDATE_TYPE_USER) {
|
||||
VIEW_TYPE_BBS
|
||||
} else {
|
||||
VIEW_TYPE_GAME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
|
||||
when (viewType) {
|
||||
VIEW_TYPE_GAME -> {
|
||||
FollowedGameViewHolder(parent.toBinding(), ::onSelectedChanged)
|
||||
}
|
||||
|
||||
VIEW_TYPE_BBS -> {
|
||||
FollowedPersonViewHolder(parent.toBinding(), ::onSelectedChanged)
|
||||
}
|
||||
|
||||
else -> FollowedViewAllViewHolder(parent.toBinding(), listener::onViewAll)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is FollowedGameViewHolder -> {
|
||||
|
||||
holder.bindView(
|
||||
resetDataChangeHelper.getItem(position),
|
||||
position,
|
||||
_selectedPosition == position,
|
||||
showAll
|
||||
)
|
||||
}
|
||||
|
||||
is FollowedPersonViewHolder -> {
|
||||
holder.bindView(
|
||||
resetDataChangeHelper.getItem(position),
|
||||
position,
|
||||
_selectedPosition == position,
|
||||
showAll
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onSelectedChanged(position: Int) {
|
||||
selectPosition(position)
|
||||
listener.onViewItem(position)
|
||||
}
|
||||
|
||||
fun selectPosition(position: Int) {
|
||||
if (_selectedPosition != -1 && _selectedPosition != position) {
|
||||
val lastSelectedPosition = _selectedPosition
|
||||
_selectedPosition = position
|
||||
notifyItemChanged(lastSelectedPosition)
|
||||
notifyItemChanged(_selectedPosition)
|
||||
|
||||
_selectedPosition = position
|
||||
}
|
||||
}
|
||||
|
||||
fun clearSelectedPosition() {
|
||||
val lastSelectedPosition = _selectedPosition
|
||||
_selectedPosition = -2
|
||||
if (lastSelectedPosition >= 0) {
|
||||
notifyItemChanged(lastSelectedPosition)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val VIEW_TYPE_GAME = 0
|
||||
private const val VIEW_TYPE_BBS = 1
|
||||
private const val VIEW_TYPE_VIEW_ALL = 2
|
||||
}
|
||||
|
||||
private class FollowedGameViewHolder(
|
||||
val binding: RecyclerFollowedListBbsBinding,
|
||||
private val itemClick: (Int) -> Unit,
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
fun bindView(item: FollowUserEntity, position: Int, isSelected: Boolean, showName: Boolean) {
|
||||
if (showName) {
|
||||
binding.tvName.goneIf(false)
|
||||
binding.tvName.text = item.bbs.name
|
||||
} else {
|
||||
binding.tvName.goneIf(true)
|
||||
}
|
||||
binding.tvName.setTextColor(R.color.text_secondary.toColor(itemView.context))
|
||||
binding.ivIcon.displayGameIcon(item.icon, null)
|
||||
val resourceId = if (isSelected) {
|
||||
R.drawable.background_shape_theme_radius_14
|
||||
} else {
|
||||
R.drawable.background_shape_white_radius_14
|
||||
}
|
||||
|
||||
binding.gIndicator.goneIf(!item.isShowTip)
|
||||
|
||||
binding.vIconBackground.background = resourceId.toDrawable(itemView.context)
|
||||
|
||||
itemView.setOnClickListener {
|
||||
itemClick(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FollowedPersonViewHolder(
|
||||
val binding: RecyclerFollowedListPersonBinding,
|
||||
private val itemClick: (Int) -> Unit,
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
fun bindView(item: FollowUserEntity, position: Int, isSelected: Boolean, showName: Boolean) {
|
||||
if (showName) {
|
||||
binding.tvName.goneIf(false)
|
||||
binding.tvName.text = item.user.name
|
||||
} else {
|
||||
binding.tvName.goneIf(true)
|
||||
}
|
||||
binding.tvName.setTextColor(R.color.text_secondary.toColor(itemView.context))
|
||||
binding.ivIcon.displayGameIcon(item.icon, null)
|
||||
val resourceId = if (isSelected) {
|
||||
R.drawable.background_shape_theme_radius_999
|
||||
} else {
|
||||
R.drawable.background_shape_white_radius_999
|
||||
}
|
||||
binding.gIndicator.goneIf(!item.isShowTip)
|
||||
binding.vIconBackground.background = resourceId.toDrawable(itemView.context)
|
||||
|
||||
itemView.setOnClickListener {
|
||||
itemClick(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FollowedViewAllViewHolder(
|
||||
binding: RecyclerFollowedListAllBinding,
|
||||
private val viewAll: () -> Unit,
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
init {
|
||||
binding.root.setOnClickListener {
|
||||
viewAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun onViewItem(position: Int)
|
||||
|
||||
fun onViewAll()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.fragment.FollowDynamicListFragment
|
||||
|
||||
class FollowedViewPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
|
||||
private val dataList = arrayListOf<FollowUserEntity>()
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun addData(newData: List<FollowUserEntity>) {
|
||||
if (dataList.isEmpty()) {
|
||||
dataList.addAll(newData)
|
||||
notifyDataSetChanged()
|
||||
} else {
|
||||
val start = dataList.size
|
||||
dataList.addAll(newData)
|
||||
notifyItemRangeChanged(start, newData.size)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = dataList.size
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
val item = dataList[position]
|
||||
return FollowDynamicListFragment.newInstance(item.user.id ?: "", item.bbs.id,item.bbs.type)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.gh.gamecenter.forum.home.follow.eventlistener
|
||||
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.ConcernEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.LibaoEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
|
||||
interface OnFollowHomeEventListener {
|
||||
|
||||
fun viewItemFollowed(position: Int, data: List<FollowUserEntity>)
|
||||
|
||||
fun viewAllFollowed()
|
||||
|
||||
fun login()
|
||||
|
||||
fun followUser(userId: String, isFollow: Boolean)
|
||||
fun navigateToLinkPageInCommonContent(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
)
|
||||
|
||||
fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String)
|
||||
|
||||
fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection)
|
||||
|
||||
fun navigateToUserHomePage(userId: String)
|
||||
|
||||
fun onPostCardClick(position: Int, article: ArticleEntity)
|
||||
|
||||
fun navigateToLibaoDetailPage(libaoEntity: LibaoEntity)
|
||||
|
||||
fun navigateToNewsDetailPage(articleId: String)
|
||||
|
||||
fun navigateToArticleCommentDetailPage(articleId: String)
|
||||
|
||||
fun onShareArticle(concernEntity: ConcernEntity)
|
||||
|
||||
fun copyExchangeCode(code: String)
|
||||
|
||||
}
|
||||
@ -0,0 +1,283 @@
|
||||
package com.gh.gamecenter.forum.home.follow.fragment
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.LazyFragment
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.FragmentAllFollowedBinding
|
||||
import com.gh.gamecenter.forum.detail.ForumDetailActivity
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedNoTopItem
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedNormalItem
|
||||
import com.gh.gamecenter.forum.home.follow.adapter.AllFollowedAdapter
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.AllFollowedViewModel
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import com.lightgame.listeners.OnBackPressedListener
|
||||
import com.lightgame.utils.toast.ToastHelper
|
||||
import splitties.resources.color
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class AllFollowedFragment : LazyFragment(), OnBackPressedListener {
|
||||
|
||||
private lateinit var binding: FragmentAllFollowedBinding
|
||||
|
||||
private val viewModel by viewModels<AllFollowedViewModel>()
|
||||
|
||||
private val itemTouchHelper = ItemTouchHelper(MyItemTouchCallback())
|
||||
|
||||
private var originalTopList = listOf<AllFollowedNormalItem>()
|
||||
|
||||
private val paint by lazy {
|
||||
Paint().apply {
|
||||
color = R.color.text_primary.toColor(requireContext())
|
||||
textSize = 14F.dip2px().toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private val overPaint by lazy {
|
||||
Paint().apply {
|
||||
color = R.color.ui_surface.toColor(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
private val adapter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
AllFollowedAdapter(viewModel) {
|
||||
startDrag(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRealLayoutId(): Int = R.layout.fragment_all_followed
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
binding = FragmentAllFollowedBinding.bind(inflatedView)
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
binding.rvFollowed.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.rvFollowed.adapter = adapter
|
||||
binding.rvFollowed.addItemDecoration(MyItemDecoration(requireContext(), paint, overPaint))
|
||||
itemTouchHelper.attachToRecyclerView(binding.rvFollowed)
|
||||
|
||||
with(viewModel) {
|
||||
dataList.observe(viewLifecycleOwner) {
|
||||
originalTopList = it.filter(AllFollowedNormalItem::isTop)
|
||||
adapter.setData(it)
|
||||
}
|
||||
|
||||
operateTopSuccessfully.observe(viewLifecycleOwner, EventObserver {
|
||||
// 提交成功
|
||||
ToastHelper.showToast(requireContext(), getString(R.string.save_successfully))
|
||||
finish(true)
|
||||
})
|
||||
|
||||
detailDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
if (it.isUser) {
|
||||
DirectUtils.directToHomeActivity(requireContext(), it.user.id)
|
||||
} else {
|
||||
startActivity(ForumDetailActivity.getIntent(requireContext(), it.bbs.id, ""))
|
||||
}
|
||||
})
|
||||
|
||||
loadFirst()
|
||||
}
|
||||
|
||||
|
||||
|
||||
binding.tvRight.setOnClickListener {
|
||||
if (adapter.isEdit && isDataChanged()) {
|
||||
viewModel.operateTop(adapter.getTopItem())
|
||||
} else {
|
||||
adapter.changeToEditable()
|
||||
binding.tvRight.text = if (adapter.isEdit) "保存" else "完成"
|
||||
val textResId = if (adapter.isEdit) R.string.finish else R.string.manager
|
||||
binding.tvRight.setText(textResId)
|
||||
}
|
||||
}
|
||||
binding.ivBack.setOnClickListener {
|
||||
if (adapter.isEdit && isDataChanged()) {
|
||||
showSaveTipsDialog()
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun showSaveTipsDialog() {
|
||||
DialogHelper.showDialog(
|
||||
requireContext(),
|
||||
getString(R.string.archive_dialog_title),
|
||||
getString(R.string.whether_to_save_changes),
|
||||
cancelText = getString(R.string.dialog_nickname_cancel),
|
||||
confirmText = getString(R.string.servers_subscribed_game_unsubscribe_dialog_confirm),
|
||||
confirmClickCallback = {
|
||||
viewModel.operateTop(adapter.getTopItem())
|
||||
},
|
||||
cancelClickCallback = {
|
||||
finish()
|
||||
},
|
||||
extraConfig = DialogHelper.Config(showCloseIcon = false, centerTitle = true, centerContent = true)
|
||||
)
|
||||
}
|
||||
|
||||
private fun isDataChanged(): Boolean = originalTopList != adapter.dataList.filter(AllFollowedNormalItem::isTop)
|
||||
|
||||
private fun startDrag(viewHolder: RecyclerView.ViewHolder) {
|
||||
itemTouchHelper.startDrag(viewHolder)
|
||||
}
|
||||
|
||||
override fun onHandleBackPressed(): Boolean {
|
||||
if (adapter.isEdit && isDataChanged()) {
|
||||
showSaveTipsDialog()
|
||||
return true
|
||||
}
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun finish(isSorted: Boolean = false) {
|
||||
if (viewModel.hasReadSetIds.isNotEmpty() || isSorted) {
|
||||
val intent = Intent()
|
||||
// 如果顺序发生变化,则直接通知外部重新加载列表,无需局部更新阅读状态
|
||||
if (!isSorted) {
|
||||
intent.putStringArrayListExtra(KEY_HAS_READ_ID, viewModel.hasReadSetIds.toList().toArrayList())
|
||||
}
|
||||
activity?.setResult(Activity.RESULT_OK, intent)
|
||||
}
|
||||
activity?.finish()
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
|
||||
paint.color = R.color.text_primary.toColor(requireContext())
|
||||
overPaint.color = R.color.ui_surface.toColor(requireContext())
|
||||
|
||||
binding.rvFollowed.tryToClearRecycler()
|
||||
binding.rvFollowed.recycledViewPool.clear()
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_HAS_READ_ID = "key_has_read_id"
|
||||
}
|
||||
|
||||
private class MyItemDecoration(
|
||||
private val context: Context,
|
||||
private val paint: Paint,
|
||||
private val overPaint: Paint
|
||||
) : ItemDecoration() {
|
||||
|
||||
private val headerHeight by lazy(LazyThreadSafetyMode.NONE) {
|
||||
HEADER_HEIGHT.dip2px()
|
||||
}
|
||||
|
||||
private val screenWidth by lazy(LazyThreadSafetyMode.NONE) {
|
||||
context.resources.displayMetrics.widthPixels
|
||||
}
|
||||
|
||||
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
// 是否需要绘制 置顶 悬浮头部
|
||||
val adapter = parent.adapter
|
||||
if (adapter is AllFollowedAdapter) {
|
||||
// 寻找最后一个 置顶 的itemView
|
||||
val lastTopView = parent.children.lastOrNull {
|
||||
val position = parent.getChildAdapterPosition(it)
|
||||
val item = if (position != -1) {
|
||||
adapter.getItem(position)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
(item is AllFollowedNormalItem && item.isTop) || item is AllFollowedNoTopItem
|
||||
}
|
||||
if (lastTopView != null) {
|
||||
// 绘制 置顶 悬浮条
|
||||
val bottom = if (lastTopView.bottom >= headerHeight) {
|
||||
headerHeight
|
||||
} else {
|
||||
lastTopView.bottom
|
||||
}
|
||||
c.drawRect(
|
||||
Rect(0, 0, screenWidth, bottom),
|
||||
overPaint
|
||||
)
|
||||
val text = "置顶(${adapter.topItemCount}/${adapter.followedCount})"
|
||||
drawFollowedHeader(text, c, bottom)
|
||||
} else {
|
||||
// 绘制 “我的关注” 悬浮条
|
||||
c.drawRect(
|
||||
Rect(0, 0, screenWidth, headerHeight),
|
||||
overPaint
|
||||
)
|
||||
drawFollowedHeader("我的关注", c, headerHeight)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun drawFollowedHeader(text: String, c: Canvas, bottom: Int) {
|
||||
val fontMetrics = paint.fontMetrics
|
||||
val centerYDistanceToBaseline =
|
||||
(abs(fontMetrics.descent) + abs(fontMetrics.ascent) + abs(fontMetrics.leading)) / 2 - abs(
|
||||
fontMetrics.descent
|
||||
)
|
||||
val baselineY = bottom - 25F.dip2px().toFloat() + centerYDistanceToBaseline
|
||||
c.drawText(text, 0, text.length, 16F.dip2px().toFloat(), baselineY, paint)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val HEADER_HEIGHT = 50F
|
||||
}
|
||||
}
|
||||
|
||||
private class MyItemTouchCallback : ItemTouchHelper.Callback() {
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
val adapter = recyclerView.adapter as? AllFollowedAdapter
|
||||
if (adapter != null) {
|
||||
val position = viewHolder.bindingAdapterPosition
|
||||
val item = adapter.getItem(position)
|
||||
if (item is AllFollowedNormalItem && item.isTop) {
|
||||
val targetPosition = target.bindingAdapterPosition
|
||||
val targetItem = adapter.getItem(targetPosition)
|
||||
if (targetItem is AllFollowedNormalItem && targetItem.isTop) {
|
||||
val newList = ArrayList(adapter.dataList)
|
||||
val dataPosition = newList.indexOfFirst { it.data.id == item.data.id }
|
||||
val targetDataPosition = newList.indexOfFirst { it.data.id == targetItem.data.id }
|
||||
Collections.swap(newList, dataPosition, targetDataPosition)
|
||||
adapter.setData(newList)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isLongPressDragEnabled(): Boolean = false
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,271 @@
|
||||
package com.gh.gamecenter.forum.home.follow.fragment
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.PointF
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.animation.Interpolator
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||
import androidx.recyclerview.widget.OrientationHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
|
||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.LazyFragment
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.visibleIf
|
||||
import com.gh.gamecenter.databinding.FragmentFollowDynamicBinding
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicActivity.Companion.KEY_SELECTED_POSITION
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicActivity.Companion.KEY_USERS
|
||||
import com.gh.gamecenter.forum.home.follow.adapter.FollowedHeaderAdapter
|
||||
import com.gh.gamecenter.forum.home.follow.adapter.FollowedViewPagerAdapter
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowDynamicViewModel
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
class FollowDynamicFragment : LazyFragment() {
|
||||
|
||||
private lateinit var binding: FragmentFollowDynamicBinding
|
||||
|
||||
private val viewModel by activityViewModels<FollowDynamicViewModel>()
|
||||
|
||||
private var selectedPosition = 0
|
||||
|
||||
private val screenCenterX by lazy(LazyThreadSafetyMode.NONE) {
|
||||
resources.displayMetrics.widthPixels / 2
|
||||
}
|
||||
|
||||
private val offsetX by lazy(LazyThreadSafetyMode.NONE) {
|
||||
screenCenterX - 32F.dip2px()
|
||||
}
|
||||
|
||||
private val layoutManager by lazy(LazyThreadSafetyMode.NONE) {
|
||||
LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
private val helper by lazy {
|
||||
OrientationHelper.createHorizontalHelper(layoutManager)
|
||||
}
|
||||
|
||||
private val followedAdapter by lazy {
|
||||
FollowedHeaderAdapter(requireContext(), object : FollowedHeaderAdapter.OnChildEventListener {
|
||||
override fun onViewItem(position: Int) {
|
||||
selectedPosition = position
|
||||
binding.viewPager.setCurrentItem(position, false)
|
||||
alignIndicator()
|
||||
}
|
||||
|
||||
override fun onViewAll() = Unit
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private val dynamicListVpAdapter by lazy {
|
||||
FollowedViewPagerAdapter(this)
|
||||
}
|
||||
|
||||
override fun getRealLayoutId(): Int = R.layout.fragment_follow_dynamic
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
binding = FragmentFollowDynamicBinding.bind(inflatedView)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val list = arguments?.getParcelableArrayList<FollowUserEntity>(KEY_USERS)
|
||||
if (list != null) {
|
||||
viewModel.addFirstPageUsers(list)
|
||||
}
|
||||
selectedPosition = arguments?.getInt(KEY_SELECTED_POSITION, 0) ?: 0
|
||||
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
|
||||
with(viewModel) {
|
||||
followedUsers.observe(viewLifecycleOwner, Observer { (isFirst, data) ->
|
||||
followedAdapter.submitList(data, selectedPosition)
|
||||
if (isFirst) {
|
||||
scrollSelectedPositionToCenter(false)
|
||||
startAnimation(true)
|
||||
}
|
||||
dynamicListVpAdapter.addData(data)
|
||||
})
|
||||
}
|
||||
|
||||
binding.rvFollowedHeader.layoutManager = layoutManager
|
||||
binding.rvFollowedHeader.adapter = followedAdapter
|
||||
|
||||
binding.viewPager.adapter = dynamicListVpAdapter
|
||||
binding.viewPager.offscreenPageLimit = 1
|
||||
binding.viewPager.setCurrentItem(selectedPosition, false)
|
||||
binding.viewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) = Unit
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
super.onPageScrollStateChanged(state)
|
||||
if (state != 0) {
|
||||
binding.ivIndicator.visibleIf(false)
|
||||
followedAdapter.clearSelectedPosition()
|
||||
} else {
|
||||
binding.ivIndicator.visibleIf(true)
|
||||
followedAdapter.selectPosition(selectedPosition)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
if (selectedPosition != position) {
|
||||
selectedPosition = position
|
||||
followedAdapter.selectPosition(position)
|
||||
scrollSelectedPositionToCenter(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
with(viewModel) {
|
||||
finishAction.observe(viewLifecycleOwner, EventObserver {
|
||||
startAnimation(false)
|
||||
requireActivity().finish()
|
||||
requireActivity().overridePendingTransition(0, R.anim.no_anim)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollSelectedPositionToCenter(isSmoothScroll: Boolean) {
|
||||
binding.rvFollowedHeader.clearOnScrollListeners()
|
||||
if (isSmoothScroll) {
|
||||
// 1. 如果当前 itemView 在屏幕内,则只需要直接将其滚动到中间即可
|
||||
if (!checkTargetViewIfExist()) {
|
||||
// 2. 如果当前itemView不在屏幕内,则需要先将 itemView 滚动到屏幕内,在将其滚到屏幕中间
|
||||
binding.rvFollowedHeader.addOnScrollListener(object : OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
binding.rvFollowedHeader.clearOnScrollListeners()
|
||||
checkTargetViewIfExist()
|
||||
}
|
||||
}
|
||||
})
|
||||
val vector = layoutManager.computeScrollVectorForPosition(selectedPosition) ?: PointF()
|
||||
val linearSmoothScroller = object : LinearSmoothScroller(requireContext()) {
|
||||
override fun getHorizontalSnapPreference(): Int {
|
||||
super.getHorizontalSnapPreference()
|
||||
return if (vector.x > 0) SNAP_TO_END else SNAP_TO_START
|
||||
}
|
||||
}
|
||||
linearSmoothScroller.targetPosition = selectedPosition
|
||||
layoutManager.startSmoothScroll(linearSmoothScroller)
|
||||
}
|
||||
|
||||
} else {
|
||||
layoutManager.scrollToPositionWithOffset(selectedPosition, offsetX)
|
||||
}
|
||||
|
||||
binding.rvFollowedHeader.addOnScrollListener(object : OnScrollListener() {
|
||||
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
alignIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
alignIndicator()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun alignIndicator() {
|
||||
val targetView = layoutManager.findViewByPosition(selectedPosition)
|
||||
if (targetView != null) {
|
||||
binding.ivIndicator.translationX = distanceToStart(targetView).toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTargetViewIfExist(): Boolean {
|
||||
val targetView = layoutManager.findViewByPosition(selectedPosition)
|
||||
if (targetView != null) {
|
||||
val distanceToCenter = distanceToCenter(targetView)
|
||||
if (distanceToCenter != 0) {
|
||||
binding.rvFollowedHeader.smoothScrollBy(distanceToCenter, 0)
|
||||
}
|
||||
}
|
||||
return targetView != null
|
||||
}
|
||||
|
||||
private val easeOutInterpolator = Interpolator { input -> 1 - (1 - input).pow(2) }
|
||||
|
||||
private fun startAnimation(isStart: Boolean) {
|
||||
val values = if (isStart) {
|
||||
floatArrayOf(ANIMATION_START_VALUE, ANIMATION_END_VALUE)
|
||||
} else {
|
||||
floatArrayOf(ANIMATION_END_VALUE, ANIMATION_START_VALUE)
|
||||
}
|
||||
val valueAnimator = ValueAnimator.ofFloat(*values)
|
||||
|
||||
val followHeaderTranslationY =
|
||||
requireContext().resources.getDimensionPixelSize(R.dimen.follow_detail_header_translation_y)
|
||||
val dynamicListTranslationY =
|
||||
requireContext().resources.getDimensionPixelSize(R.dimen.follow_detail_content_translation_y)
|
||||
|
||||
with(valueAnimator) {
|
||||
duration = ANIMATION_DURATION
|
||||
interpolator = easeOutInterpolator
|
||||
addUpdateListener {
|
||||
val value = it.animatedValue as Float
|
||||
val alphaEnd = 5F / 8F
|
||||
val alpha = if (value >= 0 && value <= alphaEnd) {
|
||||
value / alphaEnd
|
||||
} else {
|
||||
1F
|
||||
}
|
||||
binding.ivTopBackground.alpha = alpha
|
||||
binding.rvFollowedHeader.alpha = alpha
|
||||
binding.viewPager.alpha = alpha
|
||||
|
||||
if (value >= 0.5F) {
|
||||
binding.ivIndicator.alpha = (value - 0.5F) * 2
|
||||
} else {
|
||||
binding.ivIndicator.alpha = 0F
|
||||
}
|
||||
|
||||
binding.ivTopBackground.translationY = followHeaderTranslationY * (1 - value)
|
||||
binding.rvFollowedHeader.translationY = followHeaderTranslationY * (1 - value)
|
||||
binding.viewPager.translationY = dynamicListTranslationY * (1 - value)
|
||||
if (isStart && value == 1F) {
|
||||
viewModel.startLoad()
|
||||
}
|
||||
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun distanceToCenter(targetView: View): Int {
|
||||
val childCenter = helper.getDecoratedStart(targetView) + helper.getDecoratedMeasurement(targetView) / 2
|
||||
val containerCenter = helper.startAfterPadding + helper.totalSpace / 2
|
||||
return childCenter - containerCenter
|
||||
}
|
||||
|
||||
private fun distanceToStart(targetView: View): Int = distanceToCenter(targetView) + screenCenterX
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
followedAdapter.notifyItemRangeChanged(0, followedAdapter.itemCount, "")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ANIMATION_DURATION = 400L
|
||||
private const val ANIMATION_START_VALUE = 0F
|
||||
private const val ANIMATION_END_VALUE = 1F
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,318 @@
|
||||
package com.gh.gamecenter.forum.home.follow.fragment
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.BaseFragment
|
||||
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.FragmentFollowDynamicListBinding
|
||||
import com.gh.gamecenter.feature.entity.CommentEntity
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.forum.detail.ForumDetailActivity
|
||||
import com.gh.gamecenter.forum.home.follow.FollowActivityResultLauncher
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicBbsItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicPersonalItem
|
||||
import com.gh.gamecenter.forum.home.follow.adapter.FollowDynamicAdapter
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowDynamicListViewModel
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowDynamicViewModel
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import com.gh.gamecenter.qa.BbsType
|
||||
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
|
||||
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
|
||||
class FollowDynamicListFragment : BaseFragment<Unit>() {
|
||||
|
||||
private var userId: String = ""
|
||||
private var bbsId: String = ""
|
||||
private var bbsType: String = ""
|
||||
|
||||
private val paint by lazy {
|
||||
Paint().apply {
|
||||
color = R.color.ui_divider.toColor(requireContext())
|
||||
strokeWidth = 0.5F.dip2px().toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private val viewModel by viewModels<FollowDynamicListViewModel>()
|
||||
|
||||
private val followDynamicViewModel by activityViewModels<FollowDynamicViewModel>()
|
||||
|
||||
private val binding by lazy {
|
||||
FragmentFollowDynamicListBinding.inflate(layoutInflater, null, false)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
FollowDynamicAdapter(requireContext(), viewModel)
|
||||
}
|
||||
|
||||
private lateinit var launcher: FollowActivityResultLauncher
|
||||
|
||||
override fun getInflatedLayout(): View {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fragment_follow_dynamic_list
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
userId = arguments?.getString(KEY_USER_ID) ?: ""
|
||||
bbsId = arguments?.getString(KEY_BBS_ID) ?: ""
|
||||
bbsType = arguments?.getString(KEY_BBS_TYPE) ?: ""
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
initView()
|
||||
|
||||
with(viewModel) {
|
||||
loadStatus.observe(viewLifecycleOwner) { (loadStatus, isPullRefresh) ->
|
||||
if (isPullRefresh) {
|
||||
if (loadStatus != LoadStatus.INIT_LOADING) {
|
||||
binding.srlRefresh.isRefreshing = false
|
||||
}
|
||||
} else {
|
||||
binding.reuseLoading.root.goneIf(loadStatus != LoadStatus.INIT_LOADING)
|
||||
binding.reuseNoData.root.goneIf(loadStatus != LoadStatus.INIT_EMPTY)
|
||||
binding.reuseNoConnection.root.goneIf(loadStatus != LoadStatus.LIST_FAILED)
|
||||
binding.srlRefresh.isEnabled =
|
||||
!(loadStatus == LoadStatus.INIT_FAILED || loadStatus == LoadStatus.INIT_LOADING)
|
||||
}
|
||||
adapter.setLoadStatus(loadStatus)
|
||||
|
||||
}
|
||||
|
||||
dataList.observe(viewLifecycleOwner) {
|
||||
adapter.submitList(it)
|
||||
}
|
||||
|
||||
personDetailDestination.observe(viewLifecycleOwner, EventObserver { (personalEntity, position) ->
|
||||
launcher.onPersonItemClick(position, personalEntity)
|
||||
})
|
||||
|
||||
bbsDetailDestination.observe(viewLifecycleOwner, EventObserver { (position, answer) ->
|
||||
launcher.onBbsItemClick(position, answer)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
followDynamicViewModel.isStartLoadAction.observe(viewLifecycleOwner, Observer {
|
||||
if (it) {
|
||||
viewModel.loadFirst(userId, bbsId, false)
|
||||
}
|
||||
})
|
||||
|
||||
binding.tvTitle.text = if (userId.isNotBlank()) {
|
||||
getString(R.string.follow_detail_person_updates)
|
||||
} else {
|
||||
if (bbsType == BbsType.OFFICIAL_BBS.value) {
|
||||
getString(R.string.follow_detail_bbs_updates)
|
||||
} else {
|
||||
getString(R.string.follow_detail_game_updates)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
binding.tvRight.text =
|
||||
if (userId.isNotBlank()) {
|
||||
getString(R.string.user_home_page)
|
||||
} else {
|
||||
getString(R.string.go_to_forum)
|
||||
}
|
||||
|
||||
binding.tvRight.setOnClickListener {
|
||||
if (userId.isNotBlank()) {
|
||||
DirectUtils.directToHomeActivity(requireContext(), userId)
|
||||
} else {
|
||||
startActivity(ForumDetailActivity.getIntent(requireContext(), bbsId, ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
launcher = FollowActivityResultLauncher(requireActivity().activityResultRegistry,
|
||||
object : FollowActivityResultLauncher.OnResultListener {
|
||||
override fun onArticleDetailResult(position: Int, articleDetailEntity: ArticleDetailEntity) {
|
||||
val item = adapter.currentList[position]
|
||||
if (item is FollowDynamicPersonalItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.vote = articleDetailEntity.count.vote
|
||||
newCount.comment = articleDetailEntity.count.comment
|
||||
count = newCount
|
||||
title = articleDetailEntity.title
|
||||
brief = articleDetailEntity.content.removeVideoContent().removeInsertedContent()
|
||||
.clearHtmlFormatCompletely()
|
||||
me.isCommunityArticleVote = articleDetailEntity.me.isCommunityArticleVote
|
||||
}
|
||||
}
|
||||
if (item is FollowDynamicBbsItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.vote = articleDetailEntity.count.vote
|
||||
newCount.comment = articleDetailEntity.count.comment
|
||||
count = newCount
|
||||
title = articleDetailEntity.title
|
||||
brief = articleDetailEntity.content.removeVideoContent().removeInsertedContent()
|
||||
.clearHtmlFormatCompletely()
|
||||
me.isCommunityArticleVote = articleDetailEntity.me.isCommunityArticleVote
|
||||
}
|
||||
}
|
||||
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
||||
override fun onCommentResult(position: Int, commentEntity: CommentEntity) {
|
||||
val item = adapter.currentList[position]
|
||||
if (item is FollowDynamicPersonalItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.vote = commentEntity.vote
|
||||
newCount.comment = commentEntity.reply
|
||||
count = newCount
|
||||
brief = commentEntity.content ?: ""
|
||||
me.isAnswerVoted = commentEntity.me?.isCommentVoted ?: false
|
||||
}
|
||||
}
|
||||
if (item is FollowDynamicBbsItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.vote = commentEntity.vote
|
||||
newCount.comment = commentEntity.reply
|
||||
count = newCount
|
||||
brief = commentEntity.content ?: ""
|
||||
me.isAnswerVoted = commentEntity.me?.isCommentVoted ?: false
|
||||
}
|
||||
}
|
||||
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
||||
override fun onQuestionsDetailResult(position: Int, questionsDetailEntity: QuestionsDetailEntity) {
|
||||
val item = adapter.currentList[position]
|
||||
if (item is FollowDynamicPersonalItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.answer = questionsDetailEntity.count.answer - count.reply
|
||||
count = newCount
|
||||
title = questionsDetailEntity.title ?: ""
|
||||
}
|
||||
}
|
||||
if (item is FollowDynamicBbsItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.answer = questionsDetailEntity.count.answer - count.reply
|
||||
count = newCount
|
||||
title = questionsDetailEntity.title ?: ""
|
||||
}
|
||||
}
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
||||
override fun onForumVideoResult(position: Int, forumVideoEntity: ForumVideoEntity) {
|
||||
val item = adapter.currentList[position]
|
||||
if (item is FollowDynamicPersonalItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.vote = forumVideoEntity.count.vote
|
||||
newCount.comment = forumVideoEntity.count.comment
|
||||
count = newCount
|
||||
des = forumVideoEntity.des
|
||||
title = forumVideoEntity.title
|
||||
me.isVoted = forumVideoEntity.me.isVoted
|
||||
}
|
||||
}
|
||||
if (item is FollowDynamicBbsItem) {
|
||||
item.data.apply {
|
||||
val newCount = count
|
||||
newCount.vote = forumVideoEntity.count.vote
|
||||
newCount.comment = forumVideoEntity.count.comment
|
||||
count = newCount
|
||||
des = forumVideoEntity.des
|
||||
title = forumVideoEntity.title
|
||||
me.isVoted = forumVideoEntity.me.isVoted
|
||||
}
|
||||
}
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
||||
})
|
||||
viewLifecycleOwner.lifecycle.addObserver(launcher)
|
||||
|
||||
binding.rvDynamic.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.rvDynamic.itemAnimator = null
|
||||
binding.rvDynamic.addItemDecoration(FollowItemDecoration(paint))
|
||||
binding.rvDynamic.adapter = adapter
|
||||
|
||||
binding.srlRefresh.setOnRefreshListener {
|
||||
viewModel.loadFirst(userId, bbsId, true)
|
||||
}
|
||||
|
||||
binding.ivClose.setOnClickListener {
|
||||
followDynamicViewModel.finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
context?.let {
|
||||
binding.vHeaderBackground.background = R.drawable.background_shape_white_radius_8_top_only.toDrawable(it)
|
||||
}
|
||||
binding.rvDynamic.tryToClearRecycler()
|
||||
binding.rvDynamic.recycledViewPool.clear()
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount)
|
||||
paint.color = R.color.ui_divider.toColor(requireContext())
|
||||
}
|
||||
|
||||
private class FollowItemDecoration(
|
||||
private val paint: Paint
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
super.onDrawOver(c, parent, state)
|
||||
parent.children.forEach { child ->
|
||||
val bottom = child.bottom
|
||||
c.drawLine(
|
||||
16F.dip2px().toFloat(),
|
||||
bottom.toFloat(),
|
||||
(child.width - 16F.dip2px()).toFloat(),
|
||||
bottom.toFloat(),
|
||||
paint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_USER_ID = "key_user_id"
|
||||
private const val KEY_BBS_ID = "key_bbs_id"
|
||||
private const val KEY_BBS_TYPE = "key_bbs_type"
|
||||
|
||||
fun newInstance(userId: String, bbsId: String, bbsType: String): FollowDynamicListFragment {
|
||||
val fragment = FollowDynamicListFragment()
|
||||
val bundle = Bundle().apply {
|
||||
putString(KEY_USER_ID, userId)
|
||||
putString(KEY_BBS_ID, bbsId)
|
||||
putString(KEY_BBS_TYPE, bbsType)
|
||||
}
|
||||
fragment.arguments = bundle
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,550 @@
|
||||
package com.gh.gamecenter.forum.home.follow.fragment
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
|
||||
import com.alibaba.android.arouter.launcher.ARouter
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.ShareCardActivity
|
||||
import com.gh.gamecenter.ShareCardPicActivity
|
||||
import com.gh.gamecenter.common.base.fragment.LazyFragment
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.iinterface.IScrollable
|
||||
import com.gh.gamecenter.core.provider.IVisitManagerProvider
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
import com.gh.gamecenter.databinding.FragmentFollowHomeBinding
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.feature.provider.IMessageDetailProvider
|
||||
import com.gh.gamecenter.forum.home.CommunityHomeFragment
|
||||
import com.gh.gamecenter.forum.home.CommunityHomeViewModel
|
||||
import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper
|
||||
import com.gh.gamecenter.forum.home.follow.*
|
||||
import com.gh.gamecenter.forum.home.follow.adapter.FollowHomeAdapter
|
||||
import com.gh.gamecenter.forum.home.follow.fragment.AllFollowedFragment.Companion.KEY_HAS_READ_ID
|
||||
import com.gh.gamecenter.forum.home.follow.model.FollowRepository.Companion.FOLLOW_UPDATE_TYPE_GAME
|
||||
import com.gh.gamecenter.forum.home.follow.model.FollowRepository.Companion.FOLLOW_UPDATE_TYPE_USER
|
||||
import com.gh.gamecenter.forum.home.follow.viewholder.FollowHomeHeaderViewHolder
|
||||
import com.gh.gamecenter.forum.home.follow.viewholder.FollowRecommendListViewHolder
|
||||
import com.gh.gamecenter.forum.home.follow.viewmodel.FollowHomeViewModel
|
||||
import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailActivity
|
||||
import com.gh.gamecenter.libao.LibaoDetailActivity
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.login.user.UserViewModel
|
||||
import com.gh.gamecenter.message.view.concern.ConcernFragment
|
||||
import com.gh.gamecenter.newsdetail.NewsShareDialog
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class FollowHomeFragment : LazyFragment(), IScrollable {
|
||||
|
||||
override fun addSyncPageObserver(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
private val viewModel by viewModels<FollowHomeViewModel>()
|
||||
|
||||
private val communityHomeViewModel by viewModels<CommunityHomeViewModel>(
|
||||
ownerProducer = { requireParentFragment() }
|
||||
)
|
||||
|
||||
private lateinit var userViewModel: UserViewModel
|
||||
|
||||
private var userId: String? = null
|
||||
|
||||
private var dynamicType: String? = null
|
||||
|
||||
private var binding: FragmentFollowHomeBinding? = null
|
||||
|
||||
private var hasLoaded = false
|
||||
|
||||
private lateinit var pageConfiguration: FollowPageConfiguration
|
||||
|
||||
private var isFirstTimeEnter = true
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
private val dividerPaint by lazy {
|
||||
Paint().apply {
|
||||
color = R.color.ui_divider.toColor(requireContext())
|
||||
strokeWidth = 0.5F.dip2px().toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private val rvBackgroundPaint by lazy {
|
||||
Paint().apply {
|
||||
color = R.color.ui_surface.toColor(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
private val layoutManager by lazy {
|
||||
LinearLayoutManager(context)
|
||||
}
|
||||
|
||||
private val adapter: FollowHomeAdapter by lazy {
|
||||
FollowHomeAdapter(viewLifecycleOwner, viewModel, pageConfiguration)
|
||||
}
|
||||
|
||||
// 3s 以后才开始计算
|
||||
private var enterStartTime = 0L
|
||||
|
||||
private lateinit var allFollowLauncher: ActivityResultLauncher<Unit>
|
||||
|
||||
private var mScrollCalculatorHelper: ForumScrollCalculatorHelper? = null
|
||||
|
||||
private val headerViewHolder by lazy(LazyThreadSafetyMode.NONE) {
|
||||
binding?.let {
|
||||
FollowHomeHeaderViewHolder(it.layoutHeader, object : FollowHomeHeaderViewHolder.OnEventListener {
|
||||
override fun onItemClick(position: Int, data: List<FollowUserEntity>) {
|
||||
viewModel.viewItemFollowed(position, data)
|
||||
}
|
||||
|
||||
override fun onAllClick() {
|
||||
viewModel.viewAllFollowed()
|
||||
}
|
||||
|
||||
override fun onLoginClick() {
|
||||
viewModel.login()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun provideSyncAdapter(): RecyclerView.Adapter<*> {
|
||||
return adapter
|
||||
}
|
||||
|
||||
override fun getRealLayoutId(): Int = R.layout.fragment_follow_home
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
binding = FragmentFollowHomeBinding.bind(inflatedView)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pageConfiguration = FollowPageConfiguration("社区-关注", "")
|
||||
allFollowLauncher = requireActivity().activityResultRegistry.register(
|
||||
KEY_LAUNCH_ALL_FOLLOW,
|
||||
this,
|
||||
object : ActivityResultContract<Unit, List<String>?>() {
|
||||
override fun createIntent(context: Context, input: Unit?): Intent {
|
||||
return AllFollowedActivity.getIntent(context)
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): List<String>? {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
val hasReadIds = intent?.getStringArrayListExtra(KEY_HAS_READ_ID)
|
||||
return hasReadIds ?: listOf()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
) { value ->
|
||||
value?.let {
|
||||
if (it.isEmpty()) {
|
||||
viewModel.loadFollowedUsers()
|
||||
} else {
|
||||
adapter.notifyFollowUserReadStatus(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.hasFollowedUser.observe(this, Observer {
|
||||
communityHomeViewModel.updateHasFollowUser(it)
|
||||
})
|
||||
}
|
||||
|
||||
override fun initRealView() {
|
||||
super.initRealView()
|
||||
|
||||
mScrollCalculatorHelper = ForumScrollCalculatorHelper(R.id.horizontalVideoView, R.id.verticalVideoView, 0)
|
||||
binding?.recyclerView?.itemAnimator = null
|
||||
|
||||
userViewModel = viewModelProvider(UserViewModel.Factory(requireActivity().application))
|
||||
|
||||
userViewModel.loginObsUserinfo.observe(viewLifecycleOwner) {
|
||||
/* 由于其它页面每次初始化 userViewModel时,loginObsUserInfo都会被赋值一次,为避免重复加载,这里需要过滤重复的用户信息 */
|
||||
if (hasLoaded && userId == it?.data?.userId) {
|
||||
// 已经加载过数据,并且userId不变,不需要重复加载
|
||||
return@observe
|
||||
}
|
||||
hasLoaded = true
|
||||
if (it?.data == null) {
|
||||
// 未登录,加载推荐关注列表
|
||||
userId = null
|
||||
viewModel.loadFirst(userId != null, isPullToRefresh = false)
|
||||
} else {
|
||||
// 已登录,加载已关注列表
|
||||
userId = it.data.userId
|
||||
if (userId != null) {
|
||||
viewModel.loadFirst(userId != null, isPullToRefresh = false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
communityHomeViewModel.filterFollowedAction.observe(viewLifecycleOwner, EventObserver {
|
||||
val newDynamicType = when (it) {
|
||||
0 -> ""
|
||||
1 -> FOLLOW_UPDATE_TYPE_USER
|
||||
else -> FOLLOW_UPDATE_TYPE_GAME
|
||||
}
|
||||
if (dynamicType != newDynamicType) {
|
||||
dynamicType = newDynamicType
|
||||
viewModel.loadFirst(userId != null, dynamicType ?: "", false)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
with(viewModel) {
|
||||
|
||||
loadStatus.observe(viewLifecycleOwner) { (loadStatus, isPullRefresh) ->
|
||||
if (isPullRefresh) {
|
||||
if (loadStatus != LoadStatus.INIT_LOADING) {
|
||||
binding?.srlRefresh?.isRefreshing = false
|
||||
}
|
||||
} else {
|
||||
binding?.layoutLoading?.root?.goneIf(loadStatus != LoadStatus.INIT_LOADING)
|
||||
}
|
||||
adapter.setLoadStatus(loadStatus)
|
||||
|
||||
if (loadStatus == LoadStatus.INIT_LOADED) {
|
||||
AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
tryCatchInRelease {
|
||||
scroll()
|
||||
mScrollCalculatorHelper?.onScrollStateChanged(
|
||||
binding?.recyclerView!!,
|
||||
RecyclerView.SCROLL_STATE_IDLE
|
||||
)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
dataList.observe(viewLifecycleOwner) {
|
||||
if (it.size == 1) {
|
||||
// 由于第一个是头部,如果list长度为1,则说明后面没有数据
|
||||
binding?.llEmptyContainer?.goneIf(false)
|
||||
binding?.recyclerView?.goneIf(true)
|
||||
val headerItem = it.firstOrNull() as? FollowUserItem
|
||||
if (headerItem != null) {
|
||||
headerViewHolder?.bind(headerItem.isLogin, headerItem.data)
|
||||
setEmptyLayout(headerItem)
|
||||
}
|
||||
} else {
|
||||
binding?.llEmptyContainer?.goneIf(true)
|
||||
binding?.recyclerView?.goneIf(false)
|
||||
setDataList(it)
|
||||
}
|
||||
}
|
||||
|
||||
followedDetailDestination.observe(viewLifecycleOwner, EventObserver { (position, data) ->
|
||||
FollowDynamicActivity.start(requireContext(), position, data)
|
||||
activity?.overridePendingTransition(0, 0) // 取消进入和退出动画
|
||||
})
|
||||
|
||||
myFollowedListDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
allFollowLauncher.launch(Unit)
|
||||
})
|
||||
|
||||
loginDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
CheckLoginUtils.checkLogin(context, "关注", null)
|
||||
})
|
||||
|
||||
gameDetailDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
requireContext(),
|
||||
it.id,
|
||||
"",
|
||||
-1,
|
||||
traceEvent = it.exposureEvent
|
||||
)
|
||||
})
|
||||
|
||||
linkDestination.observe(viewLifecycleOwner, EventObserver { (link, exposureEvent) ->
|
||||
DirectUtils.directToLinkPage(requireContext(), link, "关注页面", "", exposureEvent)
|
||||
})
|
||||
|
||||
commonCollectionDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
val intent = CustomCommonCollectionDetailActivity.getIntent(
|
||||
requireContext(),
|
||||
it.id,
|
||||
it.layout,
|
||||
"自定义页面",
|
||||
)
|
||||
startActivity(intent)
|
||||
})
|
||||
|
||||
userHomeDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
DirectUtils.directToHomeActivity(requireContext(), it, 1, "", "")
|
||||
})
|
||||
|
||||
libaoDetailDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
val intent = LibaoDetailActivity.getIntent(requireContext(), it, false, "$mEntrance+关注列表")
|
||||
startActivity(intent)
|
||||
})
|
||||
|
||||
newDetailDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
ARouter.getInstance().build(RouteConsts.activity.newsDetailActivity)
|
||||
.withString(EntranceConsts.KEY_NEWSID, it)
|
||||
.withString(EntranceConsts.KEY_ENTRANCE, "")
|
||||
.navigation(requireActivity(), ConcernFragment.NEWS_MESSAGE_ARTICLE_REQUEST)
|
||||
})
|
||||
|
||||
articleCommentDetailDestination.observe(viewLifecycleOwner, EventObserver {
|
||||
val messageDetailProvider = ARouter.getInstance().build(RouteConsts.provider.messageDetail)
|
||||
.navigation() as? IMessageDetailProvider
|
||||
if (messageDetailProvider != null) {
|
||||
val intent = messageDetailProvider.getIntentById(requireContext(), it, -1, false, "")
|
||||
startActivityForResult(intent, ConcernFragment.NEWS_MESSAGE_ARTICLE_REQUEST)
|
||||
}
|
||||
})
|
||||
|
||||
shareArticleDestination.observe(viewLifecycleOwner, EventObserver { concernEntity ->
|
||||
NewsShareDialog.show(
|
||||
requireActivity() as AppCompatActivity,
|
||||
concernEntity.shortId,
|
||||
concernEntity.id,
|
||||
concernEntity.gameIcon,
|
||||
concernEntity.title
|
||||
)
|
||||
})
|
||||
|
||||
copyExchangeCodeAction.observe(viewLifecycleOwner, EventObserver {
|
||||
it.copyTextAndToast()
|
||||
})
|
||||
|
||||
updateOkhttpCacheAction.observe(viewLifecycleOwner, EventObserver {
|
||||
// 更新okhttp缓存数据
|
||||
val visitManagerProvider =
|
||||
ARouter.getInstance()
|
||||
.build(RouteConsts.provider.visitManager)
|
||||
.navigation() as? IVisitManagerProvider
|
||||
visitManagerProvider?.updateOkhttpCache(requireContext(), it)
|
||||
})
|
||||
}
|
||||
|
||||
binding?.srlRefresh?.setOnRefreshListener {
|
||||
mScrollCalculatorHelper?.currentPlayer?.release()
|
||||
mScrollCalculatorHelper?.reset()
|
||||
val isLogin = userId != null
|
||||
viewModel.loadFirst(isLogin, dynamicType ?: "", true)
|
||||
}
|
||||
|
||||
binding?.recyclerView?.addOnScrollListener(object : OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
binding?.recyclerView?.let {
|
||||
mScrollCalculatorHelper?.onScrollStateChanged(it, newState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy != 0) scroll()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private fun setEmptyLayout(headerItem: FollowUserItem) {
|
||||
binding?.layoutEmpty?.run {
|
||||
root.goneIf(false)
|
||||
if (headerItem.isLogin) {
|
||||
reuseNoneDataDescTv.goneIf(true)
|
||||
} else {
|
||||
reuseNoneDataDescTv.goneIf(false)
|
||||
reuseNoneDataDescTv.setText(R.string.follow_home_no_login_tips)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun scroll() {
|
||||
val firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
|
||||
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
|
||||
mScrollCalculatorHelper?.onScroll(viewModel.videoList, firstVisibleItem, lastVisibleItem)
|
||||
}
|
||||
|
||||
override fun onFragmentResume() {
|
||||
resumeVideo()
|
||||
super.onFragmentResume()
|
||||
handler.postDelayed({
|
||||
enterStartTime = System.currentTimeMillis()
|
||||
SensorsBridge.trackFollowTabBrowse(isFirstTimeEnter)
|
||||
}, TRACK_ENTER_TIME)
|
||||
}
|
||||
|
||||
override fun onFragmentPause() {
|
||||
super.onFragmentPause()
|
||||
pauseVideo()
|
||||
isFirstTimeEnter = false
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
if (enterStartTime != 0L) {
|
||||
val endTime = System.currentTimeMillis()
|
||||
val stayLength = "${(endTime - enterStartTime) / MS_UNIT}"
|
||||
SensorsBridge.trackFollowTabBrowseDuration(stayLength)
|
||||
}
|
||||
enterStartTime = 0L
|
||||
}
|
||||
|
||||
private fun resumeVideo() {
|
||||
mScrollCalculatorHelper?.run {
|
||||
if (currentPlayer != null
|
||||
&& currentPosition >= 0
|
||||
&& currentPosition < viewModel.videoList.size
|
||||
) {
|
||||
val video = viewModel.videoList.safelyGetInRelease(currentPosition)
|
||||
if (video != null) {
|
||||
val position = ForumScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(video.url))
|
||||
//这里必须要延迟操作,否则会白屏
|
||||
mBaseHandler.postDelayed({
|
||||
tryCatchInRelease {
|
||||
if (position != 0L) {
|
||||
if (currentPlayer?.currentState == GSYVideoView.CURRENT_STATE_PAUSE) {
|
||||
currentPlayer?.startPlayLogic(true)
|
||||
}
|
||||
} else {
|
||||
currentPlayer?.release()
|
||||
}
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun pauseVideo() {
|
||||
mScrollCalculatorHelper?.run {
|
||||
if (currentPlayer != null
|
||||
&& currentPosition >= 0
|
||||
&& currentPosition < viewModel.videoList.size
|
||||
) {
|
||||
currentPlayer?.onVideoPause()
|
||||
val position = currentPlayer?.getCurrentPosition() ?: 0L
|
||||
val video = viewModel.videoList.safelyGetInRelease(currentPosition)
|
||||
if (video != null) {
|
||||
ForumScrollCalculatorHelper.savePlaySchedule(MD5Utils.getContentMD5(video.url), position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mScrollCalculatorHelper?.currentPlayer?.release()
|
||||
binding = null
|
||||
}
|
||||
|
||||
private fun setDataList(dataList: List<FollowItem>) {
|
||||
binding?.run {
|
||||
if (recyclerView.adapter == null) {
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.addItemDecoration(FollowItemDecoration(dividerPaint, rvBackgroundPaint))
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
adapter.submitList(dataList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun scrollToTop() {
|
||||
binding?.recyclerView?.scrollToPosition(0)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onUserFollow(change: EBUserFollow) {
|
||||
adapter.notifyFollowUserChanged(change)
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
val rvFollows = binding?.recyclerView
|
||||
if (rvFollows != null) {
|
||||
if (parentFragment is CommunityHomeFragment) {
|
||||
(parentFragment as CommunityHomeFragment).translateTopBg(rvFollows.computeVerticalScrollOffset() - 8F.dip2px())
|
||||
}
|
||||
rvFollows.tryToClearRecycler()
|
||||
rvFollows.recycledViewPool.clear()
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount)
|
||||
}
|
||||
dividerPaint.color = R.color.ui_divider.toColor(requireContext())
|
||||
rvBackgroundPaint.color = R.color.ui_surface.toColor(requireContext())
|
||||
}
|
||||
|
||||
private class FollowItemDecoration(
|
||||
private val dividerPaint: Paint,
|
||||
private val backgroundPaint: Paint
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
super.onDrawOver(c, parent, state)
|
||||
parent.children.forEach { child ->
|
||||
val position = parent.getChildAdapterPosition(child)
|
||||
// 1.第一个itemView为头部关注人列表,不需要分割线
|
||||
// 2.默认推荐数据不需要分割线
|
||||
val isLoggedIn = UserManager.getInstance().isLoggedIn
|
||||
if (isLoggedIn && position > 0) {
|
||||
val bottom = child.bottom
|
||||
c.drawLine(
|
||||
16F.dip2px().toFloat(),
|
||||
bottom.toFloat(),
|
||||
(child.width - 16F.dip2px()).toFloat(),
|
||||
bottom.toFloat(),
|
||||
dividerPaint
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 由于 关注用户/论坛 列表需要和顶部 tab连在一起组成渐变色,所以RecyclerView不能直接设置背景颜色
|
||||
* 在这里绘制除了 关注用户/论坛 列表其它位置的背景颜色
|
||||
* + 12 是为了预留出圆角的高度
|
||||
*/
|
||||
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
super.onDraw(c, parent, state)
|
||||
val firstChild = parent.children.firstOrNull()
|
||||
if (firstChild != null) {
|
||||
c.drawRect(Rect(0, firstChild.bottom + 12F.dip2px(), parent.width, parent.bottom), backgroundPaint)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_LAUNCH_ALL_FOLLOW = "key_launch_all_follow"
|
||||
|
||||
const val LOCATION_FOLLOW_PAGE = "关注页面"
|
||||
private const val TRACK_ENTER_TIME = 3000L
|
||||
private const val MS_UNIT = 1000.0
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.gh.gamecenter.forum.home.follow.model
|
||||
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.entity.FollowOperateTopRequest
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedNormalItem
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Single
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
class AllFollowRepository(private val apiService: ApiService) {
|
||||
|
||||
fun loadMyFollowedUser(pageNo: Int) =
|
||||
apiService.getMyFollowedUsers(
|
||||
UserManager.getInstance().userId,
|
||||
PackageUtils.getGhVersionName(),
|
||||
HaloApp.getInstance().channel,
|
||||
pageNo,
|
||||
true,
|
||||
mapOf()
|
||||
)
|
||||
.map { list ->
|
||||
list.map {
|
||||
AllFollowedNormalItem(it, it.isTop)
|
||||
}
|
||||
}
|
||||
|
||||
fun operateTop(tops: List<FollowOperateTopRequest>): Single<ResponseBody> {
|
||||
val body = mapOf("top" to tops).toRequestBody()
|
||||
return apiService.operateTop(
|
||||
UserManager.getInstance().userId,
|
||||
PackageUtils.getGhVersionName(),
|
||||
HaloApp.getInstance().channel,
|
||||
body
|
||||
)
|
||||
}
|
||||
|
||||
fun postRead(type: String, typeId: String) =
|
||||
apiService.postRead(UserManager.getInstance().userId, type, typeId)
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.gh.gamecenter.forum.home.follow.model
|
||||
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicBbsItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicPersonalItem
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
class FollowDynamicRepository(
|
||||
private val apiService: ApiService
|
||||
) {
|
||||
|
||||
fun loadData(userId: String, bbsId: String, pageNo: Int) =
|
||||
if (userId.isNotBlank()) {
|
||||
loadPersonDynamic(userId, pageNo)
|
||||
} else {
|
||||
loadBbsDynamic(bbsId, pageNo)
|
||||
}
|
||||
|
||||
private fun loadPersonDynamic(userId: String, pageNo: Int) =
|
||||
apiService.getPersonalHistory(
|
||||
userId,
|
||||
pageNo,
|
||||
HaloApp.getInstance().channel,
|
||||
PERSON_DYNAMIC_FILTER
|
||||
).map {
|
||||
it.map(::FollowDynamicPersonalItem)
|
||||
}
|
||||
|
||||
private fun loadBbsDynamic(bbsId: String, pageNo: Int) =
|
||||
apiService.getAllForumList(bbsId, BBS_DYNAMIC_SORT, pageNo, mapOf())
|
||||
.map {
|
||||
it.map(::FollowDynamicBbsItem)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val PERSON_DYNAMIC_FILTER = "scene:follow_personal,type:all"
|
||||
private const val BBS_DYNAMIC_SORT = "time.reply:-1&type=community_article|question|video"
|
||||
|
||||
fun newInstance(): FollowDynamicRepository = FollowDynamicRepository(
|
||||
RetrofitManager.getInstance().api
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
package com.gh.gamecenter.forum.home.follow.model
|
||||
|
||||
import com.gh.common.util.LibaoUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity.Companion.FOLLOW_UPDATE_TYPE_ARTICLE
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity.Companion.FOLLOW_UPDATE_TYPE_LIBAO
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity.Companion.FOLLOW_UPDATE_TYPE_LIBAO_EXCHANGE
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity.Companion.FOLLOW_UPDATE_TYPE_USER_POST
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.feature.entity.LibaoEntity
|
||||
import com.gh.gamecenter.forum.home.follow.*
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Single
|
||||
|
||||
class FollowRepository(
|
||||
private val apiService: ApiService,
|
||||
private val newApiService: ApiService
|
||||
) {
|
||||
|
||||
private var hasFollowUser = false
|
||||
|
||||
private val version: String
|
||||
get() = PackageUtils.getGhVersionName()
|
||||
|
||||
private val channel: String
|
||||
get() = HaloApp.getInstance().channel
|
||||
|
||||
fun loadHomeData(isLogin: Boolean, type: String, pageNo: Int) =
|
||||
if (isLogin) {
|
||||
loadFollowedUsers()
|
||||
.flatMap { users ->
|
||||
if (users.isEmpty()) {
|
||||
hasFollowUser = false
|
||||
// 默认推荐
|
||||
loadFollowRecommend(pageNo)
|
||||
.map {
|
||||
listOf(FollowUserItem(true, listOf())) + it
|
||||
}
|
||||
} else {
|
||||
// 关注动态
|
||||
hasFollowUser = true
|
||||
loadFollowUpdates(type, pageNo)
|
||||
.map {
|
||||
listOf(FollowUserItem(true, users)) + it
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loadFollowRecommend(pageNo)
|
||||
.map {
|
||||
listOf(FollowUserItem(false, listOf())) + it
|
||||
}
|
||||
}
|
||||
|
||||
fun loadMore(type: String, pageNo: Int) =
|
||||
if (hasFollowUser) {
|
||||
loadFollowUpdates(type, pageNo)
|
||||
} else {
|
||||
loadFollowRecommend(pageNo)
|
||||
}
|
||||
|
||||
fun loadFollowedUsers(): Single<List<FollowUserEntity>> {
|
||||
return newApiService.getMyFollowedUsers(
|
||||
UserManager.getInstance().userId,
|
||||
PackageUtils.getGhVersionName(),
|
||||
HaloApp.getInstance().channel,
|
||||
1,
|
||||
true,
|
||||
mapOf("page_size" to 20)
|
||||
).onErrorReturnItem(listOf())
|
||||
}
|
||||
|
||||
private fun loadFollowUpdates(type: String, pageNo: Int): Single<List<FollowItem>> =
|
||||
newApiService.getFollowUpdates(
|
||||
UserManager.getInstance().userId,
|
||||
type,
|
||||
pageNo,
|
||||
PackageUtils.getGhVersionName(),
|
||||
HaloApp.getInstance().channel
|
||||
)
|
||||
.onErrorReturnItem(listOf())
|
||||
.map(::transformFollowUpdatesToItemData)
|
||||
|
||||
fun loadFollowRecommend(pageNo: Int): Single<List<FollowItem>> =
|
||||
if (pageNo == 1) {
|
||||
loadRecommendUsers().zipWith(
|
||||
loadFollowCommonCollection(pageNo)
|
||||
) { user, commonCollections ->
|
||||
val data = arrayListOf<FollowItem>()
|
||||
if (user.data.isNotEmpty()) {
|
||||
data.add(user)
|
||||
}
|
||||
data.addAll(commonCollections)
|
||||
data
|
||||
}
|
||||
} else {
|
||||
Single.just(listOf())
|
||||
}
|
||||
|
||||
private fun loadRecommendUsers(): Single<FollowRecommendUsersItem> =
|
||||
newApiService.getRecommendUser(version, channel)
|
||||
.onErrorReturnItem(listOf())
|
||||
.map(::FollowRecommendUsersItem)
|
||||
|
||||
|
||||
private fun loadFollowCommonCollection(pageNo: Int): Single<List<FollowItem>> =
|
||||
newApiService.getFollowCommonCollection(version, channel, pageNo)
|
||||
.onErrorReturnItem(listOf())
|
||||
.map { components ->
|
||||
val data = arrayListOf<FollowItem>()
|
||||
components.forEach {
|
||||
val layout = it.linkCommonCollection?.layout
|
||||
if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER
|
||||
|| layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD
|
||||
|| layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT
|
||||
) {
|
||||
data.addAll(convertSplitCommonContentCollection(it))
|
||||
} else {
|
||||
// data.title 为后台配置的板块title
|
||||
it.linkCommonCollection.name = it.title
|
||||
data.add(
|
||||
FollowCommonCollectionItem(it)
|
||||
)
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
private fun convertSplitCommonContentCollection(item: FollowCommonContentCollection): List<FollowSplitCommonContentCollectionItem> {
|
||||
val linkCommonCollection = item.linkCommonCollection
|
||||
// data.title 为后台配置的板块title
|
||||
linkCommonCollection.name = item.title
|
||||
val data = linkCommonCollection.data
|
||||
val items = arrayListOf<FollowSplitCommonContentCollectionItem>()
|
||||
(data.indices step 2).forEach { left ->
|
||||
items.add(
|
||||
FollowSplitCommonContentCollectionItem(
|
||||
linkCommonCollection,
|
||||
left
|
||||
)
|
||||
)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
fun followUser(userId: String, isFollow: Boolean) =
|
||||
if (isFollow) {
|
||||
apiService.postFollowing(userId)
|
||||
} else {
|
||||
apiService.deleteFollowing(userId)
|
||||
}
|
||||
|
||||
fun postArticleVisit(articleId: String) =
|
||||
apiService.postArticleVisit(articleId)
|
||||
|
||||
companion object {
|
||||
|
||||
const val FOLLOW_UPDATE_TYPE_USER = "user"
|
||||
const val FOLLOW_UPDATE_TYPE_GAME = "game"
|
||||
|
||||
fun transformFollowUpdatesToItemData(updates: List<FollowDynamicEntity>): List<FollowItem> {
|
||||
val data = arrayListOf<FollowItem>()
|
||||
updates.forEach {
|
||||
when (it.type) {
|
||||
FOLLOW_UPDATE_TYPE_LIBAO -> {
|
||||
if (it.libao != null) {
|
||||
FollowGiftPackItem(it.libao, it.time)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
FOLLOW_UPDATE_TYPE_LIBAO_EXCHANGE -> {
|
||||
if (it.libaoExchange != null) {
|
||||
FollowGiftPackItem(it.libaoExchange, it.time)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
FOLLOW_UPDATE_TYPE_ARTICLE -> it.article?.let(::FollowArticleItem)
|
||||
|
||||
FOLLOW_UPDATE_TYPE_USER_POST -> it.userPost?.let(::FollowPostCardItem)
|
||||
|
||||
else -> null
|
||||
}?.let(data::add)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
fun newInstance() = FollowRepository(RetrofitManager.getInstance().api, RetrofitManager.getInstance().newApi)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.CommonCollection12ItemCustomBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentCollection12Ui
|
||||
|
||||
/**
|
||||
* 关注-双列banner 双列竖式卡片 竖排图文列表
|
||||
* 通用链接合集(1-2样式)
|
||||
*/
|
||||
class FollowCommonCollection12ViewHolder(
|
||||
val binding: CommonCollection12ItemCustomBinding,
|
||||
private val listener: OnChildEventListener
|
||||
) :
|
||||
ViewHolder(binding.root) {
|
||||
|
||||
private lateinit var _data: CustomPageData.CommonContentCollection
|
||||
|
||||
private val collection12Ui by lazy {
|
||||
CommonContentCollection12Ui(binding, object : CommonContentCollection12Ui.OnCollection12Listener {
|
||||
override fun onChildItemClick(childPosition: Int, contentEntity: CommonCollectionContentEntity) {
|
||||
|
||||
val linkEntity = contentEntity.linkEntity
|
||||
NewLogUtils.logCommonCollectionClick(
|
||||
_data.id,
|
||||
_data.name,
|
||||
"",
|
||||
"",
|
||||
"关注列表",
|
||||
"关注首页",
|
||||
linkEntity.title ?: "",
|
||||
contentEntity.addedContent1 ?: "",
|
||||
contentEntity.addedContent2 ?: "",
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.text ?: "",
|
||||
childPosition + 1
|
||||
)
|
||||
NewLogUtils.logCommonCategoryHomeContentClick(
|
||||
contentEntity.title,
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.link ?: "",
|
||||
linkEntity.text ?: "",
|
||||
_data.name,
|
||||
_data.id,
|
||||
"关注",
|
||||
""
|
||||
)
|
||||
|
||||
listener.navigateToLinkPage(bindingAdapterPosition, linkEntity, _data, "内容卡片", null)
|
||||
}
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(collection: CustomPageData.CommonContentCollection) {
|
||||
listener.navigateToCommonCollectionDetailPage(collection)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bind(data: CustomPageData.CommonContentCollection, leftPosition: Int) {
|
||||
_data = data
|
||||
collection12Ui.bind(data, leftPosition)
|
||||
itemView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context))
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
)
|
||||
|
||||
fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.exposure.ExposureTraceUtils
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.CommonCollectionListCustomBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
import com.gh.gamecenter.entity.ExposureLinkEntity
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentHorizontalSlideListUi
|
||||
|
||||
/**
|
||||
* 关注-通用内容合集-横排滑动Banner
|
||||
* 关注-通用内容合集-横排竖式卡片
|
||||
* 关注-通用内容合集-横排图文列表
|
||||
*/
|
||||
class FollowCommonCollectionViewHolder(
|
||||
val binding: CommonCollectionListCustomBinding,
|
||||
private val listener: OnChildEventListener
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
private lateinit var _data: FollowCommonContentCollection
|
||||
|
||||
private val horizontalSlideUi by lazy {
|
||||
CommonContentHorizontalSlideListUi(binding,
|
||||
object : CommonContentHorizontalSlideListUi.OnHorizontalSlideListListener {
|
||||
override fun addExposureEvent(childPosition: Int, link: ExposureLinkEntity) = Unit
|
||||
|
||||
override fun onChildItemClick(childPosition: Int, entity: CommonCollectionContentEntity) {
|
||||
|
||||
val linkEntity = entity.linkEntity
|
||||
|
||||
NewLogUtils.logCommonCollectionClick(
|
||||
_data.linkCommonCollection.id,
|
||||
_data.linkCommonCollection.name,
|
||||
"",
|
||||
"",
|
||||
"关注推荐列表",
|
||||
"关注页",
|
||||
linkEntity.title ?: "",
|
||||
entity.addedContent1 ?: "",
|
||||
entity.addedContent2 ?: "",
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.text ?: "",
|
||||
childPosition + 1
|
||||
)
|
||||
NewLogUtils.logCommonCategoryHomeContentClick(
|
||||
entity.title,
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.link ?: "",
|
||||
linkEntity.text ?: "",
|
||||
_data.linkCommonCollection.name,
|
||||
_data.linkCommonCollection.id,
|
||||
"",
|
||||
""
|
||||
)
|
||||
linkEntity.exposureEvent?.let {
|
||||
val clickEvent = ExposureEvent(
|
||||
payload = it.payload,
|
||||
source = it.source,
|
||||
eTrace = ExposureTraceUtils.appendTrace(it),
|
||||
event = ExposureType.CLICK
|
||||
)
|
||||
if (linkEntity.type != "game") {
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
}
|
||||
listener.navigateToLinkPage(bindingAdapterPosition, linkEntity, _data.linkCommonCollection, "内容卡片", null)
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection) {
|
||||
listener.navigateToCommonCollectionDetailPage(data)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: FollowCommonContentCollection) {
|
||||
_data = data
|
||||
horizontalSlideUi.bind(_data.linkCommonCollection)
|
||||
itemView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context))
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
)
|
||||
|
||||
fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.baselist.LoadType
|
||||
import com.gh.gamecenter.common.databinding.RefreshFooterviewBinding
|
||||
|
||||
class FollowFooterViewHolder(val binding: RefreshFooterviewBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
val loading: ProgressBar
|
||||
val hint: TextView
|
||||
|
||||
init {
|
||||
loading = itemView.findViewById(R.id.footerview_loading)
|
||||
hint = itemView.findViewById(R.id.footerview_hint)
|
||||
}
|
||||
|
||||
fun initItemPadding() {
|
||||
itemView.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
fun initFooterViewHolder(
|
||||
isNetworkError: Boolean,
|
||||
isOver: Boolean,
|
||||
@StringRes loadOverHint: Int
|
||||
) {
|
||||
if (isNetworkError) {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(R.string.loading_failed_retry)
|
||||
} else if (isOver) {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(loadOverHint)
|
||||
} else {
|
||||
loading.visibility = View.VISIBLE
|
||||
hint.setText(R.string.loading)
|
||||
}
|
||||
}
|
||||
|
||||
fun initFooterViewHolder(
|
||||
viewModel: ListViewModel<*, *>,
|
||||
isLoading: Boolean,
|
||||
isNetworkError: Boolean,
|
||||
isOver: Boolean
|
||||
) {
|
||||
initFooterViewHolder(
|
||||
isLoading,
|
||||
isNetworkError,
|
||||
isOver,
|
||||
R.string.load_over_hint
|
||||
) { v: View? -> if (isNetworkError) viewModel.load(LoadType.RETRY) }
|
||||
}
|
||||
|
||||
fun bindFooterDefaultEmpty(
|
||||
viewModel: ListViewModel<*, *>,
|
||||
isLoading: Boolean,
|
||||
isNetworkError: Boolean,
|
||||
isOver: Boolean
|
||||
) {
|
||||
if (isNetworkError) {
|
||||
loading.visibility = View.GONE
|
||||
hint.visibility = View.VISIBLE
|
||||
hint.setText(R.string.loading_failed_retry)
|
||||
} else if (isOver) {
|
||||
loading.visibility = View.GONE
|
||||
hint.visibility = View.VISIBLE
|
||||
hint.setText(R.string.load_over_hint)
|
||||
} else if (isLoading) {
|
||||
loading.visibility = View.VISIBLE
|
||||
hint.visibility = View.VISIBLE
|
||||
hint.setText(R.string.loading)
|
||||
} else {
|
||||
loading.visibility = View.GONE
|
||||
hint.visibility = View.GONE
|
||||
}
|
||||
itemView.setOnClickListener { v: View? -> if (isNetworkError) viewModel.load(LoadType.RETRY) }
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun initFooterViewHolder(
|
||||
isLoading: Boolean,
|
||||
isNetworkError: Boolean,
|
||||
isOver: Boolean,
|
||||
@StringRes loadOverHint: Int = R.string.load_over_hint
|
||||
) {
|
||||
BaseActivity.updateStaticView(itemView, ArrayList())
|
||||
if (isNetworkError) {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(R.string.loading_failed_retry)
|
||||
} else if (isOver) {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(loadOverHint)
|
||||
} else if (isLoading) {
|
||||
loading.visibility = View.VISIBLE
|
||||
hint.setText(R.string.loading)
|
||||
} else {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(R.string.loading_more_hint)
|
||||
}
|
||||
}
|
||||
|
||||
fun initFooterViewHolder(
|
||||
isLoading: Boolean,
|
||||
mIsNetworkError: Boolean,
|
||||
mIsOver: Boolean,
|
||||
onClickListener: View.OnClickListener?
|
||||
) {
|
||||
initFooterViewHolder(
|
||||
isLoading,
|
||||
mIsNetworkError,
|
||||
mIsOver,
|
||||
R.string.load_over_hint,
|
||||
onClickListener
|
||||
)
|
||||
}
|
||||
|
||||
fun initFooterViewHolder(
|
||||
isLoading: Boolean,
|
||||
mIsNetworkError: Boolean,
|
||||
mIsOver: Boolean,
|
||||
@StringRes loadOverHint: Int,
|
||||
onClickListener: View.OnClickListener?
|
||||
) {
|
||||
if (mIsNetworkError) {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(R.string.loading_failed_retry)
|
||||
itemView.isClickable = true
|
||||
itemView.setOnClickListener(onClickListener)
|
||||
} else if (mIsOver) {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(loadOverHint)
|
||||
itemView.isClickable = true
|
||||
itemView.setOnClickListener(onClickListener)
|
||||
} else if (isLoading) {
|
||||
loading.visibility = View.VISIBLE
|
||||
hint.setText(R.string.loading)
|
||||
itemView.isClickable = false
|
||||
} else {
|
||||
loading.visibility = View.GONE
|
||||
hint.setText(R.string.loading_more_hint)
|
||||
itemView.isClickable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.databinding.RecyclerFollowHomeEmptyBinding
|
||||
|
||||
class FollowHomeEmptyViewHolder(val binding: RecyclerFollowHomeEmptyBinding) : ViewHolder(binding.root) {
|
||||
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.databinding.RecyclerFollowHomeHeaderBinding
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.adapter.FollowedHeaderAdapter
|
||||
|
||||
class FollowHomeHeaderViewHolder(
|
||||
val binding: RecyclerFollowHomeHeaderBinding,
|
||||
val listener: OnEventListener
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
private var data: List<FollowUserEntity> = listOf()
|
||||
|
||||
private val followedHeaderAdapter by lazy {
|
||||
FollowedHeaderAdapter(itemView.context, object : FollowedHeaderAdapter.OnChildEventListener {
|
||||
override fun onViewItem(position: Int) {
|
||||
listener.onItemClick(position, data)
|
||||
}
|
||||
|
||||
override fun onViewAll() {
|
||||
listener.onAllClick()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bind(isLogin: Boolean, followedList: List<FollowUserEntity>) {
|
||||
|
||||
fun setViewsVisible(tipsGone: Boolean, recyclerGone: Boolean, loginGone: Boolean) {
|
||||
binding.tvNoDataTips.goneIf(tipsGone)
|
||||
binding.rvFollowed.goneIf(recyclerGone)
|
||||
binding.gLogin.goneIf(loginGone)
|
||||
}
|
||||
|
||||
when {
|
||||
!isLogin -> {
|
||||
setViewsVisible(tipsGone = true, recyclerGone = true, loginGone = false)
|
||||
binding.tvLogin.setOnClickListener {
|
||||
listener.onLoginClick()
|
||||
}
|
||||
}
|
||||
|
||||
followedList.isEmpty() -> {
|
||||
setViewsVisible(tipsGone = false, recyclerGone = true, loginGone = true)
|
||||
}
|
||||
|
||||
else -> {
|
||||
setViewsVisible(tipsGone = true, recyclerGone = false, loginGone = true)
|
||||
if (binding.rvFollowed.adapter == null) {
|
||||
binding.rvFollowed.layoutManager =
|
||||
LinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false)
|
||||
binding.rvFollowed.adapter = followedHeaderAdapter
|
||||
}
|
||||
data = followedList
|
||||
followedHeaderAdapter.submitList(followedList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateReadStatus(ids: List<String>) {
|
||||
val oldData = followedHeaderAdapter.dataList
|
||||
val newData = oldData.toMutableList()
|
||||
oldData.forEachIndexed { index, followUserEntity ->
|
||||
if (ids.contains(followUserEntity.id)) {
|
||||
val newItem = followUserEntity.copy(_isShowTip = 0)
|
||||
newData[index] = newItem
|
||||
}
|
||||
}
|
||||
data = newData
|
||||
followedHeaderAdapter.submitList(newData)
|
||||
}
|
||||
|
||||
interface OnEventListener {
|
||||
|
||||
fun onItemClick(position: Int, data: List<FollowUserEntity>)
|
||||
|
||||
fun onAllClick()
|
||||
|
||||
fun onLoginClick()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.ItemHomeRecommendListCustomBinding
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentRecommendUi
|
||||
|
||||
/**
|
||||
* 通用内容合集-金刚区
|
||||
* 首页推荐入口
|
||||
*/
|
||||
|
||||
class FollowHomeRecommendItemViewHolder(
|
||||
val binding: ItemHomeRecommendListCustomBinding,
|
||||
private val listener: OnChildEventListener
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
private lateinit var data: FollowCommonContentCollection
|
||||
|
||||
private val recommendUi by lazy {
|
||||
CommonContentRecommendUi(binding, object : CommonContentRecommendUi.OnRecommendListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(
|
||||
bindingAdapterPosition,
|
||||
link,
|
||||
data.linkCommonCollection,
|
||||
text,
|
||||
exposureEvent
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: FollowCommonContentCollection) {
|
||||
this.data = data
|
||||
val recommends = data.linkCommonCollection.recommends
|
||||
recommendUi.bind(recommends, listOf())
|
||||
itemView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context))
|
||||
}
|
||||
|
||||
companion object {
|
||||
//图标最多列数
|
||||
private const val MAX_SPAN_COUNT = 5
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.HomeSlideListCustomBinding
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.OnViewHolderAttachListener
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentHomeSLideListUi
|
||||
|
||||
/**
|
||||
* 关注-通用内容合集-轮播Banner
|
||||
*/
|
||||
class FollowHomeSlideListViewHolder(
|
||||
val binding: HomeSlideListCustomBinding,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
listener: OnChildEventListener,
|
||||
) :
|
||||
ViewHolder(binding.root), OnViewHolderAttachListener {
|
||||
|
||||
private lateinit var data: FollowCommonContentCollection
|
||||
|
||||
private val slideListUi by lazy {
|
||||
CommonContentHomeSLideListUi(
|
||||
binding,
|
||||
lifecycleOwner,
|
||||
object : CommonContentHomeSLideListUi.OnCommonHomeSlideListEventListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(bindingAdapterPosition, link, data.linkCommonCollection, text, exposureEvent)
|
||||
}
|
||||
|
||||
override fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String) {
|
||||
listener.navigateToGameDetailPage(position, game, text)
|
||||
}
|
||||
|
||||
override fun updateImmersiveColor(color: Int) = Unit
|
||||
|
||||
override fun createExposureEvent(childPosition: Int, game: GameEntity?): ExposureEvent? = null
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: FollowCommonContentCollection) {
|
||||
this.data = data
|
||||
|
||||
val slideList = data.linkCommonCollection.slides.slide
|
||||
|
||||
slideListUi.bind(slideList, bindingAdapterPosition + 1)
|
||||
|
||||
itemView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context))
|
||||
|
||||
}
|
||||
|
||||
override fun onViewAttach(parent: RecyclerView?) {
|
||||
slideListUi.onViewAttach(parent)
|
||||
}
|
||||
|
||||
override fun onViewDetach(parent: RecyclerView?) {
|
||||
slideListUi.onViewDetach(parent)
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
)
|
||||
|
||||
fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.HomeSlideWithCardsCustomBinding
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection
|
||||
import com.gh.gamecenter.entity.HomeSubSlide
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.CommonContentCollectionUseCase
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentHomeSlideWithCardsUi
|
||||
|
||||
/**
|
||||
* 关注-通用内容合集-轮播Banner
|
||||
*/
|
||||
class FollowHomeSlideWithCardsViewHolder(
|
||||
val binding: HomeSlideWithCardsCustomBinding,
|
||||
private val useCase: CommonContentCollectionUseCase,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
private val listener: OnChildEventListener
|
||||
) : ViewHolder(binding.root) {
|
||||
private lateinit var data: FollowCommonContentCollection
|
||||
|
||||
private val slideWithCardsUi by lazy {
|
||||
CommonContentHomeSlideWithCardsUi(
|
||||
useCase,
|
||||
lifecycleOwner,
|
||||
binding,
|
||||
object : CommonContentHomeSlideWithCardsUi.HomeSLideWithCardsEventListener {
|
||||
override fun updateImmersiveColor(color: Int) = Unit
|
||||
|
||||
override fun createEventWithSourceConcat(game: GameEntity, subSlideId: String) = Unit
|
||||
|
||||
override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? = null
|
||||
|
||||
override fun addGameExposureEvent(position: Int, game: GameEntity, subSlideId: String) = Unit
|
||||
|
||||
override fun navigateToGameDetailPage(childPosition: Int, gameEntity: GameEntity, text: String) {
|
||||
listener.navigateToGameDetailPage(childPosition, gameEntity, text)
|
||||
}
|
||||
|
||||
override fun navigateToLinkPageInSubSlide(game: GameEntity, homeSubSlide: HomeSubSlide, text: String) {
|
||||
listener.navigateToLinkPage(
|
||||
bindingAdapterPosition,
|
||||
homeSubSlide.toLinkEntity(),
|
||||
data.linkCommonCollection,
|
||||
text,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(
|
||||
bindingAdapterPosition,
|
||||
link,
|
||||
data.linkCommonCollection,
|
||||
text,
|
||||
exposureEvent
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: FollowCommonContentCollection) {
|
||||
this.data = data
|
||||
slideWithCardsUi.bind(data.linkCommonCollection, bindingAdapterPosition + 1)
|
||||
itemView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context))
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun navigateToGameDetailPage(childPosition: Int, gameEntity: GameEntity, text: String)
|
||||
|
||||
fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.databinding.RecyclerInvalidBinding
|
||||
|
||||
class FollowInvalidViewHolder(val binding: RecyclerInvalidBinding) : ViewHolder(binding.root)
|
||||
@ -0,0 +1,57 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.RecyclerNavigationCustomBinding
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentNavigationUi
|
||||
|
||||
/**
|
||||
* 通用内容合集-导航栏
|
||||
*/
|
||||
class FollowNavigationViewHolder(
|
||||
val binding: RecyclerNavigationCustomBinding,
|
||||
private val listener: OnChildEventListener
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
private lateinit var data: FollowCommonContentCollection
|
||||
|
||||
private val navigationUi by lazy {
|
||||
CommonContentNavigationUi(binding, object : CommonContentNavigationUi.OnNavigationListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(
|
||||
bindingAdapterPosition,
|
||||
link,
|
||||
data.linkCommonCollection,
|
||||
text,
|
||||
exposureEvent
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: FollowCommonContentCollection) {
|
||||
this.data = data
|
||||
val navigationList = data.linkCommonCollection.navigations
|
||||
if (!navigationList.isNullOrEmpty()) {
|
||||
navigationUi.bind(navigationList, null)
|
||||
}
|
||||
itemView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context))
|
||||
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun navigateToLinkPage(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.forum.home.ForumArticleAskItemViewHolder
|
||||
import com.gh.gamecenter.forum.search.CommunitySearchEventListener
|
||||
import com.gh.gamecenter.forum.search.CommunitySearchEventListener.Companion.SEARCH_BUTTON_COMMENT
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
|
||||
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
|
||||
|
||||
class FollowPostCardViewHolder(
|
||||
val binding: CommunityAnswerItemBinding,
|
||||
val entrance: String,
|
||||
val path: String
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
private val context: Context
|
||||
get() = itemView.context
|
||||
|
||||
private val viewHolder = ForumArticleAskItemViewHolder(binding, null)
|
||||
|
||||
fun bind(data: ArticleEntity, position: Int) {
|
||||
val articleEntity = data
|
||||
|
||||
viewHolder.binding.run {
|
||||
root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||
topMargin = if (position == 0) 8F.dip2px() else 0
|
||||
}
|
||||
if (position == 0) {
|
||||
root.background = R.drawable.background_shape_white_radius_12_top_only.toDrawable(context)
|
||||
} else {
|
||||
root.setBackgroundColor(R.color.ui_surface.toColor(context))
|
||||
}
|
||||
rightContainer.setBackgroundColor(Color.TRANSPARENT)
|
||||
topLine.goneIf(position == 0)
|
||||
}
|
||||
// 关注-动态列表一定是已关注的数据
|
||||
articleEntity.me.isFollower = true
|
||||
articleEntity.brief = articleEntity.content
|
||||
viewHolder.bindForumArticleItem(articleEntity, entrance, path, position)
|
||||
|
||||
if (articleEntity.type == "question") {
|
||||
if (articleEntity.count.answer > 0) {
|
||||
viewHolder.commentCount.text = articleEntity.count.answer.toString()
|
||||
} else {
|
||||
viewHolder.commentCount.text = "回答"
|
||||
}
|
||||
viewHolder.commentCount.setDrawableStart(R.drawable.community_comment_count)
|
||||
viewHolder.voteCountContainer.visibility = View.GONE
|
||||
} else {
|
||||
viewHolder.voteCountContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
SensorsBridge.trackFollowPageContentClick("帖子", articleEntity.title)
|
||||
|
||||
when (articleEntity.type) {
|
||||
"community_article" -> {
|
||||
context.startActivity(
|
||||
ArticleDetailActivity.getRecommendIntent(
|
||||
context,
|
||||
articleEntity.community,
|
||||
articleEntity.id,
|
||||
articleEntity.recommendId,
|
||||
"",
|
||||
path,
|
||||
"社区-关注"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"video" -> {
|
||||
|
||||
context.startActivity(
|
||||
ForumVideoDetailActivity.getRecommendIntent(
|
||||
context,
|
||||
articleEntity.id,
|
||||
articleEntity.community.id,
|
||||
articleEntity.recommendId,
|
||||
"社区-关注"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"question" -> {
|
||||
context.startActivity(
|
||||
NewQuestionDetailActivity.getRecommendIntent(
|
||||
context,
|
||||
articleEntity.id,
|
||||
"",
|
||||
articleEntity.recommendId,
|
||||
"",
|
||||
path,
|
||||
sourceEntrance = "社区-关注"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
context.startActivity(
|
||||
NewQuestionDetailActivity.getRecommendIntent(
|
||||
context,
|
||||
articleEntity.questions.id,
|
||||
articleEntity.id,
|
||||
articleEntity.recommendId,
|
||||
"",
|
||||
path,
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.databinding.RecyclerFollowRecommendListBinding
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.feature.entity.UserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.adapter.FollowRecommendListAdapter
|
||||
|
||||
class FollowRecommendListViewHolder(
|
||||
val binding: RecyclerFollowRecommendListBinding,
|
||||
|
||||
listener: FollowRecommendListAdapter.OnChildEventListener
|
||||
) :
|
||||
ViewHolder(binding.root) {
|
||||
|
||||
private val adapter by lazy {
|
||||
FollowRecommendListAdapter(itemView.context, listener)
|
||||
}
|
||||
|
||||
fun bindView(data: List<UserEntity>) {
|
||||
if (binding.rvRecommends.adapter == null) {
|
||||
binding.rvRecommends.layoutManager =
|
||||
LinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false)
|
||||
binding.rvRecommends.adapter = adapter
|
||||
}
|
||||
adapter.submitList(data)
|
||||
|
||||
}
|
||||
|
||||
fun updateFollowed(change: EBUserFollow) {
|
||||
adapter.updateFollowed(change)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.NewsUtils
|
||||
import com.gh.common.view.ImageContainerView
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.debounceActionWithInterval
|
||||
import com.gh.gamecenter.common.utils.toArrayList
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.RecyclerGameArticleBinding
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.ConcernEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.SimpleGame
|
||||
|
||||
class GameArticleViewHolder(
|
||||
val binding: RecyclerGameArticleBinding,
|
||||
private val listener: OnChildEventListener
|
||||
) : ViewHolder(binding.root) {
|
||||
|
||||
fun bind(article: FollowDynamicEntity.Article) {
|
||||
val game = article.game
|
||||
binding.tvGameName.text = game.name
|
||||
binding.ivIcon.displayGameIcon(game)
|
||||
binding.tvTime.text = NewsUtils.getFormattedTime(article.time)
|
||||
binding.tvTitle.text = article.title
|
||||
binding.tvDescription.text = article.content
|
||||
|
||||
val images = article.img.take(1).map {
|
||||
ImageContainerView.ImageContainerData.ImageInfo(it, 3, 2)
|
||||
}
|
||||
val imageContainerData = ImageContainerView.ImageContainerData("", false, images, show = images.isNotEmpty())
|
||||
|
||||
binding.ivImagesContainer.bindData(imageContainerData,
|
||||
object : ImageContainerView.OnImageContainerEventListener {
|
||||
override fun onImageClick(
|
||||
images: List<String>,
|
||||
position: Int,
|
||||
imageViewList: ArrayList<SimpleDraweeView>
|
||||
) {
|
||||
val checkIntent = ImageViewerActivity.getIntent(
|
||||
binding.root.context,
|
||||
images.toArrayList(),
|
||||
position,
|
||||
imageViewList,
|
||||
""
|
||||
)
|
||||
binding.root.context.startActivity(checkIntent)
|
||||
}
|
||||
|
||||
override fun onVideoCLick(videoId: String) = Unit
|
||||
|
||||
})
|
||||
|
||||
arrayOf(binding.ivIcon, binding.tvTitle).forEach {
|
||||
it.setOnClickListener {
|
||||
listener.onGameCLick(game)
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
listener.onArticleClick(article)
|
||||
}
|
||||
|
||||
binding.tvComment.setOnClickListener {
|
||||
listener.onComment(article)
|
||||
}
|
||||
|
||||
binding.tvShare.setOnClickListener {
|
||||
debounceActionWithInterval(800) {
|
||||
listener.onShare(toConcernEntity(article))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun toConcernEntity(article: FollowDynamicEntity.Article): ConcernEntity {
|
||||
val simpleGame = SimpleGame().apply {
|
||||
mName = article.game.name
|
||||
}
|
||||
return ConcernEntity().apply {
|
||||
id = article.id
|
||||
title = article.title
|
||||
brief = article.content
|
||||
game = simpleGame
|
||||
gameIcon = article.game.icon
|
||||
img = article.img
|
||||
shortId = article.shortId
|
||||
}
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun onGameCLick(game: GameEntity)
|
||||
|
||||
fun onArticleClick(article: FollowDynamicEntity.Article)
|
||||
|
||||
fun onComment(article: FollowDynamicEntity.Article)
|
||||
|
||||
fun onShare(concernEntity: ConcernEntity)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.common.util.NewsUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.RecyclerGiftPackBinding
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.LibaoEntity
|
||||
import com.gh.gamecenter.feature.entity.MeEntity
|
||||
|
||||
class GiftPackViewHolder(
|
||||
val binding: RecyclerGiftPackBinding,
|
||||
val listener: OnChildEventListener
|
||||
) : ViewHolder(binding.root) {
|
||||
fun bind(
|
||||
libao: LibaoEntity,
|
||||
time: Long
|
||||
) {
|
||||
val game = libao.game?.toGameEntity() ?: return
|
||||
binding.ivIcon.displayGameIcon(game)
|
||||
binding.tvGameName.text = game.name
|
||||
binding.tvTime.text = NewsUtils.getFormattedTime(libao.time)
|
||||
binding.tvGiftPackName.text = libao.name
|
||||
binding.tvGiftPackContent.text = libao.content
|
||||
binding.gCode.goneIf(libao.code.isNullOrBlank()) {
|
||||
binding.tvCode.text = libao.code
|
||||
}
|
||||
|
||||
arrayOf(binding.ivIcon, binding.tvGameName).forEach {
|
||||
it.setOnClickListener {
|
||||
listener.onGameClick(game)
|
||||
}
|
||||
}
|
||||
binding.tvCopy.setOnClickListener {
|
||||
listener.onCopy(libao.code ?: "")
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
listener.onGiftPackageClick(libao)
|
||||
}
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun onGameClick(game: GameEntity)
|
||||
|
||||
fun onGiftPackageClick(libaoEntity: LibaoEntity)
|
||||
|
||||
fun onCopy(code: String)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewmodel
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.entity.FollowOperateTopRequest
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.AllFollowedNormalItem
|
||||
import com.gh.gamecenter.forum.home.follow.model.AllFollowRepository
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
class AllFollowedViewModel : ViewModel() {
|
||||
|
||||
private val repository = AllFollowRepository(RetrofitManager.getInstance().newApi)
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private var pageNo = 1
|
||||
|
||||
private val _dataList = MutableLiveData<List<AllFollowedNormalItem>>()
|
||||
val dataList: LiveData<List<AllFollowedNormalItem>> = _dataList
|
||||
|
||||
val hasReadSetIds = mutableSetOf<String>()
|
||||
|
||||
fun loadFirst() {
|
||||
pageNo = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun loadMore() {
|
||||
loadData()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
repository.loadMyFollowedUser(pageNo)
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<AllFollowedNormalItem>>() {
|
||||
override fun onSuccess(data: List<AllFollowedNormalItem>) {
|
||||
addData(data)
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private val _operateTopSuccessfully = MutableLiveData<Event<Unit>>()
|
||||
val operateTopSuccessfully: LiveData<Event<Unit>> = _operateTopSuccessfully
|
||||
|
||||
fun operateTop(topItem: List<FollowOperateTopRequest>) {
|
||||
repository.operateTop(topItem)
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
_operateTopSuccessfully.value = Event(Unit)
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun addData(newData: List<AllFollowedNormalItem>) {
|
||||
val oldData = dataList.value
|
||||
_dataList.value = if (oldData == null) {
|
||||
newData
|
||||
} else {
|
||||
oldData + newData
|
||||
}
|
||||
pageNo++
|
||||
}
|
||||
|
||||
private val _detailDestination = MutableLiveData<Event<FollowUserEntity>>()
|
||||
val detailDestination: LiveData<Event<FollowUserEntity>> = _detailDestination
|
||||
fun navigateToDetailPage(item: AllFollowedNormalItem) {
|
||||
val entity = item.data
|
||||
_detailDestination.value = Event(entity)
|
||||
val id = if (entity.isUser) {
|
||||
entity.user.id
|
||||
} else {
|
||||
entity.bbs.id
|
||||
}
|
||||
if (entity.isShowTip) {
|
||||
repository.postRead(entity.type, id ?: "")
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
hasReadSetIds.add(item.data.id)
|
||||
updateReadStatus(item)
|
||||
}
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun updateReadStatus(item: AllFollowedNormalItem) {
|
||||
val oldData = _dataList.value ?: return
|
||||
val position = oldData.indexOfFirst {
|
||||
item.data.id == it.data.id
|
||||
}
|
||||
val newData = oldData.toMutableList()
|
||||
val newItem = item.copy(data = item.data.copy(_isShowTip = 0))
|
||||
newData[position] = newItem
|
||||
_dataList.value = newData
|
||||
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewmodel
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.gh.common.util.ErrorHelper
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.observableToMain
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.forum.home.follow.FollowDynamicItem
|
||||
import com.gh.gamecenter.forum.home.follow.model.FollowDynamicRepository
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import retrofit2.HttpException
|
||||
|
||||
class FollowDynamicListViewModel : ViewModel() {
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private val repository = FollowDynamicRepository.newInstance()
|
||||
|
||||
private var pageNo = 1
|
||||
|
||||
private var userId: String = ""
|
||||
var bbsId: String = ""
|
||||
|
||||
private val _loadStatus = MutableLiveData<Pair<LoadStatus, Boolean>>()
|
||||
val loadStatus: LiveData<Pair<LoadStatus, Boolean>> = _loadStatus
|
||||
|
||||
private val _dataList = MutableLiveData<List<FollowDynamicItem>>()
|
||||
val dataList: LiveData<List<FollowDynamicItem>> = _dataList
|
||||
|
||||
fun loadFirst(userId: String, bbsId: String, isPullRefresh: Boolean) {
|
||||
pageNo = 1
|
||||
this.userId = userId
|
||||
this.bbsId = bbsId
|
||||
loadData(userId, bbsId, isPullRefresh)
|
||||
}
|
||||
|
||||
fun loadMore() {
|
||||
loadData(userId, bbsId, false)
|
||||
}
|
||||
|
||||
private fun loadData(userId: String, bbsId: String, isPullRefresh: Boolean) {
|
||||
if (pageNo == 1) {
|
||||
_loadStatus.value = LoadStatus.INIT_LOADING to isPullRefresh
|
||||
} else {
|
||||
_loadStatus.value = LoadStatus.LIST_LOADING to isPullRefresh
|
||||
}
|
||||
repository.loadData(userId, bbsId, pageNo)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<List<FollowDynamicItem>>() {
|
||||
|
||||
override fun onResponse(data: List<FollowDynamicItem>?) {
|
||||
if (data.isNullOrEmpty()) {
|
||||
if (pageNo == 1) {
|
||||
_loadStatus.value = LoadStatus.INIT_EMPTY to isPullRefresh
|
||||
} else {
|
||||
_loadStatus.value = LoadStatus.LIST_OVER to isPullRefresh
|
||||
}
|
||||
} else {
|
||||
if (pageNo == 1) {
|
||||
_loadStatus.value = LoadStatus.INIT_LOADED to isPullRefresh
|
||||
} else {
|
||||
_loadStatus.value = LoadStatus.LIST_LOADED to isPullRefresh
|
||||
}
|
||||
}
|
||||
if (pageNo == 1) {
|
||||
_dataList.value = data ?: listOf()
|
||||
pageNo++
|
||||
} else {
|
||||
addData(data ?: listOf())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
if (pageNo == 1) {
|
||||
_loadStatus.value = LoadStatus.INIT_FAILED to isPullRefresh
|
||||
} else {
|
||||
_loadStatus.value = LoadStatus.LIST_FAILED to isPullRefresh
|
||||
}
|
||||
ErrorHelper.handleError(HaloApp.getInstance().application, e?.response()?.errorBody()?.string())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun addData(newData: List<FollowDynamicItem>) {
|
||||
val oldData = dataList.value
|
||||
_dataList.value = if (oldData == null) {
|
||||
newData
|
||||
} else {
|
||||
oldData + newData
|
||||
}
|
||||
pageNo++
|
||||
}
|
||||
|
||||
|
||||
private val _personDetailDestination = MutableLiveData<Event<Pair<PersonalHistoryEntity, Int>>>()
|
||||
val personDetailDestination: LiveData<Event<Pair<PersonalHistoryEntity, Int>>> = _personDetailDestination
|
||||
fun navigateToPersonDetailPage(history: PersonalHistoryEntity, position: Int) {
|
||||
_personDetailDestination.value = Event(history to position)
|
||||
}
|
||||
|
||||
private val _bbsDetailDestination = MutableLiveData<Event<Pair<Int, AnswerEntity>>>()
|
||||
val bbsDetailDestination: LiveData<Event<Pair<Int, AnswerEntity>>> = _bbsDetailDestination
|
||||
fun navigateToBbsDetailPage(position: Int, answer: AnswerEntity) {
|
||||
_bbsDetailDestination.value = Event(position to answer)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewmodel
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.forum.home.follow.model.FollowDynamicRepository
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
|
||||
class FollowDynamicViewModel : ViewModel() {
|
||||
|
||||
private var userPageNo = 1
|
||||
|
||||
private val repository = FollowDynamicRepository.newInstance()
|
||||
|
||||
private val _followedUsers = MutableLiveData<Pair<Boolean, List<FollowUserEntity>>>()
|
||||
val followedUsers: LiveData<Pair<Boolean, List<FollowUserEntity>>> = _followedUsers
|
||||
|
||||
private val _isStartLoadAction = MutableLiveData<Boolean>()
|
||||
val isStartLoadAction: LiveData<Boolean> = _isStartLoadAction
|
||||
fun startLoad() {
|
||||
_isStartLoadAction.value = true
|
||||
}
|
||||
|
||||
fun addFirstPageUsers(users: List<FollowUserEntity>) {
|
||||
userPageNo = 1
|
||||
addData(users, true)
|
||||
}
|
||||
|
||||
private fun addData(newData: List<FollowUserEntity>, isFirstPage: Boolean) {
|
||||
val oldData = followedUsers.value?.second
|
||||
_followedUsers.value = isFirstPage to if (oldData == null) {
|
||||
newData
|
||||
} else {
|
||||
oldData + newData
|
||||
}
|
||||
userPageNo++
|
||||
}
|
||||
|
||||
private val _finishAction = MutableLiveData<Event<Unit>>()
|
||||
val finishAction: LiveData<Event<Unit>> = _finishAction
|
||||
|
||||
fun finish() {
|
||||
_finishAction.value = Event(Unit)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,316 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.retrofit.JSONObjectResponse
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.observableToMain
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.entity.FollowUserEntity
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.feature.entity.*
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.forum.home.follow.FollowArticleItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowPostCardItem
|
||||
import com.gh.gamecenter.forum.home.follow.FollowUserItem
|
||||
import com.gh.gamecenter.forum.home.follow.eventlistener.OnFollowHomeEventListener
|
||||
import com.gh.gamecenter.forum.home.follow.fragment.FollowHomeFragment.Companion.LOCATION_FOLLOW_PAGE
|
||||
import com.gh.gamecenter.forum.home.follow.model.FollowRepository
|
||||
import com.gh.gamecenter.home.custom.CommonContentCollectionUseCase
|
||||
import com.gh.gamecenter.home.custom.CustomPageTracker
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.commonContentLayoutToComponentName
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import retrofit2.HttpException
|
||||
|
||||
class FollowHomeViewModel(application: Application) : AndroidViewModel(application), OnFollowHomeEventListener {
|
||||
|
||||
private var pageNo = 1
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private val repository = FollowRepository.newInstance()
|
||||
|
||||
val commonContentCollectionUseCase = CommonContentCollectionUseCase.newInstance()
|
||||
|
||||
private var dynamicType = ""
|
||||
|
||||
var videoList = listOf<ForumVideoEntity>()
|
||||
|
||||
private val _dataList = MutableLiveData<List<FollowItem>>()
|
||||
val dataList: LiveData<List<FollowItem>> = _dataList
|
||||
|
||||
private val _loadStatus = MutableLiveData<Pair<LoadStatus, Boolean>>()
|
||||
val loadStatus: LiveData<Pair<LoadStatus, Boolean>> = _loadStatus
|
||||
|
||||
private val _hasFollowedUser = MutableLiveData<Boolean>()
|
||||
val hasFollowedUser: LiveData<Boolean> = _hasFollowedUser
|
||||
|
||||
fun loadFirst(isLogin: Boolean, type: String = "", isPullToRefresh: Boolean) {
|
||||
pageNo = 1
|
||||
dynamicType = type
|
||||
_loadStatus.value = LoadStatus.INIT_LOADING to isPullToRefresh
|
||||
repository.loadHomeData(isLogin, dynamicType, pageNo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<List<FollowItem>>() {
|
||||
override fun onSuccess(data: List<FollowItem>) {
|
||||
findVideoList(data)
|
||||
_hasFollowedUser.value = (data.firstOrNull() as? FollowUserItem)?.data?.isNotEmpty() == true
|
||||
_loadStatus.value = LoadStatus.INIT_LOADED to isPullToRefresh
|
||||
pageNo++
|
||||
_dataList.value = data
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
_loadStatus.value = LoadStatus.INIT_LOADED to isPullToRefresh
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
fun loadMore() {
|
||||
_loadStatus.value = LoadStatus.LIST_LOADING to false
|
||||
repository.loadMore(dynamicType, pageNo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<List<FollowItem>>() {
|
||||
override fun onSuccess(data: List<FollowItem>) {
|
||||
if (data.isNotEmpty()) {
|
||||
_loadStatus.value = LoadStatus.LIST_LOADED to false
|
||||
addData(data)
|
||||
} else {
|
||||
_loadStatus.value = LoadStatus.LIST_OVER to false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
_loadStatus.value = LoadStatus.LIST_FAILED to false
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun findVideoList(data: List<FollowItem>) {
|
||||
videoList = data.map {
|
||||
if (it is FollowPostCardItem) {
|
||||
it.data.transformForumVideoEntity()
|
||||
} else {
|
||||
ForumVideoEntity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFollowedUsers() {
|
||||
repository.loadFollowedUsers()
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<FollowUserEntity>>() {
|
||||
override fun onSuccess(data: List<FollowUserEntity>) {
|
||||
val oldData = _dataList.value ?: return
|
||||
val headItem = oldData.getOrNull(0)
|
||||
if (headItem is FollowUserItem) {
|
||||
val newHeadItem = headItem.copy(data = data)
|
||||
val newData = oldData.toMutableList()
|
||||
newData[0] = newHeadItem
|
||||
_dataList.value = newData
|
||||
}
|
||||
}
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun addData(newData: List<FollowItem>) {
|
||||
val oldData = dataList.value
|
||||
val data = if (oldData == null) {
|
||||
newData
|
||||
} else {
|
||||
oldData + newData
|
||||
}
|
||||
findVideoList(data)
|
||||
_dataList.value = data
|
||||
pageNo++
|
||||
}
|
||||
|
||||
|
||||
private val _followedDetailDestination = MutableLiveData<Event<Pair<Int, List<FollowUserEntity>>>>()
|
||||
val followedDetailDestination: LiveData<Event<Pair<Int, List<FollowUserEntity>>>> = _followedDetailDestination
|
||||
|
||||
override fun viewItemFollowed(position: Int, data: List<FollowUserEntity>) {
|
||||
val item = data.getOrNull(position) ?: return
|
||||
SensorsBridge.trackFollowPageUserAndForumDataClick(
|
||||
item.user.id ?: "",
|
||||
item.bbs.id,
|
||||
item.bbs.name,
|
||||
item.bbs.type
|
||||
)
|
||||
_followedDetailDestination.value = Event(position to data)
|
||||
}
|
||||
|
||||
|
||||
private val _myFollowedListDestination = MutableLiveData<Event<Unit>>()
|
||||
val myFollowedListDestination: LiveData<Event<Unit>> = _myFollowedListDestination
|
||||
override fun viewAllFollowed() {
|
||||
_myFollowedListDestination.value = Event(Unit)
|
||||
}
|
||||
|
||||
private val _loginDestination = MutableLiveData<Event<Unit>>()
|
||||
val loginDestination: LiveData<Event<Unit>> = _loginDestination
|
||||
override fun login() {
|
||||
_loginDestination.value = Event(Unit)
|
||||
}
|
||||
|
||||
override fun followUser(userId: String, isFollow: Boolean) {
|
||||
val isLogin = CheckLoginUtils.isLogin()
|
||||
if (isLogin) {
|
||||
repository.followUser(userId, isFollow)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
if (isFollow) {
|
||||
Utils.toast(getApplication(), R.string.concern_success)
|
||||
} else {
|
||||
Utils.toast(getApplication(), R.string.concern_cancel)
|
||||
}
|
||||
EventBus.getDefault().post(EBUserFollow(userId, isFollow))
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
Utils.toast(getApplication(), R.string.loading_failed_hint)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
login()
|
||||
}
|
||||
}
|
||||
|
||||
private val _linkDestination = MutableLiveData<Event<Pair<LinkEntity, ExposureEvent?>>>()
|
||||
val linkDestination: LiveData<Event<Pair<LinkEntity, ExposureEvent?>>> = _linkDestination
|
||||
|
||||
override fun navigateToLinkPageInCommonContent(
|
||||
position: Int,
|
||||
link: LinkEntity,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
SensorsBridge.trackLinkContentCollectionClick(
|
||||
LOCATION_FOLLOW_PAGE,
|
||||
"",
|
||||
"",
|
||||
data.name,
|
||||
data.id,
|
||||
position,
|
||||
link.type ?: "",
|
||||
link.link ?: "",
|
||||
link.text ?: "",
|
||||
text,
|
||||
commonContentLayoutToComponentName[data.layout] ?: "",
|
||||
)
|
||||
_linkDestination.value = Event(link to exposureEvent)
|
||||
}
|
||||
|
||||
private val _gameDetailDestination = MutableLiveData<Event<GameEntity>>()
|
||||
val gameDetailDestination: LiveData<Event<GameEntity>> = _gameDetailDestination
|
||||
override fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String) {
|
||||
_gameDetailDestination.value = Event(game)
|
||||
}
|
||||
|
||||
private val _commonCollectionDestination = MutableLiveData<Event<CustomPageData.CommonContentCollection>>()
|
||||
val commonCollectionDestination: LiveData<Event<CustomPageData.CommonContentCollection>> =
|
||||
_commonCollectionDestination
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection) {
|
||||
_commonCollectionDestination.value = Event(data)
|
||||
}
|
||||
|
||||
private val _userHomeDestination = MutableLiveData<Event<String>>()
|
||||
val userHomeDestination: LiveData<Event<String>> = _userHomeDestination
|
||||
override fun navigateToUserHomePage(userId: String) {
|
||||
_userHomeDestination.value = Event(userId)
|
||||
}
|
||||
|
||||
private val _postCardClickAction = MutableLiveData<Event<Pair<Int, ArticleEntity>>>()
|
||||
val postCardClickAction: LiveData<Event<Pair<Int, ArticleEntity>>> = _postCardClickAction
|
||||
override fun onPostCardClick(position: Int, article: ArticleEntity) {
|
||||
_postCardClickAction.value = Event(position to article)
|
||||
}
|
||||
|
||||
private val _libaoDetailDestination = MutableLiveData<Event<LibaoEntity>>()
|
||||
val libaoDetailDestination: LiveData<Event<LibaoEntity>> = _libaoDetailDestination
|
||||
override fun navigateToLibaoDetailPage(libaoEntity: LibaoEntity) {
|
||||
_libaoDetailDestination.value = Event(libaoEntity)
|
||||
}
|
||||
|
||||
private val _newDetailDestination = MutableLiveData<Event<String>>()
|
||||
val newDetailDestination: LiveData<Event<String>> = _newDetailDestination
|
||||
|
||||
private val _updateOkhttpCacheAction = MutableLiveData<Event<String>>()
|
||||
val updateOkhttpCacheAction: LiveData<Event<String>> = _updateOkhttpCacheAction
|
||||
|
||||
override fun navigateToNewsDetailPage(articleId: String) {
|
||||
_newDetailDestination.value = Event(articleId)
|
||||
// 统计文章阅读量
|
||||
repository.postArticleVisit(articleId)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : JSONObjectResponse() {
|
||||
|
||||
override fun onResponse(response: JSONObject) {
|
||||
super.onResponse(response)
|
||||
if (response.length() != 0) {
|
||||
try {
|
||||
if ("success" == response.getString("status")) {
|
||||
_updateOkhttpCacheAction.value = Event(articleId)
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private val _articleCommentDetailDestination = MutableLiveData<Event<String>>()
|
||||
val articleCommentDetailDestination: LiveData<Event<String>> = _articleCommentDetailDestination
|
||||
override fun navigateToArticleCommentDetailPage(articleId: String) {
|
||||
_articleCommentDetailDestination.value = Event(articleId)
|
||||
}
|
||||
|
||||
private val _shareArticleDestination = MutableLiveData<Event<ConcernEntity>>()
|
||||
val shareArticleDestination: LiveData<Event<ConcernEntity>> = _shareArticleDestination
|
||||
override fun onShareArticle(concernEntity: ConcernEntity) {
|
||||
_shareArticleDestination.value = Event(concernEntity)
|
||||
}
|
||||
|
||||
private val _copyExchangeCodeAction = MutableLiveData<Event<String>>()
|
||||
val copyExchangeCodeAction: LiveData<Event<String>> = _copyExchangeCodeAction
|
||||
override fun copyExchangeCode(code: String) {
|
||||
_copyExchangeCodeAction.value = Event(code)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.gh.gamecenter.home.custom
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.observableToMain
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.entity.HomeSubSlide
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Observable
|
||||
import retrofit2.HttpException
|
||||
|
||||
class CommonContentCollectionUseCase private constructor(
|
||||
private val api: ApiService
|
||||
) {
|
||||
var refreshCount = 0
|
||||
private var slideDiscoveryGamesPage = -1
|
||||
|
||||
private val _slideDiscoveryCardGames = MutableLiveData<List<GameEntity>>()
|
||||
val slideDiscoveryCardGames: LiveData<List<GameEntity>> = _slideDiscoveryCardGames
|
||||
fun loadSlideDiscoverCardGames(homeSubSlide: HomeSubSlide) {
|
||||
if (slideDiscoveryGamesPage != homeSubSlide.repeatCount || slideDiscoveryCardGames.value == null) {
|
||||
val paramsMap = if (SPUtils.getBoolean(Constants.SP_DISCOVER_FORCE_REFRESH)) {
|
||||
mapOf("page_size" to 3, "view" to "sub_slide", "refresh" to "true")
|
||||
} else {
|
||||
mapOf("page_size" to 3, "view" to "sub_slide")
|
||||
}
|
||||
api.getDiscoveryGames(1, paramsMap)
|
||||
.map { it.games }
|
||||
.map(RegionSettingHelper.filterGame)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<List<GameEntity>>() {
|
||||
override fun onResponse(response: List<GameEntity>?) {
|
||||
response?.let {
|
||||
SPUtils.setBoolean(Constants.SP_DISCOVER_FORCE_REFRESH, false)
|
||||
_slideDiscoveryCardGames.postValue(it)
|
||||
slideDiscoveryGamesPage = homeSubSlide.repeatCount
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance() =
|
||||
CommonContentCollectionUseCase(
|
||||
RetrofitManager.getInstance().api
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -56,6 +56,8 @@ class CustomPageViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
val commonContentCollectionUseCase = CommonContentCollectionUseCase.newInstance()
|
||||
|
||||
private val repository = CustomPageRepository.newInstance()
|
||||
|
||||
val gamePositionAndPackageHelper = GamePositionAndPackageHelper()
|
||||
@ -200,6 +202,7 @@ class CustomPageViewModel(
|
||||
|
||||
override fun onRefresh() {
|
||||
refreshCount++
|
||||
commonContentCollectionUseCase.refreshCount = refreshCount
|
||||
NewFlatLogUtils.logHomePagePullRefresh(refreshCount)
|
||||
}
|
||||
|
||||
@ -423,31 +426,6 @@ class CustomPageViewModel(
|
||||
_changeAppBarColorAction.value = Event(color)
|
||||
}
|
||||
|
||||
private val _slideDiscoveryCardGames = MutableLiveData<List<GameEntity>>()
|
||||
val slideDiscoveryCardGames: LiveData<List<GameEntity>> = _slideDiscoveryCardGames
|
||||
|
||||
override fun loadSlideDiscoverCardGames(homeSubSlide: HomeSubSlide) {
|
||||
if (slideDiscoveryGamesPage == homeSubSlide.repeatCount && _slideDiscoveryCardGames.value != null) {
|
||||
_slideDiscoveryCardGames.postValue(ArrayList(_slideDiscoveryCardGames.value!!))
|
||||
} else {
|
||||
repository.loadSlideDiscoverCardGames()
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<List<GameEntity>>() {
|
||||
override fun onResponse(response: List<GameEntity>?) {
|
||||
response?.let {
|
||||
SPUtils.setBoolean(Constants.SP_DISCOVER_FORCE_REFRESH, false)
|
||||
_slideDiscoveryCardGames.postValue(it)
|
||||
slideDiscoveryGamesPage = homeSubSlide.repeatCount
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
Utils.toast(getApplication(), "网络异常")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun addData(newData: List<CustomPageItem>) {
|
||||
val oldData = _dataList.value
|
||||
if (oldData.isNullOrEmpty()) {
|
||||
@ -634,7 +612,12 @@ class CustomPageViewModel(
|
||||
|
||||
private val _linkDestination = MutableLiveData<Event<Pair<LinkEntity, ExposureEvent?>>>()
|
||||
val linkDestination: LiveData<Event<Pair<LinkEntity, ExposureEvent?>>> = _linkDestination
|
||||
override fun navigateToLinkPage(item: CustomPageItem, link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
override fun navigateToLinkPage(
|
||||
item: CustomPageItem,
|
||||
link: LinkEntity,
|
||||
text: String,
|
||||
exposureEvent: ExposureEvent?
|
||||
) {
|
||||
_linkDestination.value = Event(Pair(link, exposureEvent))
|
||||
}
|
||||
|
||||
@ -650,7 +633,8 @@ class CustomPageViewModel(
|
||||
_badgeWallDestination.value = Event(comment)
|
||||
}
|
||||
|
||||
private val _gameDetailDestinationOnAmway = MutableLiveData<Event<Triple<CustomPageTrackData, String, ExposureEvent?>>>()
|
||||
private val _gameDetailDestinationOnAmway =
|
||||
MutableLiveData<Event<Triple<CustomPageTrackData, String, ExposureEvent?>>>()
|
||||
val gameDetailDestinationOnAmway: LiveData<Event<Triple<CustomPageTrackData, String, ExposureEvent?>>> =
|
||||
_gameDetailDestinationOnAmway
|
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.gh.gamecenter.home.custom
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
interface OnViewHolderAttachListener {
|
||||
|
||||
fun onViewAttach(parent: RecyclerView?) = Unit
|
||||
|
||||
|
||||
fun onViewDetach(parent: RecyclerView?) = Unit
|
||||
}
|
||||
@ -10,29 +10,27 @@ import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.CommonCollectionItemBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
import com.gh.gamecenter.entity.ExposureLinkEntity
|
||||
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD
|
||||
|
||||
class CustomCommonCollectionAdapter(
|
||||
context: Context,
|
||||
private val clickInvoke: (Int, CommonCollectionContentEntity) -> Unit,
|
||||
private val exposureInvoke: (Int, ExposureLinkEntity) -> Unit
|
||||
private val listener: OnEventListener
|
||||
) :
|
||||
CustomBaseChildAdapter<CommonCollectionContentEntity, CustomCommonCollectionAdapter.CustomCommonCollectionItemViewHolder>(
|
||||
context
|
||||
) {
|
||||
private var layout = 0
|
||||
|
||||
private lateinit var _data: CustomCommonContentCollectionItem
|
||||
|
||||
fun setData(data: CustomCommonContentCollectionItem) {
|
||||
this._data = data
|
||||
submitList(data.data.data)
|
||||
fun setData(data: CustomPageData.CommonContentCollection) {
|
||||
layout = data.layout
|
||||
submitList(data.data)
|
||||
}
|
||||
|
||||
override fun getKey(t: CommonCollectionContentEntity): String {
|
||||
return "${_data.data.layout}-${t.id}"
|
||||
return "${layout}-${t.id}"
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomCommonCollectionItemViewHolder {
|
||||
@ -40,7 +38,6 @@ class CustomCommonCollectionAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: CustomCommonCollectionItemViewHolder, position: Int) {
|
||||
val layout = _data.data.layout
|
||||
val item = getItem(position)
|
||||
var width = 0
|
||||
var height = 0
|
||||
@ -60,7 +57,7 @@ class CustomCommonCollectionAdapter(
|
||||
height = 80F.dip2px()
|
||||
}
|
||||
}
|
||||
exposureInvoke.invoke(position, item.linkEntity)
|
||||
listener.addExposureEvent(position, item.linkEntity)
|
||||
|
||||
holder.binding.apply {
|
||||
ImageUtils.display(commonCollectionImage, item.image)
|
||||
@ -99,7 +96,7 @@ class CustomCommonCollectionAdapter(
|
||||
root.layoutParams = rootParams
|
||||
|
||||
root.setOnClickListener {
|
||||
clickInvoke(position, item)
|
||||
listener.onChildItemClick(position, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,4 +111,11 @@ class CustomCommonCollectionAdapter(
|
||||
|
||||
class CustomCommonCollectionItemViewHolder(val binding: CommonCollectionItemBinding) :
|
||||
BaseRecyclerViewHolder<Any>(binding.root)
|
||||
|
||||
interface OnEventListener {
|
||||
|
||||
fun addExposureEvent(childPosition: Int, exposureLinkEntity: ExposureLinkEntity)
|
||||
|
||||
fun onChildItemClick(childPosition: Int, item: CommonCollectionContentEntity)
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import com.gh.common.exposure.ExposureTraceUtils
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.ExposureEntity
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.json.json
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
@ -25,7 +26,7 @@ import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventH
|
||||
|
||||
class CustomGameNavigationAdapter(
|
||||
context: Context,
|
||||
private val eventHelper: CommonContentCollectionEventHelper,
|
||||
private val listener: OnEventListener,
|
||||
) : CustomBaseChildAdapter<GameNavigationEntity, CustomGameNavigationAdapter.GameNavigationViewHolder>(context) {
|
||||
|
||||
private var recordMap = SPUtils.getMap(Constants.SP_GAME_NAVIGATION)
|
||||
@ -113,7 +114,7 @@ class CustomGameNavigationAdapter(
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
}
|
||||
eventHelper.navigateToLinkPage(it, "导航栏", exposureEvent)
|
||||
listener.navigateToLinkPage(it, "导航栏", exposureEvent)
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,4 +126,8 @@ class CustomGameNavigationAdapter(
|
||||
|
||||
class GameNavigationViewHolder(val binding: ItemGameNavigationCustomBinding) :
|
||||
BaseRecyclerViewHolder<RecyclerView.ViewHolder>(binding.root)
|
||||
|
||||
interface OnEventListener {
|
||||
fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?)
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import com.gh.common.exposure.ExposureTraceUtils
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.setDebouncedClickListener
|
||||
@ -28,7 +29,7 @@ import org.json.JSONObject
|
||||
* description :
|
||||
*/
|
||||
class CustomHomeRecommendItemGridAdapter(
|
||||
private val eventHelper: CommonContentCollectionEventHelper,
|
||||
private val listener: OnEventListener,
|
||||
) : BaseAdapter() {
|
||||
|
||||
private val dataList = arrayListOf<HomeRecommend>()
|
||||
@ -114,7 +115,16 @@ class CustomHomeRecommendItemGridAdapter(
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
}
|
||||
eventHelper.navigateToLinkPage(recommend.transformLinkEntity(), "金刚区", exposureEventList?.getOrNull(position))
|
||||
listener.navigateToLinkPage(
|
||||
recommend.transformLinkEntity(),
|
||||
"金刚区",
|
||||
exposureEventList?.getOrNull(position)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface OnEventListener {
|
||||
|
||||
fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?)
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,7 @@ import com.gh.common.databind.BindingAdapters
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.exposure.ExposureTraceUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.DataLogUtils
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
@ -21,7 +22,6 @@ import com.gh.gamecenter.entity.HomeSlide
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.viewholder.CustomHomeSlideListItemViewHolder
|
||||
import com.gh.gamecenter.home.custom.viewholder.CustomHomeSubSlideListItemViewHolder
|
||||
import com.lightgame.download.DownloadEntity
|
||||
@ -31,8 +31,7 @@ class CustomHomeSlideListAdapter(
|
||||
val snapHelper: PagerSnapHelper,
|
||||
private val layoutManager: LinearLayoutManager,
|
||||
private val hasCards: Boolean = false,
|
||||
private val eventHelper: CommonContentCollectionEventHelper,
|
||||
private val exposureInvoke: (Int, GameEntity?) -> ExposureEvent
|
||||
private val listener: OnEventListener
|
||||
) : CustomBaseChildAdapter<HomeSlide, ViewHolder>(context) {
|
||||
|
||||
private var recyclerView: RecyclerView? = null
|
||||
@ -85,14 +84,17 @@ class CustomHomeSlideListAdapter(
|
||||
var exposureEvent: ExposureEvent? = null
|
||||
if (homeSlide.linkType == "game") {
|
||||
runOnIoThread(true) {
|
||||
exposureEvent = exposureInvoke(actualPosition, game)
|
||||
exposureEvent = listener.createExposureEvent(actualPosition, game)
|
||||
}
|
||||
} else {
|
||||
val event = exposureInvoke(actualPosition, game)
|
||||
event.payload.controlType = "轮播图"
|
||||
event.payload.controlName = homeSlide.title
|
||||
event.payload.controlLinkName = homeSlide.linkText
|
||||
event.payload.controlLinkType = homeSlide.linkType
|
||||
val event = listener.createExposureEvent(actualPosition, game)
|
||||
?.also {
|
||||
it.payload.controlType = "轮播图"
|
||||
it.payload.controlName = homeSlide.title
|
||||
it.payload.controlLinkName = homeSlide.linkText
|
||||
it.payload.controlLinkType = homeSlide.linkType
|
||||
}
|
||||
|
||||
exposureEvent = event
|
||||
}
|
||||
game?.exposureEvent = exposureEvent
|
||||
@ -164,7 +166,7 @@ class CustomHomeSlideListAdapter(
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
}
|
||||
eventHelper.navigateToLinkPage(homeSlide.transformLinkEntity(), "轮播图", exposureEvent)
|
||||
listener.navigateToLinkPage(homeSlide.transformLinkEntity(), "轮播图", exposureEvent)
|
||||
}
|
||||
gameView.setOnClickListener {
|
||||
val actualPositionString = (actualPosition + 1).toString()
|
||||
@ -178,7 +180,7 @@ class CustomHomeSlideListAdapter(
|
||||
"新首页"
|
||||
)
|
||||
if (linkGame != null) {
|
||||
eventHelper.navigateToGameDetailPage(actualPosition, linkGame, "轮播图")
|
||||
listener.navigateToGameDetailPage(actualPosition, linkGame, "轮播图")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,8 +232,21 @@ class CustomHomeSlideListAdapter(
|
||||
}
|
||||
|
||||
fun getDataPosition(position: Int): Int {
|
||||
return position % dataCount
|
||||
return if (dataCount > 0) {
|
||||
position % dataCount
|
||||
} else {
|
||||
position
|
||||
}
|
||||
}
|
||||
|
||||
fun getActualSize() = dataList.size
|
||||
|
||||
interface OnEventListener {
|
||||
fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent?
|
||||
|
||||
fun navigateToGameDetailPage(actualPosition: Int, game: GameEntity, text: String)
|
||||
|
||||
fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?)
|
||||
|
||||
}
|
||||
}
|
||||
@ -33,8 +33,6 @@ interface OnCustomPageEventListener {
|
||||
|
||||
fun onChangeAppBarColor(color: Int)
|
||||
|
||||
fun loadSlideDiscoverCardGames(homeSubSlide: HomeSubSlide)
|
||||
|
||||
/**
|
||||
* 通过Game进入游戏详情
|
||||
*/
|
||||
|
||||
@ -351,7 +351,7 @@ class CustomPageData(
|
||||
@SerializedName("_id")
|
||||
private val _id: String? = null,
|
||||
@SerializedName("name")
|
||||
private val _name: String? = null,
|
||||
private var _name: String? = null,
|
||||
@SerializedName("detail_style")
|
||||
private val _detailStyle: String? = null,
|
||||
@SerializedName("layout")
|
||||
@ -375,8 +375,11 @@ class CustomPageData(
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
val name: String
|
||||
var name: String
|
||||
get() = _name ?: ""
|
||||
set(value) {
|
||||
_name = value
|
||||
}
|
||||
|
||||
val detailStyle: String
|
||||
get() = _detailStyle ?: ""
|
||||
|
||||
@ -0,0 +1,155 @@
|
||||
package com.gh.gamecenter.home.custom.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.databinding.CommonCollection12ItemCustomBinding
|
||||
import com.gh.gamecenter.databinding.CommonCollectionItemBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT
|
||||
import com.gh.gamecenter.home.custom.viewholder.CustomCommonCollectionViewHolder
|
||||
|
||||
/**
|
||||
* ui-双列banner 双列竖式卡片 竖排图文列表
|
||||
*/
|
||||
class CommonContentCollection12Ui(
|
||||
val binding: CommonCollection12ItemCustomBinding,
|
||||
private val listener: OnCollection12Listener
|
||||
) {
|
||||
|
||||
private val context: Context
|
||||
get() = binding.root.context
|
||||
|
||||
fun bind(
|
||||
collection: CustomPageData.CommonContentCollection,
|
||||
leftPosition: Int
|
||||
) {
|
||||
|
||||
val data = collection.data
|
||||
|
||||
val isFirstLine = leftPosition == 0
|
||||
binding.layoutTitle.root.goneIf(!isFirstLine) {
|
||||
binding.layoutTitle.tvTitle.setTextColor(R.color.text_primary.toColor(context))
|
||||
}
|
||||
|
||||
CustomCommonCollectionViewHolder.setTitle(collection, binding.layoutTitle) {
|
||||
listener.navigateToCommonCollectionDetailPage(collection)
|
||||
}
|
||||
|
||||
|
||||
val width = (DisplayUtils.getScreenWidth() - 16F.dip2px() * 2 - 8F.dip2px()) / 2
|
||||
val height = if (collection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) {
|
||||
width * 4 / 3
|
||||
} else {
|
||||
width / 2
|
||||
}
|
||||
|
||||
val leftBinding = CommonCollectionItemBinding.bind(binding.leftView.root)
|
||||
val rightBinding = CommonCollectionItemBinding.bind(binding.rightView.root)
|
||||
|
||||
|
||||
bindSubView(
|
||||
data[leftPosition],
|
||||
leftBinding,
|
||||
collection.layout,
|
||||
width,
|
||||
height,
|
||||
leftPosition
|
||||
)
|
||||
|
||||
val rightPosition = leftPosition + 1
|
||||
if (rightPosition < data.size) {
|
||||
binding.rightView.root.visibility = View.VISIBLE
|
||||
bindSubView(
|
||||
data[rightPosition],
|
||||
rightBinding,
|
||||
collection.layout,
|
||||
width,
|
||||
height,
|
||||
rightPosition
|
||||
)
|
||||
} else {
|
||||
binding.rightView.root.visibility = View.INVISIBLE
|
||||
|
||||
val imageParams = binding.rightView.commonCollectionImage.layoutParams as ConstraintLayout.LayoutParams
|
||||
imageParams.width = width
|
||||
imageParams.height = 1
|
||||
binding.rightView.commonCollectionImage.layoutParams = imageParams
|
||||
}
|
||||
|
||||
val isLastLine = leftPosition + 2 >= data.size
|
||||
val _paddingBottom =
|
||||
if (isLastLine || collection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) {
|
||||
16f.dip2px()
|
||||
} else {
|
||||
8f.dip2px()
|
||||
}
|
||||
with(binding.root) {
|
||||
setPadding(paddingLeft, paddingTop, paddingRight, _paddingBottom)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun bindSubView(
|
||||
contentEntity: CommonCollectionContentEntity,
|
||||
subBinding: CommonCollectionItemBinding,
|
||||
layout: Int,
|
||||
width: Int,
|
||||
height: Int,
|
||||
position: Int
|
||||
) {
|
||||
subBinding.run {
|
||||
ImageUtils.display(commonCollectionImage, contentEntity.image)
|
||||
maskView.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT || (contentEntity.title.isEmpty() && contentEntity.addedContent1.isNullOrEmpty()))
|
||||
titleTv.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) {
|
||||
titleTv.text = contentEntity.title
|
||||
}
|
||||
desTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) {
|
||||
desTv.text = contentEntity.addedContent1
|
||||
}
|
||||
linkTitleTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) {
|
||||
linkTitleTv.text = contentEntity.title
|
||||
}
|
||||
linkDes1.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) {
|
||||
linkDes1.text = contentEntity.addedContent1
|
||||
linkDes1.setTextColor(R.color.text_secondary.toColor(root.context))
|
||||
}
|
||||
linkDes2.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) {
|
||||
linkDes2.text = contentEntity.addedContent2
|
||||
linkDes2.setTextColor(R.color.text_tertiary.toColor(root.context))
|
||||
}
|
||||
|
||||
val maskHeight = if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) {
|
||||
60f.dip2px()
|
||||
} else {
|
||||
38f.dip2px()
|
||||
}
|
||||
val maskParams = maskView.layoutParams as ConstraintLayout.LayoutParams
|
||||
maskParams.height = maskHeight
|
||||
maskView.layoutParams = maskParams
|
||||
|
||||
val imageParams = commonCollectionImage.layoutParams as ConstraintLayout.LayoutParams
|
||||
imageParams.width = width
|
||||
imageParams.height = height
|
||||
commonCollectionImage.layoutParams = imageParams
|
||||
|
||||
root.setOnClickListener {
|
||||
listener.onChildItemClick(position, contentEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnCollection12Listener {
|
||||
fun onChildItemClick(childPosition: Int, contentEntity: CommonCollectionContentEntity)
|
||||
|
||||
fun navigateToCommonCollectionDetailPage(collection: CustomPageData.CommonContentCollection)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,280 @@
|
||||
package com.gh.gamecenter.home.custom.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.ScrollEventListener
|
||||
import com.gh.gamecenter.common.view.TouchSlopRecyclerView
|
||||
import com.gh.gamecenter.databinding.HomeSlideListCustomBinding
|
||||
import com.gh.gamecenter.entity.HomeSlide
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.BannerInRecyclerController
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomHomeSlideListAdapter
|
||||
import splitties.views.verticalPadding
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* 通用内容合集-轮播banner
|
||||
* ui逻辑封装,方便多个地方复用
|
||||
*/
|
||||
class CommonContentHomeSLideListUi(
|
||||
private val binding: HomeSlideListCustomBinding,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
private val listener: OnCommonHomeSlideListEventListener
|
||||
) {
|
||||
|
||||
private val context: Context
|
||||
get() = binding.root.context
|
||||
|
||||
private var _slideList: List<HomeSlide>? = null
|
||||
|
||||
private var _currentPosition = -1
|
||||
|
||||
private var mLastX: Int = 0
|
||||
private var mLastY: Int = 0
|
||||
|
||||
private val layoutManager by lazy {
|
||||
LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
private val snapHelper by lazy {
|
||||
PagerSnapHelper()
|
||||
}
|
||||
|
||||
private val scrollEventListener by lazy {
|
||||
ScrollEventListener(binding.recyclerView)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomHomeSlideListAdapter(
|
||||
context,
|
||||
snapHelper,
|
||||
layoutManager,
|
||||
false,
|
||||
object : CustomHomeSlideListAdapter.OnEventListener {
|
||||
override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? {
|
||||
return listener.createExposureEvent(actualPosition, game)
|
||||
}
|
||||
|
||||
override fun navigateToGameDetailPage(actualPosition: Int, game: GameEntity, text: String) {
|
||||
listener.navigateToGameDetailPage(actualPosition, game, text)
|
||||
}
|
||||
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private val bannerController = BannerInRecyclerController {
|
||||
adapter.scrollToNextPage()
|
||||
}
|
||||
|
||||
init {
|
||||
binding.recyclerView.itemAnimator = null
|
||||
lifecycleOwner.lifecycle.addObserver(bannerController)
|
||||
}
|
||||
|
||||
fun bind(slideList: List<HomeSlide>, position: Int) {
|
||||
val isFirst = position == 0
|
||||
val paddingVertical = if (isFirst) {
|
||||
8f.dip2px()
|
||||
} else {
|
||||
16f.dip2px()
|
||||
}
|
||||
binding.recyclerView.verticalPadding = paddingVertical
|
||||
if (binding.recyclerView.adapter == null) {
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
snapHelper.attachToRecyclerView(binding.recyclerView)
|
||||
binding.recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
val x = e.x.toInt()
|
||||
val y = e.y.toInt()
|
||||
|
||||
when (e.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mLastX = e.x.toInt()
|
||||
mLastY = e.y.toInt()
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> rv.parent.parent.requestDisallowInterceptTouchEvent(
|
||||
false
|
||||
)
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX: Int = x - mLastX
|
||||
val deltaY: Int = y - mLastY
|
||||
val isHorizontalScroll = abs(deltaX) > abs(deltaY)
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(isHorizontalScroll)
|
||||
}
|
||||
}
|
||||
|
||||
val isStop =
|
||||
e.action == MotionEvent.ACTION_DOWN || e.action == MotionEvent.ACTION_MOVE
|
||||
if (isStop) bannerController.pause() else bannerController.start()
|
||||
|
||||
val touchSlopRecyclerView = binding.recyclerView.parent.parent
|
||||
if (touchSlopRecyclerView is TouchSlopRecyclerView) {
|
||||
touchSlopRecyclerView.touchSlopEnabled = isStop
|
||||
} else throwExceptionInDebug("TouchSlopRecyclerView not found")
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
binding.recyclerView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
||||
requestTransform()
|
||||
}
|
||||
|
||||
binding.recyclerView.addOnScrollListener(scrollEventListener.apply {
|
||||
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
var lastStatePosition = -1
|
||||
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
super.onPageScrollStateChanged(state)
|
||||
|
||||
if (state == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
bannerController.start()
|
||||
} else {
|
||||
bannerController.pause()
|
||||
}
|
||||
|
||||
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
val view = snapHelper.findSnapView(layoutManager)
|
||||
var curPosition = 0
|
||||
if (view != null) {
|
||||
curPosition = adapter.getDataPosition(layoutManager.getPosition(view))
|
||||
_currentPosition = layoutManager.getPosition(view)
|
||||
}
|
||||
lastStatePosition = curPosition
|
||||
lastScrollState = scrollState
|
||||
listener.updateImmersiveColor(
|
||||
slideList.safelyGetInRelease(curPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
} else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||
lastScrollState = scrollState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
_currentPosition = position
|
||||
}
|
||||
|
||||
override fun onPageScrolled(
|
||||
position: Int,
|
||||
positionOffset: Float,
|
||||
positionOffsetPixels: Int
|
||||
) {
|
||||
_currentPosition = position
|
||||
val currentPosition = adapter.getDataPosition(position)
|
||||
val nextPosition = adapter.getDataPosition(position + 1)
|
||||
|
||||
val currentColor =
|
||||
slideList.getOrNull(currentPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
val nextColor =
|
||||
slideList.getOrNull(nextPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: currentColor
|
||||
|
||||
val colorInBetween =
|
||||
ColorUtils.blendARGB(currentColor, nextColor, positionOffset)
|
||||
|
||||
transformPage(position, positionOffset)
|
||||
listener.updateImmersiveColor(colorInBetween)
|
||||
}
|
||||
})
|
||||
})
|
||||
adapter.submitList(slideList, true)
|
||||
} else {
|
||||
if (_slideList != slideList) {
|
||||
adapter.checkResetData(slideList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_slideList = slideList
|
||||
val currentPosition = if (_currentPosition == -1) {
|
||||
adapter.getInitPosition()
|
||||
} else {
|
||||
_currentPosition
|
||||
}
|
||||
_currentPosition = currentPosition
|
||||
layoutManager.scrollToPosition(position)
|
||||
|
||||
val dataPosition = adapter.getDataPosition(currentPosition)
|
||||
listener.updateImmersiveColor(
|
||||
slideList.getOrNull(dataPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
bannerController.start()
|
||||
}
|
||||
|
||||
private fun requestTransform() {
|
||||
val relativePosition: Double = scrollEventListener.relativeScrollPosition
|
||||
val position = relativePosition.toInt()
|
||||
val positionOffset = (relativePosition - position).toFloat()
|
||||
transformPage(position, positionOffset)
|
||||
}
|
||||
|
||||
private fun transformPage(position: Int, positionOffset: Float) {
|
||||
val transformOffset = -positionOffset
|
||||
|
||||
for (i in 0 until layoutManager.childCount) {
|
||||
val view: View = layoutManager.getChildAt(i) ?: return
|
||||
val currPos: Int = layoutManager.getPosition(view)
|
||||
val viewOffset = transformOffset + (currPos - position)
|
||||
val scaleY = when {
|
||||
viewOffset <= -1 -> {
|
||||
0.9f
|
||||
}
|
||||
|
||||
viewOffset < 0 -> {
|
||||
(1 + viewOffset) * 0.1f + 0.9f
|
||||
}
|
||||
|
||||
viewOffset < 1 -> {
|
||||
(1 - viewOffset) * 0.1f + 0.9f
|
||||
}
|
||||
|
||||
else -> {
|
||||
0.9f
|
||||
}
|
||||
}
|
||||
view.scaleY = scaleY
|
||||
}
|
||||
}
|
||||
|
||||
fun onViewAttach(parent: RecyclerView?) {
|
||||
bannerController.onViewAttachedToWindow(parent)
|
||||
}
|
||||
|
||||
fun onViewDetach(parent: RecyclerView?) {
|
||||
bannerController.onViewDetachedFromWindow(parent)
|
||||
}
|
||||
|
||||
interface OnCommonHomeSlideListEventListener {
|
||||
|
||||
fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?)
|
||||
|
||||
fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String)
|
||||
|
||||
fun updateImmersiveColor(color: Int)
|
||||
|
||||
fun createExposureEvent(childPosition: Int, game: GameEntity?): ExposureEvent?
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,527 @@
|
||||
package com.gh.gamecenter.home.custom.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.FixLinearLayoutManager
|
||||
import com.gh.gamecenter.common.view.ScrollEventListener
|
||||
import com.gh.gamecenter.common.view.TouchSlopRecyclerView
|
||||
import com.gh.gamecenter.core.utils.AlphaGradientProcess
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.databinding.HomeSlideCardItemCustomBinding
|
||||
import com.gh.gamecenter.databinding.HomeSlideWithCardsCustomBinding
|
||||
import com.gh.gamecenter.entity.HomeSlide
|
||||
import com.gh.gamecenter.entity.HomeSubSlide
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.BannerInRecyclerController
|
||||
import com.gh.gamecenter.home.custom.CommonContentCollectionUseCase
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomHomeSlideListAdapter
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* 通用内容合集-轮播banner
|
||||
* ui逻辑封装,方便多个地方复用
|
||||
*/
|
||||
class CommonContentHomeSlideWithCardsUi(
|
||||
private val useCase: CommonContentCollectionUseCase,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
val binding: HomeSlideWithCardsCustomBinding,
|
||||
private val listener: HomeSLideWithCardsEventListener
|
||||
) {
|
||||
private val context: Context
|
||||
get() = binding.root.context
|
||||
|
||||
private val layoutManager by lazy {
|
||||
FixLinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
private val snapHelper by lazy {
|
||||
PagerSnapHelper()
|
||||
}
|
||||
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomHomeSlideListAdapter(
|
||||
context,
|
||||
snapHelper,
|
||||
layoutManager,
|
||||
true,
|
||||
object : CustomHomeSlideListAdapter.OnEventListener {
|
||||
override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? {
|
||||
return listener.createExposureEvent(actualPosition, game)
|
||||
}
|
||||
|
||||
override fun navigateToGameDetailPage(actualPosition: Int, game: GameEntity, text: String) {
|
||||
listener.navigateToGameDetailPage(actualPosition, game, text)
|
||||
}
|
||||
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private val bannerController = BannerInRecyclerController {
|
||||
adapter.scrollToNextPage()
|
||||
}
|
||||
|
||||
private var mLastX: Int = 0
|
||||
private var mLastY: Int = 0
|
||||
private var _currentPosition = -1
|
||||
|
||||
private var firstSubSlide: HomeSubSlide? = null
|
||||
private var secondSubSlide: HomeSubSlide? = null
|
||||
private var _slideList: List<HomeSlide>? = null
|
||||
|
||||
init {
|
||||
binding.recyclerView.itemAnimator = null
|
||||
lifecycleOwner.lifecycle.addObserver(bannerController)
|
||||
}
|
||||
|
||||
fun bind(collection: CustomPageData.CommonContentCollection, position: Int) {
|
||||
val isFirst = position == 0
|
||||
val bannerTopMargin = if (isFirst) {
|
||||
8f.dip2px()
|
||||
} else {
|
||||
16f.dip2px()
|
||||
}
|
||||
val layoutParams = binding.bannerCv.layoutParams as? ViewGroup.MarginLayoutParams
|
||||
layoutParams?.let {
|
||||
it.topMargin = bannerTopMargin
|
||||
it.bottomMargin = if (isFirst) 16f.dip2px() else 24f.dip2px()
|
||||
binding.bannerCv.layoutParams = it
|
||||
}
|
||||
|
||||
val rootLayoutParams = binding.root.layoutParams as? ViewGroup.MarginLayoutParams
|
||||
rootLayoutParams?.let {
|
||||
it.bottomMargin = -8f.dip2px()
|
||||
binding.root.layoutParams = it
|
||||
}
|
||||
bindSlide(collection.slides.slide)
|
||||
if (collection.slides.subSlide.isNotEmpty()) {
|
||||
bindCards(isFirst, collection, collection.slides.subSlide)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun bindSlide(
|
||||
slideList: List<HomeSlide>
|
||||
) {
|
||||
|
||||
if (binding.recyclerView.adapter == null) {
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
snapHelper.attachToRecyclerView(binding.recyclerView)
|
||||
binding.recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
val x = e.x.toInt()
|
||||
val y = e.y.toInt()
|
||||
|
||||
when (e.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mLastX = e.x.toInt()
|
||||
mLastY = e.y.toInt()
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> rv.parent.parent.requestDisallowInterceptTouchEvent(
|
||||
false
|
||||
)
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX: Int = x - mLastX
|
||||
val deltaY: Int = y - mLastY
|
||||
val isHorizontalScroll = abs(deltaX) > abs(deltaY)
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(isHorizontalScroll)
|
||||
}
|
||||
}
|
||||
|
||||
val isStop =
|
||||
e.action == MotionEvent.ACTION_DOWN || e.action == MotionEvent.ACTION_MOVE
|
||||
if (isStop) bannerController.pause() else bannerController.start()
|
||||
|
||||
val touchSlopRecyclerView = binding.recyclerView.parent.parent.parent
|
||||
if (touchSlopRecyclerView is TouchSlopRecyclerView) {
|
||||
touchSlopRecyclerView.touchSlopEnabled = isStop
|
||||
} else throwExceptionInDebug("TouchSlopRecyclerView not found")
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
|
||||
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
var lastStatePosition = -1
|
||||
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
_currentPosition = position
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
super.onPageScrollStateChanged(state)
|
||||
|
||||
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
val view = snapHelper.findSnapView(layoutManager)
|
||||
var curPosition = 0
|
||||
if (view != null) {
|
||||
curPosition = adapter.getDataPosition(layoutManager.getPosition(view))
|
||||
_currentPosition = layoutManager.getPosition(view)
|
||||
}
|
||||
if (lastScrollState == RecyclerView.SCROLL_STATE_DRAGGING && curPosition != lastStatePosition) {
|
||||
MtaHelper.onEvent(
|
||||
"首页_新",
|
||||
"轮播_滑动",
|
||||
if (curPosition > lastStatePosition) "左滑" else "右滑"
|
||||
)
|
||||
}
|
||||
lastStatePosition = curPosition
|
||||
lastScrollState = scrollState
|
||||
listener.updateImmersiveColor(
|
||||
slideList.safelyGetInRelease(curPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
} else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||
lastScrollState = scrollState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
|
||||
_currentPosition = position
|
||||
binding.bannerIndicator.onPageScrolled(
|
||||
adapter.getDataPosition(position),
|
||||
positionOffset
|
||||
)
|
||||
|
||||
val currentPosition = adapter.getDataPosition(position)
|
||||
val nextPosition = adapter.getDataPosition(position + 1)
|
||||
|
||||
val currentColor =
|
||||
slideList.safelyGetInRelease(currentPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
val nextColor =
|
||||
slideList.safelyGetInRelease(nextPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: currentColor
|
||||
|
||||
val colorInBetween =
|
||||
ColorUtils.blendARGB(currentColor, nextColor, positionOffset)
|
||||
|
||||
listener.updateImmersiveColor(colorInBetween)
|
||||
}
|
||||
})
|
||||
})
|
||||
adapter.submitList(slideList)
|
||||
} else {
|
||||
if (_slideList != slideList) {
|
||||
adapter.checkResetData(slideList)
|
||||
}
|
||||
}
|
||||
_slideList = slideList
|
||||
|
||||
val currentPosition = if (_currentPosition == -1) {
|
||||
adapter.getInitPosition()
|
||||
} else {
|
||||
_currentPosition
|
||||
}
|
||||
binding.recyclerView.scrollToPosition(currentPosition)
|
||||
|
||||
binding.bannerIndicator.run {
|
||||
pageSize = adapter.getActualSize()
|
||||
notifyDataChanged()
|
||||
}
|
||||
|
||||
val dataPosition = adapter.getDataPosition(currentPosition)
|
||||
listener.updateImmersiveColor(
|
||||
slideList.safelyGetInRelease(dataPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
|
||||
bannerController.start()
|
||||
|
||||
}
|
||||
|
||||
private fun bindCards(
|
||||
isFirst: Boolean,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
slideList: List<HomeSubSlide>
|
||||
) {
|
||||
val firstSubSlidePosition = (useCase.refreshCount * 2) % slideList.size
|
||||
val secondSubSlidePosition = (useCase.refreshCount * 2 + 1) % slideList.size
|
||||
slideList.getOrNull(firstSubSlidePosition)?.let {
|
||||
firstSubSlide = it
|
||||
it.sequence = firstSubSlidePosition
|
||||
it.repeatCount = (useCase.refreshCount * 2) / slideList.size
|
||||
bindCard(isFirst, data, binding.firstCv, it, 0)
|
||||
}
|
||||
slideList.getOrNull(secondSubSlidePosition)?.let {
|
||||
secondSubSlide = it
|
||||
it.sequence = secondSubSlidePosition
|
||||
it.repeatCount = (useCase.refreshCount * 2 + 1) / slideList.size
|
||||
bindCard(isFirst, data, binding.secondCv, it, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindCard(
|
||||
isFirst: Boolean,
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
homeSlideCardItemBinding: HomeSlideCardItemCustomBinding,
|
||||
homeSubSlide: HomeSubSlide,
|
||||
position: Int
|
||||
) {
|
||||
val cardCv = homeSlideCardItemBinding.root
|
||||
val cardContainer = homeSlideCardItemBinding.cardContainer
|
||||
val cardIv = homeSlideCardItemBinding.cardIv
|
||||
val backgroundGroup = homeSlideCardItemBinding.backgroundGroup
|
||||
val titleTv = homeSlideCardItemBinding.titleTv
|
||||
val descTv = homeSlideCardItemBinding.descTv
|
||||
val textContainer = homeSlideCardItemBinding.textContainer
|
||||
val countTv = homeSlideCardItemBinding.countTv
|
||||
val labelIv = homeSlideCardItemBinding.labelIv
|
||||
val gameIconStackIv1 = homeSlideCardItemBinding.gameIconStackIv1
|
||||
val gameIconStackIv2 = homeSlideCardItemBinding.gameIconStackIv2
|
||||
val gameIconStackIv3 = homeSlideCardItemBinding.gameIconStackIv3
|
||||
val gameIconStackGroup = listOf(gameIconStackIv1, gameIconStackIv2, gameIconStackIv3)
|
||||
val gameIconIv1 = homeSlideCardItemBinding.gameIconIv1
|
||||
val gameIconIv2 = homeSlideCardItemBinding.gameIconIv2
|
||||
val gameIconIv3 = homeSlideCardItemBinding.gameIconIv3
|
||||
val gameIconGroup = listOf(gameIconIv1, gameIconIv2, gameIconIv3)
|
||||
|
||||
titleTv.text = homeSubSlide.title
|
||||
titleTv.setTextColor(R.color.text_primary.toColor(titleTv.context))
|
||||
descTv.setTextColor(R.color.text_secondary.toColor(descTv.context))
|
||||
|
||||
listener.addGameExposureEvent(
|
||||
position,
|
||||
GameEntity(sequence = position, outerSequence = useCase.refreshCount),
|
||||
homeSubSlide.id
|
||||
)
|
||||
|
||||
// 获取发现卡片游戏
|
||||
if (homeSubSlide.cardType == "game_explore" && homeSubSlide.cardData.games.isEmpty()) {
|
||||
useCase.slideDiscoveryCardGames.observe(lifecycleOwner, object : Observer<List<GameEntity>> {
|
||||
override fun onChanged(t: List<GameEntity>?) {
|
||||
val currentSubSlide = if (position == 0) firstSubSlide else secondSubSlide
|
||||
if (t != null && homeSubSlide == currentSubSlide) {
|
||||
bindCard(
|
||||
isFirst,
|
||||
data,
|
||||
homeSlideCardItemBinding,
|
||||
homeSubSlide.apply { cardData.games = t },
|
||||
position
|
||||
)
|
||||
useCase.slideDiscoveryCardGames.removeObserver(this)
|
||||
}
|
||||
}
|
||||
})
|
||||
useCase.loadSlideDiscoverCardGames(homeSubSlide)
|
||||
return
|
||||
}
|
||||
|
||||
when (homeSubSlide.cardType) {
|
||||
"column",
|
||||
"column_test_v2",
|
||||
"server",
|
||||
"game_explore" -> {
|
||||
titleTv.visibility = View.VISIBLE
|
||||
descTv.visibility = View.VISIBLE
|
||||
countTv.visibility = View.GONE
|
||||
labelIv.visibility = View.GONE
|
||||
backgroundGroup.visibility = View.GONE
|
||||
gameIconStackGroup.forEach { it.isVisible = homeSubSlide.cardData.games.isNotEmpty() }
|
||||
gameIconGroup.forEach { it.visibility = View.GONE }
|
||||
|
||||
descTv.text = homeSubSlide.cardDesc
|
||||
if (homeSubSlide.cardData.games.size == 1) {
|
||||
gameIconStackIv2.visibility = View.GONE
|
||||
gameIconStackIv1.visibility = View.GONE
|
||||
}
|
||||
if (homeSubSlide.cardData.games.size == 2) {
|
||||
gameIconStackIv1.visibility = View.GONE
|
||||
}
|
||||
homeSubSlide.cardData.games.take(3).forEachIndexed { index, gameEntity ->
|
||||
listener.addGameExposureEvent(position, gameEntity, homeSubSlide.id)
|
||||
when (index) {
|
||||
0 -> gameIconStackIv3.displayGameIcon(gameEntity, true)
|
||||
1 -> gameIconStackIv2.displayGameIcon(gameEntity, true)
|
||||
2 -> gameIconStackIv1.displayGameIcon(gameEntity, true)
|
||||
}
|
||||
}
|
||||
ConstraintSet().apply {
|
||||
clone(cardContainer)
|
||||
clear(textContainer.id, ConstraintSet.TOP)
|
||||
clear(textContainer.id, ConstraintSet.BOTTOM)
|
||||
connect(textContainer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
|
||||
connect(textContainer.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
|
||||
}.applyTo(cardContainer)
|
||||
}
|
||||
|
||||
"game_list_detail" -> {
|
||||
titleTv.visibility = View.VISIBLE
|
||||
descTv.visibility = View.GONE
|
||||
backgroundGroup.visibility = View.GONE
|
||||
gameIconStackGroup.forEach { it.visibility = View.GONE }
|
||||
gameIconGroup.forEach { it.isVisible = homeSubSlide.cardData.games.isNotEmpty() }
|
||||
|
||||
if (homeSubSlide.cardData.games.size == 1) {
|
||||
gameIconIv2.visibility = View.GONE
|
||||
gameIconIv3.visibility = View.GONE
|
||||
}
|
||||
if (homeSubSlide.cardData.games.size == 2) {
|
||||
gameIconIv3.visibility = View.GONE
|
||||
}
|
||||
homeSubSlide.cardData.games.take(3).forEachIndexed { index, gameEntity ->
|
||||
listener.addGameExposureEvent(position, gameEntity, homeSubSlide.id)
|
||||
when (index) {
|
||||
0 -> {
|
||||
gameIconIv1.displayGameIcon(gameEntity, true)
|
||||
gameIconIv1.setOnClickListener {
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(
|
||||
homeSubSlide,
|
||||
useCase.refreshCount,
|
||||
"游戏"
|
||||
)
|
||||
listener.navigateToGameDetailPage(index, gameEntity, "右侧卡片")
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
gameIconIv2.displayGameIcon(gameEntity, true)
|
||||
gameIconIv2.setOnClickListener {
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(
|
||||
homeSubSlide,
|
||||
useCase.refreshCount,
|
||||
"游戏"
|
||||
)
|
||||
listener.navigateToGameDetailPage(index, gameEntity, "右侧卡片")
|
||||
}
|
||||
}
|
||||
|
||||
2 -> {
|
||||
gameIconIv3.displayGameIcon(gameEntity, true)
|
||||
gameIconIv3.setOnClickListener {
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(
|
||||
homeSubSlide,
|
||||
useCase.refreshCount,
|
||||
"游戏"
|
||||
)
|
||||
listener.navigateToGameDetailPage(index, gameEntity, "右侧卡片")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
countTv.isVisible = homeSubSlide.cardData.gameTotal.game > 3
|
||||
countTv.typeface = Typeface.createFromAsset(countTv.context.assets, Constants.DIN_FONT_PATH)
|
||||
countTv.text = "+${homeSubSlide.cardData.gameTotal.game - 3}"
|
||||
labelIv.isVisible = homeSubSlide.cardData.stamp.isNotEmpty()
|
||||
labelIv.setImageDrawable(
|
||||
when (homeSubSlide.cardData.stamp) {
|
||||
"official" -> R.drawable.label_official.toDrawable(labelIv.context)
|
||||
"special_choice" -> R.drawable.label_premium.toDrawable(labelIv.context)
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
ConstraintSet().apply {
|
||||
clone(cardContainer)
|
||||
clear(textContainer.id, ConstraintSet.TOP)
|
||||
clear(textContainer.id, ConstraintSet.BOTTOM)
|
||||
connect(
|
||||
textContainer.id,
|
||||
ConstraintSet.TOP,
|
||||
ConstraintSet.PARENT_ID,
|
||||
ConstraintSet.TOP,
|
||||
8F.dip2px()
|
||||
)
|
||||
if (homeSubSlide.cardData.stamp.isEmpty()) {
|
||||
clear(countTv.id, ConstraintSet.START)
|
||||
connect(countTv.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 10F.dip2px())
|
||||
} else {
|
||||
clear(countTv.id, ConstraintSet.END)
|
||||
connect(countTv.id, ConstraintSet.START, gameIconIv3.id, ConstraintSet.END, 4F.dip2px())
|
||||
}
|
||||
}.applyTo(cardContainer)
|
||||
}
|
||||
|
||||
"column_collection", "common_collection" -> {
|
||||
titleTv.visibility = View.VISIBLE
|
||||
descTv.visibility = View.VISIBLE
|
||||
countTv.visibility = View.GONE
|
||||
labelIv.visibility = View.GONE
|
||||
|
||||
backgroundGroup.visibility = View.VISIBLE
|
||||
val visibility = if (isFirst) View.VISIBLE else View.GONE
|
||||
homeSlideCardItemBinding.cardMask.visibility = visibility
|
||||
homeSlideCardItemBinding.cardGradientMask.visibility = visibility
|
||||
homeSlideCardItemBinding.cardIv.visibility = View.VISIBLE
|
||||
|
||||
gameIconStackGroup.forEach { it.visibility = View.GONE }
|
||||
gameIconGroup.forEach { it.visibility = View.GONE }
|
||||
descTv.text = homeSubSlide.cardDesc
|
||||
// 防止重复加载图片导致闪烁
|
||||
if (homeSubSlide.image != cardIv.getTag(R.string.tag_img_url_id)) {
|
||||
ImageUtils.display(cardIv, homeSubSlide.image, false, AlphaGradientProcess())
|
||||
cardIv.setTag(R.string.tag_img_url_id, homeSubSlide.image)
|
||||
}
|
||||
ConstraintSet().apply {
|
||||
clone(cardContainer)
|
||||
clear(textContainer.id, ConstraintSet.TOP)
|
||||
clear(textContainer.id, ConstraintSet.BOTTOM)
|
||||
connect(textContainer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
|
||||
connect(textContainer.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
|
||||
}.applyTo(cardContainer)
|
||||
}
|
||||
}
|
||||
|
||||
cardCv.setOnClickListener {
|
||||
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(homeSubSlide, useCase.refreshCount, "卡片")
|
||||
listener.navigateToLinkPageInSubSlide(
|
||||
GameEntity(sequence = position, outerSequence = useCase.refreshCount),
|
||||
homeSubSlide,
|
||||
"右侧卡片"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onViewAttach(parent: RecyclerView?) {
|
||||
bannerController.onViewAttachedToWindow(parent)
|
||||
}
|
||||
|
||||
fun onViewDetach(parent: RecyclerView?) {
|
||||
bannerController.onViewDetachedFromWindow(parent)
|
||||
}
|
||||
|
||||
interface HomeSLideWithCardsEventListener {
|
||||
fun updateImmersiveColor(color: Int)
|
||||
|
||||
fun createEventWithSourceConcat(game: GameEntity, subSlideId: String)
|
||||
|
||||
fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent?
|
||||
|
||||
fun addGameExposureEvent(position: Int, game: GameEntity, subSlideId: String)
|
||||
|
||||
fun navigateToGameDetailPage(childPosition: Int, gameEntity: GameEntity, text: String)
|
||||
|
||||
fun navigateToLinkPageInSubSlide(game: GameEntity, homeSubSlide: HomeSubSlide, text: String)
|
||||
|
||||
fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package com.gh.gamecenter.home.custom.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.view.SpacingItemDecoration
|
||||
import com.gh.gamecenter.databinding.CommonCollectionListCustomBinding
|
||||
import com.gh.gamecenter.databinding.LayoutTitleCustomBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
import com.gh.gamecenter.entity.ExposureLinkEntity
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomCommonCollectionAdapter
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
|
||||
/**
|
||||
* ui-通用内容合集-横排滑动Banner
|
||||
* ui-通用内容合集-横排竖式卡片
|
||||
* ui-通用内容合集-横排图文列表
|
||||
*/
|
||||
class CommonContentHorizontalSlideListUi(
|
||||
val binding: CommonCollectionListCustomBinding,
|
||||
private val listener: OnHorizontalSlideListListener
|
||||
) {
|
||||
|
||||
private val context: Context
|
||||
get() = binding.root.context
|
||||
|
||||
private val layoutManager by lazy {
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomCommonCollectionAdapter(context, object : CustomCommonCollectionAdapter.OnEventListener {
|
||||
override fun addExposureEvent(childPosition: Int, exposureLinkEntity: ExposureLinkEntity) {
|
||||
listener.addExposureEvent(childPosition, exposureLinkEntity)
|
||||
}
|
||||
|
||||
override fun onChildItemClick(childPosition: Int, item: CommonCollectionContentEntity) {
|
||||
listener.onChildItemClick(childPosition, item)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fun bind(data: CustomPageData.CommonContentCollection) {
|
||||
binding.layoutTitle.tvTitle.setTextColor(R.color.text_primary.toColor(context))
|
||||
setTitle(data, binding.layoutTitle) {
|
||||
listener.navigateToCommonCollectionDetailPage(data)
|
||||
}
|
||||
if (binding.collectionList.adapter == null) {
|
||||
binding.collectionList.addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
notDecorateTheFirstItem = true,
|
||||
left = 8f.dip2px()
|
||||
)
|
||||
)
|
||||
binding.collectionList.layoutManager = layoutManager
|
||||
binding.collectionList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
var position = layoutManager.findLastCompletelyVisibleItemPosition()
|
||||
if (position == -1) position = layoutManager.findLastVisibleItemPosition() - 1
|
||||
if (position < 0) return
|
||||
}
|
||||
}
|
||||
})
|
||||
binding.collectionList.adapter = adapter
|
||||
}
|
||||
adapter.setData(data)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun setTitle(
|
||||
data: CustomPageData.CommonContentCollection,
|
||||
layoutTitle: LayoutTitleCustomBinding,
|
||||
trackInvoke: () -> Unit
|
||||
) {
|
||||
with(layoutTitle) {
|
||||
lavRefresh.goneIf(true)
|
||||
gUser.goneIf(true)
|
||||
tvTitle.text = data.name
|
||||
|
||||
if (data.topRightText.isEmpty()) {
|
||||
tvRight.goneIf(true)
|
||||
} else {
|
||||
tvRight.goneIf(false)
|
||||
tvRight.text = data.topRightText
|
||||
tvRight.setOnClickListener {
|
||||
trackInvoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnHorizontalSlideListListener {
|
||||
|
||||
fun addExposureEvent(childPosition: Int, link: ExposureLinkEntity)
|
||||
|
||||
fun onChildItemClick(childPosition: Int, entity: CommonCollectionContentEntity)
|
||||
|
||||
fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.gh.gamecenter.home.custom.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.view.GridSpacingItemDecoration
|
||||
import com.gh.gamecenter.databinding.RecyclerNavigationCustomBinding
|
||||
import com.gh.gamecenter.entity.GameNavigationEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomGameNavigationAdapter
|
||||
|
||||
class CommonContentNavigationUi(
|
||||
val binding: RecyclerNavigationCustomBinding,
|
||||
private val listener: OnNavigationListener
|
||||
) {
|
||||
|
||||
private val context: Context
|
||||
get() = binding.root.context
|
||||
|
||||
private val layoutManager by lazy {
|
||||
GridLayoutManager(context, 4)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomGameNavigationAdapter(context, object : CustomGameNavigationAdapter.OnEventListener {
|
||||
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bind(navigationList: List<GameNavigationEntity>?, trackEventList: List<ExposureEvent>?) {
|
||||
|
||||
if (binding.rvNavigation.adapter == null) {
|
||||
binding.rvNavigation.layoutManager = layoutManager
|
||||
binding.rvNavigation.addItemDecoration(GridSpacingItemDecoration(4, 8f.dip2px(), false, 16f.dip2px()))
|
||||
binding.rvNavigation.adapter = adapter
|
||||
binding.rvNavigation.isNestedScrollingEnabled = false
|
||||
}
|
||||
|
||||
if (!navigationList.isNullOrEmpty()) {
|
||||
|
||||
adapter.setData(navigationList, trackEventList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface OnNavigationListener {
|
||||
|
||||
fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.gh.gamecenter.home.custom.ui
|
||||
|
||||
import android.widget.GridView
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.databinding.ItemHomeRecommendListCustomBinding
|
||||
import com.gh.gamecenter.entity.HomeRecommend
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomHomeRecommendItemGridAdapter
|
||||
import com.gh.gamecenter.home.custom.createExposureEvent
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
|
||||
/**
|
||||
* ui-通用内容合集-金刚区
|
||||
*/
|
||||
class CommonContentRecommendUi(
|
||||
val binding: ItemHomeRecommendListCustomBinding,
|
||||
private val listener: OnRecommendListener
|
||||
) {
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomHomeRecommendItemGridAdapter(object : CustomHomeRecommendItemGridAdapter.OnEventListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据处理
|
||||
* 当后台“日常管理(新)-首页推荐入口(新)/多渠道多版本-首页推荐入口”配置的可显示的内容≥10个时,前端需要支持展示两行首页推荐入口
|
||||
* 1, 如果 6≤推荐入口数量<10 ,前端均仅显示一行推荐入口的内容
|
||||
* 2, 如果 推荐入口数量<5 ,还是保留原有的规则平铺展示
|
||||
*/
|
||||
private fun mapData(recommends: List<HomeRecommend>): List<HomeRecommend> {
|
||||
val column = (recommends.size / MAX_SPAN_COUNT) // 显示几行
|
||||
val count = if (column == 0) {
|
||||
recommends.count()
|
||||
} else {
|
||||
column * MAX_SPAN_COUNT
|
||||
}
|
||||
return recommends.take(minOf(10, count))
|
||||
}
|
||||
|
||||
fun bind(recommends: List<HomeRecommend>, exposureList: List<ExposureEvent>?) {
|
||||
val filteredList = mapData(recommends)
|
||||
val size = recommends.size
|
||||
if (size == 0) return
|
||||
if (binding.recommendGv.adapter == null) {
|
||||
val spanCount = minOf(size, MAX_SPAN_COUNT)
|
||||
binding.recommendGv.stretchMode = GridView.STRETCH_SPACING
|
||||
binding.recommendGv.numColumns = spanCount
|
||||
binding.recommendGv.columnWidth = 60F.dip2px()
|
||||
}
|
||||
binding.recommendGv.adapter = adapter
|
||||
adapter.submitList(filteredList, exposureList)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
//图标最多列数
|
||||
private const val MAX_SPAN_COUNT = 5
|
||||
}
|
||||
|
||||
interface OnRecommendListener {
|
||||
|
||||
fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?)
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.PageConfigure
|
||||
import com.gh.gamecenter.home.custom.CustomPageViewModel
|
||||
import com.gh.gamecenter.home.custom.IGameChangedNotifier
|
||||
import com.gh.gamecenter.home.custom.OnViewHolderAttachListener
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CustomPageItemChildEventHelper
|
||||
import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper
|
||||
@ -29,7 +30,7 @@ import com.lightgame.download.DownloadEntity
|
||||
abstract class BaseCustomViewHolder(
|
||||
val viewModel: CustomPageViewModel,
|
||||
itemView: View
|
||||
) : RecyclerView.ViewHolder(itemView), IGameChangedNotifier, IExposable {
|
||||
) : RecyclerView.ViewHolder(itemView), IGameChangedNotifier, IExposable, OnViewHolderAttachListener {
|
||||
|
||||
protected val pageConfigure: PageConfigure
|
||||
get() = viewModel.pageConfigure
|
||||
@ -82,10 +83,6 @@ abstract class BaseCustomViewHolder(
|
||||
gameChangedNotifier?.notifyInstalled(busFour)
|
||||
}
|
||||
|
||||
open fun onViewAttach(parent: RecyclerView?) = Unit
|
||||
|
||||
open fun onViewDetach(parent: RecyclerView?) = Unit
|
||||
|
||||
fun setSplitSubjectTitle(
|
||||
titleBinding: LayoutTitleCustomBinding,
|
||||
item: CustomSplitSubjectItem,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.gh.gamecenter.home.custom.viewholder
|
||||
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.exposure.ExposureTraceUtils
|
||||
@ -11,7 +10,6 @@ import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.databinding.CommonCollection12ItemCustomBinding
|
||||
import com.gh.gamecenter.databinding.CommonCollectionItemBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
@ -21,10 +19,12 @@ import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.home.custom.CustomPageViewModel
|
||||
import com.gh.gamecenter.home.custom.createExposureEvent
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT
|
||||
import com.gh.gamecenter.home.custom.model.CustomSplitCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentCollection12Ui
|
||||
|
||||
/**
|
||||
* 双列banner 双列竖式卡片 竖排图文列表
|
||||
@ -40,46 +40,59 @@ class CustomCommonCollection12ViewHolder(
|
||||
CommonContentCollectionEventHelper(viewModel)
|
||||
}
|
||||
|
||||
private val clickInvoke = { childPosition: Int, contentEntity: CommonCollectionContentEntity ->
|
||||
val item = _item as CustomSplitCommonContentCollectionItem
|
||||
val collection = item.data
|
||||
val linkEntity = contentEntity.linkEntity
|
||||
NewLogUtils.logCommonCollectionClick(
|
||||
collection.id,
|
||||
collection.name,
|
||||
"blockData?.link",
|
||||
"blockData?.name",
|
||||
"板块内容列表",
|
||||
"合集首页",
|
||||
linkEntity.title ?: "",
|
||||
contentEntity.addedContent1 ?: "",
|
||||
contentEntity.addedContent2 ?: "",
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.text ?: "",
|
||||
childPosition + 1
|
||||
)
|
||||
NewLogUtils.logCommonCategoryHomeContentClick(
|
||||
contentEntity.title,
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.link ?: "",
|
||||
linkEntity.text ?: "",
|
||||
collection.name,
|
||||
collection.id,
|
||||
"板块",
|
||||
"blockData?.name"
|
||||
)
|
||||
item.exposureEventList.getOrNull(childPosition)?.let {
|
||||
val clickEvent = ExposureEvent(
|
||||
payload = it.payload,
|
||||
source = it.source,
|
||||
eTrace = ExposureTraceUtils.appendTrace(it),
|
||||
event = ExposureType.CLICK
|
||||
)
|
||||
if (linkEntity.type != "game") {
|
||||
ExposureManager.log(clickEvent)
|
||||
private val collection12Ui by lazy {
|
||||
CommonContentCollection12Ui(binding, object : CommonContentCollection12Ui.OnCollection12Listener {
|
||||
override fun onChildItemClick(childPosition: Int, contentEntity: CommonCollectionContentEntity) {
|
||||
val item = _item as CustomSplitCommonContentCollectionItem
|
||||
val collection = item.data
|
||||
val linkEntity = contentEntity.linkEntity
|
||||
NewLogUtils.logCommonCollectionClick(
|
||||
collection.id,
|
||||
collection.name,
|
||||
"blockData?.link",
|
||||
"blockData?.name",
|
||||
"板块内容列表",
|
||||
"合集首页",
|
||||
linkEntity.title ?: "",
|
||||
contentEntity.addedContent1 ?: "",
|
||||
contentEntity.addedContent2 ?: "",
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.text ?: "",
|
||||
childPosition + 1
|
||||
)
|
||||
NewLogUtils.logCommonCategoryHomeContentClick(
|
||||
contentEntity.title,
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.link ?: "",
|
||||
linkEntity.text ?: "",
|
||||
collection.name,
|
||||
collection.id,
|
||||
"板块",
|
||||
"blockData?.name"
|
||||
)
|
||||
item.exposureEventList.getOrNull(childPosition)?.let {
|
||||
val clickEvent = ExposureEvent(
|
||||
payload = it.payload,
|
||||
source = it.source,
|
||||
eTrace = ExposureTraceUtils.appendTrace(it),
|
||||
event = ExposureType.CLICK
|
||||
)
|
||||
if (linkEntity.type != "game") {
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
}
|
||||
childEventHelper.navigateToLinkPage(
|
||||
linkEntity,
|
||||
"内容卡片",
|
||||
item.exposureEventList.getOrNull(childPosition)
|
||||
)
|
||||
}
|
||||
}
|
||||
childEventHelper.navigateToLinkPage(linkEntity, "内容卡片", item.exposureEventList.getOrNull(childPosition))
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(collection: CustomPageData.CommonContentCollection) {
|
||||
childEventHelper.navigateToCommonCollectionDetailPage(_item.link)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun bindView(item: CustomPageItem) {
|
||||
@ -87,92 +100,45 @@ class CustomCommonCollection12ViewHolder(
|
||||
if (item is CustomSplitCommonContentCollectionItem) {
|
||||
|
||||
item.exposureEventList.clear()
|
||||
item.data.data.forEachIndexed { index, contentEntity ->
|
||||
val gameEntity = if (contentEntity.linkEntity.type == "game") {
|
||||
GameEntity(id = contentEntity.linkEntity.link, name = contentEntity.linkEntity.text)
|
||||
} else {
|
||||
GameEntity()
|
||||
}
|
||||
val event = createExposureEvent(
|
||||
gameEntity.also {
|
||||
it.sequence = index
|
||||
it.outerSequence = item.position
|
||||
},
|
||||
listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${item.data.name}+${item.data.layoutChinese}+${item.data.id}"
|
||||
)
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
index,
|
||||
item.componentPosition
|
||||
|
||||
).also { contentEntity.linkEntity.exposureEvent = it }
|
||||
item.exposureEventList.add(event)
|
||||
}
|
||||
|
||||
binding.layoutTitle.root.goneIf(!item.isFirstLine) {
|
||||
setTitleStyle(binding.layoutTitle)
|
||||
}
|
||||
|
||||
CustomCommonCollectionViewHolder.setTitle(item.data, binding.layoutTitle) {
|
||||
childEventHelper.navigateToCommonCollectionDetailPage(item.link)
|
||||
}
|
||||
val collection = item.data
|
||||
val data = collection.data
|
||||
|
||||
val width = (DisplayUtils.getScreenWidth() - 16F.dip2px() * 2 - 8F.dip2px()) / 2
|
||||
val height = if (collection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) {
|
||||
width * 4 / 3
|
||||
} else {
|
||||
width / 2
|
||||
}
|
||||
|
||||
val leftBinding = CommonCollectionItemBinding.bind(binding.leftView.root)
|
||||
val rightBinding = CommonCollectionItemBinding.bind(binding.rightView.root)
|
||||
|
||||
|
||||
bindSubView(
|
||||
data[item.leftPosition],
|
||||
leftBinding,
|
||||
collection.layout,
|
||||
width,
|
||||
height,
|
||||
item.leftPosition,
|
||||
clickInvoke
|
||||
)
|
||||
val data = item.data.data
|
||||
val leftPosition = item.leftPosition
|
||||
addExposureEvent(leftPosition, data[leftPosition])
|
||||
|
||||
val rightPosition = item.leftPosition + 1
|
||||
if (rightPosition < data.size) {
|
||||
binding.rightView.root.visibility = View.VISIBLE
|
||||
bindSubView(
|
||||
data[rightPosition],
|
||||
rightBinding,
|
||||
collection.layout,
|
||||
width,
|
||||
height,
|
||||
rightPosition,
|
||||
clickInvoke
|
||||
)
|
||||
addExposureEvent(rightPosition, data[rightPosition])
|
||||
}
|
||||
item.isFirstLine
|
||||
collection12Ui.bind(item.data, item.leftPosition)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun addExposureEvent(childPosition: Int, contentEntity: CommonCollectionContentEntity) {
|
||||
(_item as? CustomSplitCommonContentCollectionItem)?.let { item ->
|
||||
val gameEntity = if (contentEntity.linkEntity.type == "game") {
|
||||
GameEntity(id = contentEntity.linkEntity.link, name = contentEntity.linkEntity.text)
|
||||
} else {
|
||||
binding.rightView.root.visibility = View.INVISIBLE
|
||||
|
||||
val imageParams = binding.rightView.commonCollectionImage.layoutParams as ConstraintLayout.LayoutParams
|
||||
imageParams.width = width
|
||||
imageParams.height = 1
|
||||
binding.rightView.commonCollectionImage.layoutParams = imageParams
|
||||
GameEntity()
|
||||
}
|
||||
val event = createExposureEvent(
|
||||
gameEntity.also {
|
||||
it.sequence = childPosition
|
||||
it.outerSequence = item.position
|
||||
},
|
||||
listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${item.data.name}+${item.data.layoutChinese}+${item.data.id}"
|
||||
)
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
childPosition,
|
||||
item.componentPosition
|
||||
|
||||
val _paddingBottom =
|
||||
if (item.isLastLine || collection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) {
|
||||
16f.dip2px()
|
||||
} else {
|
||||
8f.dip2px()
|
||||
}
|
||||
with(binding.root) {
|
||||
setPadding(paddingLeft, paddingTop, paddingRight, _paddingBottom)
|
||||
}
|
||||
).also { contentEntity.linkEntity.exposureEvent = it }
|
||||
item.exposureEventList.add(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,26 +1,24 @@
|
||||
package com.gh.gamecenter.home.custom.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.exposure.ExposureTraceUtils
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.view.SpacingItemDecoration
|
||||
import com.gh.gamecenter.databinding.CommonCollectionListCustomBinding
|
||||
import com.gh.gamecenter.databinding.LayoutTitleCustomBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
import com.gh.gamecenter.entity.ExposureLinkEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.home.custom.CustomPageViewModel
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomCommonCollectionAdapter
|
||||
import com.gh.gamecenter.home.custom.createExposureEvent
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentHorizontalSlideListUi
|
||||
|
||||
/**
|
||||
* 通用内容合集-横排滑动Banner
|
||||
@ -33,108 +31,100 @@ class CustomCommonCollectionViewHolder(
|
||||
val binding: CommonCollectionListCustomBinding,
|
||||
) : BaseCustomViewHolder(viewModel, binding.root) {
|
||||
|
||||
private val layoutManager by lazy {
|
||||
LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) {
|
||||
CommonContentCollectionEventHelper(viewModel)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomCommonCollectionAdapter(itemView.context, { childPosition, entity ->
|
||||
val item = _item as CustomCommonContentCollectionItem
|
||||
val subject = item.data
|
||||
val linkEntity = entity.linkEntity
|
||||
private val horizontalSlideUi by lazy {
|
||||
CommonContentHorizontalSlideListUi(binding,
|
||||
object : CommonContentHorizontalSlideListUi.OnHorizontalSlideListListener {
|
||||
override fun addExposureEvent(childPosition: Int, link: ExposureLinkEntity) {
|
||||
(_item as? CustomCommonContentCollectionItem)?.let { item ->
|
||||
val gameEntity =
|
||||
if (link.type == "game") GameEntity(id = link.link, name = link.text) else GameEntity()
|
||||
_item.exposureEventList.add(
|
||||
createExposureEvent(
|
||||
gameEntity.also {
|
||||
it.sequence = childPosition
|
||||
it.outerSequence = _item.position
|
||||
},
|
||||
listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${item.data.name}+${item.data.layoutChinese}+${item.data.id}"
|
||||
)
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
childPosition,
|
||||
_item.componentPosition
|
||||
).also { link.exposureEvent = it }
|
||||
)
|
||||
}
|
||||
|
||||
NewLogUtils.logCommonCollectionClick(
|
||||
subject.id,
|
||||
subject.name,
|
||||
"blockData?.link",
|
||||
"blockData?.name",
|
||||
"板块内容列表",
|
||||
"合集首页",
|
||||
linkEntity.title ?: "",
|
||||
entity.addedContent1 ?: "",
|
||||
entity.addedContent2 ?: "",
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.text ?: "",
|
||||
childPosition + 1
|
||||
)
|
||||
NewLogUtils.logCommonCategoryHomeContentClick(
|
||||
entity.title,
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.link ?: "",
|
||||
linkEntity.text ?: "",
|
||||
subject.name,
|
||||
subject.id,
|
||||
"板块",
|
||||
"blockData?.name"
|
||||
)
|
||||
linkEntity.exposureEvent?.let {
|
||||
val clickEvent = ExposureEvent(
|
||||
payload = it.payload,
|
||||
source = it.source,
|
||||
eTrace = ExposureTraceUtils.appendTrace(it),
|
||||
event = ExposureType.CLICK
|
||||
)
|
||||
if (linkEntity.type != "game") {
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
}
|
||||
childEventHelper.navigateToLinkPage(
|
||||
linkEntity,
|
||||
"内容卡片",
|
||||
(_item as CustomCommonContentCollectionItem).exposureEventList.getOrNull(childPosition)
|
||||
)
|
||||
}) { childPosition, link ->
|
||||
val item = _item as CustomCommonContentCollectionItem
|
||||
val gameEntity = if (link.type == "game") GameEntity(id = link.link, name = link.text) else GameEntity()
|
||||
_item.exposureEventList.add(
|
||||
createExposureEvent(
|
||||
gameEntity.also {
|
||||
it.sequence = childPosition
|
||||
it.outerSequence = _item.position
|
||||
},
|
||||
listOf(ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}")),
|
||||
pageConfigure.exposureSourceList,
|
||||
childPosition,
|
||||
_item.componentPosition
|
||||
).also { link.exposureEvent = it }
|
||||
)
|
||||
}
|
||||
|
||||
override fun onChildItemClick(childPosition: Int, entity: CommonCollectionContentEntity) {
|
||||
(_item as? CustomCommonContentCollectionItem)?.let { item ->
|
||||
val subject = item.data
|
||||
val linkEntity = entity.linkEntity
|
||||
|
||||
NewLogUtils.logCommonCollectionClick(
|
||||
subject.id,
|
||||
subject.name,
|
||||
"blockData?.link",
|
||||
"blockData?.name",
|
||||
"板块内容列表",
|
||||
"合集首页",
|
||||
linkEntity.title ?: "",
|
||||
entity.addedContent1 ?: "",
|
||||
entity.addedContent2 ?: "",
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.text ?: "",
|
||||
childPosition + 1
|
||||
)
|
||||
NewLogUtils.logCommonCategoryHomeContentClick(
|
||||
entity.title,
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.link ?: "",
|
||||
linkEntity.text ?: "",
|
||||
subject.name,
|
||||
subject.id,
|
||||
"板块",
|
||||
"blockData?.name"
|
||||
)
|
||||
linkEntity.exposureEvent?.let {
|
||||
val clickEvent = ExposureEvent(
|
||||
payload = it.payload,
|
||||
source = it.source,
|
||||
eTrace = ExposureTraceUtils.appendTrace(it),
|
||||
event = ExposureType.CLICK
|
||||
)
|
||||
if (linkEntity.type != "game") {
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
}
|
||||
childEventHelper.navigateToLinkPage(
|
||||
linkEntity,
|
||||
"内容卡片",
|
||||
(_item as CustomCommonContentCollectionItem).exposureEventList.getOrNull(childPosition)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection) {
|
||||
childEventHelper.navigateToCommonCollectionDetailPage(_item.link)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun bindView(item: CustomPageItem) {
|
||||
super.bindView(item)
|
||||
if (item is CustomCommonContentCollectionItem) {
|
||||
item.exposureEventList.clear()
|
||||
|
||||
setTitleStyle(binding.layoutTitle)
|
||||
setTitle(item.data, binding.layoutTitle) {
|
||||
childEventHelper.navigateToCommonCollectionDetailPage(item.link)
|
||||
}
|
||||
if (binding.collectionList.adapter == null) {
|
||||
binding.collectionList.addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
notDecorateTheFirstItem = true,
|
||||
left = 8f.dip2px()
|
||||
)
|
||||
)
|
||||
binding.collectionList.layoutManager = layoutManager
|
||||
binding.collectionList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
var position = layoutManager.findLastCompletelyVisibleItemPosition()
|
||||
if (position == -1) position = layoutManager.findLastVisibleItemPosition() - 1
|
||||
if (position < 0) return
|
||||
}
|
||||
}
|
||||
})
|
||||
binding.collectionList.adapter = adapter
|
||||
}
|
||||
adapter.setData(item)
|
||||
horizontalSlideUi.bind(item.data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
package com.gh.gamecenter.home.custom.viewholder
|
||||
|
||||
import android.widget.GridView
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.databinding.ItemHomeRecommendListCustomBinding
|
||||
import com.gh.gamecenter.entity.HomeRecommend
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.CustomPageViewModel
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomHomeRecommendItemGridAdapter
|
||||
import com.gh.gamecenter.home.custom.createExposureEvent
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentRecommendUi
|
||||
|
||||
/**
|
||||
* 通用内容合集-金刚区
|
||||
@ -28,63 +27,44 @@ class CustomHomeRecommendItemViewHolder(
|
||||
CommonContentCollectionEventHelper(viewModel)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomHomeRecommendItemGridAdapter(childEventHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据处理
|
||||
* 当后台“日常管理(新)-首页推荐入口(新)/多渠道多版本-首页推荐入口”配置的可显示的内容≥10个时,前端需要支持展示两行首页推荐入口
|
||||
* 1, 如果 6≤推荐入口数量<10 ,前端均仅显示一行推荐入口的内容
|
||||
* 2, 如果 推荐入口数量<5 ,还是保留原有的规则平铺展示
|
||||
*/
|
||||
private fun mapData(recommends: List<HomeRecommend>): List<HomeRecommend> {
|
||||
_item.exposureEventList.clear()
|
||||
val item = _item as CustomCommonContentCollectionItem
|
||||
runOnIoThread(true) {
|
||||
recommends.forEachIndexed { index, homeRecommend ->
|
||||
val exposureEvent = createExposureEvent(
|
||||
GameEntity().also { it.sequence = index },
|
||||
listOf(
|
||||
ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"),
|
||||
ExposureSource("金刚区", homeRecommend.name)
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
index,
|
||||
_item.componentPosition
|
||||
)
|
||||
exposureEvent.payload.controlType = "推荐入口"
|
||||
exposureEvent.payload.controlName = homeRecommend.name
|
||||
exposureEvent.payload.controlLinkName = homeRecommend.linkText
|
||||
exposureEvent.payload.controlLinkType = homeRecommend.linkType
|
||||
homeRecommend.exposureEvent = exposureEvent
|
||||
_item.exposureEventList.add(exposureEvent)
|
||||
private val recommendUi by lazy {
|
||||
CommonContentRecommendUi(binding, object : CommonContentRecommendUi.OnRecommendListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
childEventHelper.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
}
|
||||
|
||||
val column = (recommends.size / MAX_SPAN_COUNT) // 显示几行
|
||||
val count = if (column == 0) {
|
||||
recommends.count()
|
||||
} else {
|
||||
column * MAX_SPAN_COUNT
|
||||
}
|
||||
return recommends.take(minOf(10, count))
|
||||
})
|
||||
}
|
||||
|
||||
override fun bindView(item: CustomPageItem) {
|
||||
super.bindView(item)
|
||||
if (item is CustomCommonContentCollectionItem) {
|
||||
val recommends = mapData(item.data.recommends)
|
||||
val size = recommends.size
|
||||
if (size == 0) return
|
||||
if (binding.recommendGv.adapter == null) {
|
||||
val spanCount = minOf(size, MAX_SPAN_COUNT)
|
||||
binding.recommendGv.stretchMode = GridView.STRETCH_SPACING
|
||||
binding.recommendGv.numColumns = spanCount
|
||||
binding.recommendGv.columnWidth = 60F.dip2px()
|
||||
val recommends = item.data.recommends
|
||||
_item.exposureEventList.clear()
|
||||
runOnIoThread(true) {
|
||||
item.data.recommends.forEachIndexed { index, homeRecommend ->
|
||||
val exposureEvent = createExposureEvent(
|
||||
GameEntity().also { it.sequence = index },
|
||||
listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${item.data.name}+${item.data.layoutChinese}+${item.data.id}"
|
||||
),
|
||||
ExposureSource("金刚区", homeRecommend.name)
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
index,
|
||||
_item.componentPosition
|
||||
)
|
||||
exposureEvent.payload.controlType = "推荐入口"
|
||||
exposureEvent.payload.controlName = homeRecommend.name
|
||||
exposureEvent.payload.controlLinkName = homeRecommend.linkText
|
||||
exposureEvent.payload.controlLinkType = homeRecommend.linkType
|
||||
homeRecommend.exposureEvent = exposureEvent
|
||||
_item.exposureEventList.add(exposureEvent)
|
||||
}
|
||||
}
|
||||
binding.recommendGv.adapter = adapter
|
||||
adapter.submitList(recommends, _item.exposureEventList)
|
||||
recommendUi.bind(recommends, _item.exposureEventList)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,32 +1,23 @@
|
||||
package com.gh.gamecenter.home.custom.viewholder
|
||||
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.view.DrawableView
|
||||
import com.gh.gamecenter.common.view.ScrollEventListener
|
||||
import com.gh.gamecenter.common.view.TouchSlopRecyclerView
|
||||
import com.gh.gamecenter.databinding.HomeSlideListCustomBinding
|
||||
import com.gh.gamecenter.entity.HomeSlide
|
||||
import com.gh.gamecenter.home.custom.BannerInRecyclerController
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.CustomPageViewModel
|
||||
import com.gh.gamecenter.home.custom.OnCustomPageRefreshStateListener
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomHomeSlideListAdapter
|
||||
import com.gh.gamecenter.home.custom.createExposureEvent
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
import splitties.views.verticalPadding
|
||||
import kotlin.math.abs
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentHomeSLideListUi
|
||||
|
||||
/**
|
||||
* 通用内容合集-轮播Banner
|
||||
@ -38,61 +29,59 @@ class CustomHomeSlideListViewHolder(
|
||||
) :
|
||||
BaseCustomViewHolder(viewModel, binding.root), OnCustomPageRefreshStateListener {
|
||||
|
||||
private var mLastX: Int = 0
|
||||
private var mLastY: Int = 0
|
||||
|
||||
private var _currentPosition = -1
|
||||
|
||||
// 是否是首位
|
||||
private val isFirst: Boolean
|
||||
get() = bindingAdapterPosition == 0
|
||||
|
||||
private var backgroundAlpha: Float = 1F
|
||||
|
||||
private val layoutManager by lazy {
|
||||
LinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
private val scrollEventListener by lazy {
|
||||
ScrollEventListener(binding.recyclerView)
|
||||
}
|
||||
|
||||
private val snapHelper by lazy {
|
||||
PagerSnapHelper()
|
||||
}
|
||||
|
||||
override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) {
|
||||
CommonContentCollectionEventHelper(viewModel)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomHomeSlideListAdapter(
|
||||
itemView.context, snapHelper, layoutManager, false,
|
||||
childEventHelper
|
||||
) { childPosition, game ->
|
||||
val item = _item as CustomCommonContentCollectionItem
|
||||
createExposureEvent(
|
||||
game,
|
||||
listOf(
|
||||
ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"),
|
||||
ExposureSource("轮播图", "")
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
childPosition,
|
||||
_item.componentPosition
|
||||
)
|
||||
}
|
||||
}
|
||||
private val slideListUi by lazy {
|
||||
CommonContentHomeSLideListUi(
|
||||
binding,
|
||||
lifecycleOwner,
|
||||
object : CommonContentHomeSLideListUi.OnCommonHomeSlideListEventListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
childEventHelper.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
|
||||
private val bannerController = BannerInRecyclerController {
|
||||
adapter.scrollToNextPage()
|
||||
}
|
||||
override fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String) {
|
||||
childEventHelper.navigateToGameDetailPage(position, game, text)
|
||||
}
|
||||
|
||||
private var _slideList: List<HomeSlide>? = null
|
||||
override fun updateImmersiveColor(color: Int) {
|
||||
if (pageConfigure.isInSearchToolbarTabWrapperPage) {
|
||||
if (isFirst) {
|
||||
viewModel.onChangeAppBarColor(color)
|
||||
}
|
||||
updateBackground(color)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
binding.recyclerView.itemAnimator = null
|
||||
lifecycleOwner.lifecycle.addObserver(bannerController)
|
||||
override fun createExposureEvent(childPosition: Int, game: GameEntity?): ExposureEvent? {
|
||||
val item = _item
|
||||
return if (item is CustomCommonContentCollectionItem) {
|
||||
createExposureEvent(
|
||||
game,
|
||||
listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${item.data.name}+${item.data.layoutChinese}+${item.data.id}"
|
||||
),
|
||||
ExposureSource("轮播图", "")
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
childPosition,
|
||||
_item.componentPosition
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun bindView(item: CustomPageItem) {
|
||||
@ -103,198 +92,19 @@ class CustomHomeSlideListViewHolder(
|
||||
|
||||
item.exposureEventList.clear()
|
||||
|
||||
val paddingVertical = if (isFirst) {
|
||||
8f.dip2px()
|
||||
} else {
|
||||
16f.dip2px()
|
||||
}
|
||||
binding.recyclerView.verticalPadding = paddingVertical
|
||||
|
||||
if (binding.recyclerView.adapter == null) {
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
snapHelper.attachToRecyclerView(binding.recyclerView)
|
||||
binding.recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
val x = e.x.toInt()
|
||||
val y = e.y.toInt()
|
||||
|
||||
when (e.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mLastX = e.x.toInt()
|
||||
mLastY = e.y.toInt()
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> rv.parent.parent.requestDisallowInterceptTouchEvent(
|
||||
false
|
||||
)
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX: Int = x - mLastX
|
||||
val deltaY: Int = y - mLastY
|
||||
val isHorizontalScroll = abs(deltaX) > abs(deltaY)
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(isHorizontalScroll)
|
||||
}
|
||||
}
|
||||
|
||||
val isStop =
|
||||
e.action == MotionEvent.ACTION_DOWN || e.action == MotionEvent.ACTION_MOVE
|
||||
if (isStop) bannerController.pause() else bannerController.start()
|
||||
|
||||
val touchSlopRecyclerView = binding.recyclerView.parent.parent
|
||||
if (touchSlopRecyclerView is TouchSlopRecyclerView) {
|
||||
touchSlopRecyclerView.touchSlopEnabled = isStop
|
||||
} else throwExceptionInDebug("TouchSlopRecyclerView not found")
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
binding.recyclerView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
||||
requestTransform()
|
||||
}
|
||||
|
||||
binding.recyclerView.addOnScrollListener(scrollEventListener.apply {
|
||||
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
var lastStatePosition = -1
|
||||
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
super.onPageScrollStateChanged(state)
|
||||
|
||||
if (state == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
bannerController.start()
|
||||
} else {
|
||||
bannerController.pause()
|
||||
}
|
||||
|
||||
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
val view = snapHelper.findSnapView(layoutManager)
|
||||
var curPosition = 0
|
||||
if (view != null) {
|
||||
curPosition = adapter.getDataPosition(layoutManager.getPosition(view))
|
||||
_currentPosition = layoutManager.getPosition(view)
|
||||
}
|
||||
lastStatePosition = curPosition
|
||||
lastScrollState = scrollState
|
||||
updateImmersiveColor(
|
||||
slideList.safelyGetInRelease(curPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
} else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||
lastScrollState = scrollState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
_currentPosition = position
|
||||
}
|
||||
|
||||
override fun onPageScrolled(
|
||||
position: Int,
|
||||
positionOffset: Float,
|
||||
positionOffsetPixels: Int
|
||||
) {
|
||||
_currentPosition = position
|
||||
val currentPosition = adapter.getDataPosition(position)
|
||||
val nextPosition = adapter.getDataPosition(position + 1)
|
||||
|
||||
val currentColor =
|
||||
slideList.safelyGetInRelease(currentPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
val nextColor =
|
||||
slideList.safelyGetInRelease(nextPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: currentColor
|
||||
|
||||
val colorInBetween =
|
||||
ColorUtils.blendARGB(currentColor, nextColor, positionOffset)
|
||||
|
||||
transformPage(position, positionOffset)
|
||||
updateImmersiveColor(colorInBetween)
|
||||
}
|
||||
})
|
||||
})
|
||||
adapter.submitList(slideList, true)
|
||||
} else {
|
||||
if (_slideList != slideList) {
|
||||
adapter.checkResetData(slideList)
|
||||
}
|
||||
|
||||
}
|
||||
_slideList = slideList
|
||||
|
||||
val currentPosition = if (_currentPosition == -1) {
|
||||
adapter.getInitPosition()
|
||||
} else {
|
||||
_currentPosition
|
||||
}
|
||||
_currentPosition = currentPosition
|
||||
binding.recyclerView.scrollToPosition(currentPosition)
|
||||
|
||||
val dataPosition = adapter.getDataPosition(currentPosition)
|
||||
updateImmersiveColor(
|
||||
slideList.safelyGetInRelease(dataPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
bannerController.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestTransform() {
|
||||
val relativePosition: Double = scrollEventListener.relativeScrollPosition
|
||||
val position = relativePosition.toInt()
|
||||
val positionOffset = (relativePosition - position).toFloat()
|
||||
transformPage(position, positionOffset)
|
||||
}
|
||||
|
||||
private fun transformPage(position: Int, positionOffset: Float) {
|
||||
val transformOffset = -positionOffset
|
||||
|
||||
for (i in 0 until layoutManager.childCount) {
|
||||
val view: View = layoutManager.getChildAt(i) ?: return
|
||||
val currPos: Int = layoutManager.getPosition(view)
|
||||
val viewOffset = transformOffset + (currPos - position)
|
||||
val scaleY = when {
|
||||
viewOffset <= -1 -> {
|
||||
0.9f
|
||||
}
|
||||
|
||||
viewOffset < 0 -> {
|
||||
(1 + viewOffset) * 0.1f + 0.9f
|
||||
}
|
||||
|
||||
viewOffset < 1 -> {
|
||||
(1 - viewOffset) * 0.1f + 0.9f
|
||||
}
|
||||
|
||||
else -> {
|
||||
0.9f
|
||||
}
|
||||
}
|
||||
view.scaleY = scaleY
|
||||
slideListUi.bind(slideList, bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewAttach(parent: RecyclerView?) {
|
||||
bannerController.onViewAttachedToWindow(parent)
|
||||
slideListUi.onViewAttach(parent)
|
||||
}
|
||||
|
||||
override fun onViewDetach(parent: RecyclerView?) {
|
||||
bannerController.onViewDetachedFromWindow(parent)
|
||||
}
|
||||
|
||||
private fun updateImmersiveColor(color: Int) {
|
||||
if (pageConfigure.isInSearchToolbarTabWrapperPage) {
|
||||
if (isFirst) {
|
||||
viewModel.onChangeAppBarColor(color)
|
||||
}
|
||||
updateBackground(color)
|
||||
}
|
||||
slideListUi.onViewDetach(parent)
|
||||
}
|
||||
|
||||
override fun onBackgroundAlphaChanged(alpha: Float) {
|
||||
backgroundAlpha = alpha
|
||||
binding.backgroundView.alpha = alpha
|
||||
}
|
||||
|
||||
|
||||
@ -1,48 +1,29 @@
|
||||
package com.gh.gamecenter.home.custom.viewholder
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.json.json
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.view.DrawableView
|
||||
import com.gh.gamecenter.common.view.FixLinearLayoutManager
|
||||
import com.gh.gamecenter.common.view.ScrollEventListener
|
||||
import com.gh.gamecenter.common.view.TouchSlopRecyclerView
|
||||
import com.gh.gamecenter.core.utils.AlphaGradientProcess
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.databinding.HomeSlideCardItemCustomBinding
|
||||
import com.gh.gamecenter.databinding.HomeSlideWithCardsCustomBinding
|
||||
import com.gh.gamecenter.entity.HomeSlide
|
||||
import com.gh.gamecenter.entity.HomeSubSlide
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.home.custom.BannerInRecyclerController
|
||||
import com.gh.gamecenter.home.custom.CustomPageViewModel
|
||||
import com.gh.gamecenter.home.custom.OnCustomPageRefreshStateListener
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomHomeSlideListAdapter
|
||||
import com.gh.gamecenter.home.custom.createExposureEvent
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
import kotlin.math.abs
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentHomeSlideWithCardsUi
|
||||
|
||||
/**
|
||||
* 通用内容合集-轮播banner
|
||||
@ -54,517 +35,133 @@ class CustomHomeSlideWithCardsViewHolder(
|
||||
val binding: HomeSlideWithCardsCustomBinding,
|
||||
) : BaseCustomViewHolder(viewModel, binding.root), OnCustomPageRefreshStateListener {
|
||||
|
||||
private var mLastX: Int = 0
|
||||
private var mLastY: Int = 0
|
||||
|
||||
private val hasCards = true
|
||||
|
||||
private var _currentPosition = -1
|
||||
|
||||
private val isFirst: Boolean
|
||||
get() = bindingAdapterPosition == 0
|
||||
|
||||
private var backgroundAlpha: Float = 1F
|
||||
|
||||
private val layoutManager by lazy {
|
||||
FixLinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
private val snapHelper by lazy {
|
||||
PagerSnapHelper()
|
||||
}
|
||||
|
||||
override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) {
|
||||
CommonContentCollectionEventHelper(viewModel)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomHomeSlideListAdapter(
|
||||
itemView.context, snapHelper, layoutManager, hasCards,
|
||||
childEventHelper
|
||||
) { childPosition, game ->
|
||||
val item = _item as CustomCommonContentCollectionItem
|
||||
createExposureEvent(
|
||||
game,
|
||||
listOf(
|
||||
ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"),
|
||||
ExposureSource("轮播图", "")
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
childPosition,
|
||||
_item.componentPosition
|
||||
)
|
||||
}
|
||||
}
|
||||
private val slideWithCardsUi by lazy {
|
||||
CommonContentHomeSlideWithCardsUi(
|
||||
viewModel.commonContentCollectionUseCase,
|
||||
lifecycleOwner,
|
||||
binding,
|
||||
object : CommonContentHomeSlideWithCardsUi.HomeSLideWithCardsEventListener {
|
||||
override fun updateImmersiveColor(color: Int) {
|
||||
this@CustomHomeSlideWithCardsViewHolder.updateImmersiveColor(color)
|
||||
}
|
||||
|
||||
private val bannerController = BannerInRecyclerController {
|
||||
adapter.scrollToNextPage()
|
||||
}
|
||||
override fun createEventWithSourceConcat(game: GameEntity, subSlideId: String) {
|
||||
(_item as? CustomCommonContentCollectionItem)?.let {
|
||||
ExposureEvent.createEventWithSourceConcat(
|
||||
game,
|
||||
basicSource = pageConfigure.exposureSourceList,
|
||||
source = listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${it.data.name}+${it.data.layoutChinese}+${it.data.id}"
|
||||
),
|
||||
ExposureSource("右侧卡片", subSlideId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private var firstSubSlide: HomeSubSlide? = null
|
||||
private var secondSubSlide: HomeSubSlide? = null
|
||||
private var _slideList: List<HomeSlide>? = null
|
||||
}
|
||||
|
||||
init {
|
||||
binding.recyclerView.itemAnimator = null
|
||||
lifecycleOwner.lifecycle.addObserver(bannerController)
|
||||
override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? {
|
||||
val item = _item
|
||||
return if (item is CustomCommonContentCollectionItem) {
|
||||
createExposureEvent(
|
||||
game,
|
||||
listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${item.data.name}+${item.data.layoutChinese}+${item.data.id}"
|
||||
),
|
||||
ExposureSource("轮播图", "")
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
actualPosition,
|
||||
_item.componentPosition
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun addGameExposureEvent(position: Int, game: GameEntity, subSlideId: String) {
|
||||
(_item as? CustomCommonContentCollectionItem)?.let {
|
||||
_item.exposureEventList.add(
|
||||
getGameExposureEvent(
|
||||
it.data,
|
||||
subSlideId,
|
||||
game,
|
||||
position,
|
||||
pageConfigure.exposureSourceList
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun navigateToGameDetailPage(childPosition: Int, gameEntity: GameEntity, text: String) {
|
||||
childEventHelper.navigateToGameDetailPage(childPosition, gameEntity, text)
|
||||
}
|
||||
|
||||
override fun navigateToLinkPageInSubSlide(
|
||||
game: GameEntity,
|
||||
homeSubSlide: HomeSubSlide,
|
||||
text: String
|
||||
) {
|
||||
(_item as? CustomCommonContentCollectionItem)?.let {
|
||||
val clickEvent = ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = game,
|
||||
basicSource = pageConfigure.exposureSourceList,
|
||||
source = listOf(
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${it.data.name}+${it.data.layoutChinese}+${it.data.id}"
|
||||
),
|
||||
ExposureSource("右侧卡片", homeSubSlide.id)
|
||||
),
|
||||
event = ExposureType.CLICK
|
||||
)
|
||||
val homeSubLink = homeSubSlide.toLinkEntity()
|
||||
if (homeSubLink.type != "game") {
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
SensorsBridge.trackEvent("RightSideCardClick", json {
|
||||
"position" to homeSubSlide.sequence
|
||||
"title" to homeSubSlide.title
|
||||
"card_id" to homeSubSlide.id
|
||||
})
|
||||
childEventHelper.navigateToLinkPage(homeSubLink, "右侧卡片", clickEvent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
childEventHelper.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun bindView(item: CustomPageItem) {
|
||||
super.bindView(item)
|
||||
if (item is CustomCommonContentCollectionItem) {
|
||||
|
||||
val bannerTopMargin = if (isFirst) {
|
||||
8f.dip2px()
|
||||
} else {
|
||||
16f.dip2px()
|
||||
}
|
||||
val layoutParams = binding.bannerCv.layoutParams as? MarginLayoutParams
|
||||
layoutParams?.let {
|
||||
it.topMargin = bannerTopMargin
|
||||
it.bottomMargin = if (isFirst) 16f.dip2px() else 24f.dip2px()
|
||||
binding.bannerCv.layoutParams = it
|
||||
}
|
||||
|
||||
val rootLayoutParams = itemView.layoutParams as? MarginLayoutParams
|
||||
rootLayoutParams?.let {
|
||||
it.bottomMargin = -8f.dip2px()
|
||||
itemView.layoutParams = it
|
||||
}
|
||||
bindSlide(item.data.slides.slide)
|
||||
bindCards(item, item.data.slides.subSlide)
|
||||
slideWithCardsUi.bind(item.data, bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun bindSlide(
|
||||
slideList: List<HomeSlide>
|
||||
) {
|
||||
|
||||
if (binding.recyclerView.adapter == null) {
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
snapHelper.attachToRecyclerView(binding.recyclerView)
|
||||
binding.recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
val x = e.x.toInt()
|
||||
val y = e.y.toInt()
|
||||
|
||||
when (e.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mLastX = e.x.toInt()
|
||||
mLastY = e.y.toInt()
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> rv.parent.parent.requestDisallowInterceptTouchEvent(
|
||||
false
|
||||
)
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX: Int = x - mLastX
|
||||
val deltaY: Int = y - mLastY
|
||||
val isHorizontalScroll = abs(deltaX) > abs(deltaY)
|
||||
rv.parent.parent.requestDisallowInterceptTouchEvent(isHorizontalScroll)
|
||||
}
|
||||
}
|
||||
|
||||
val isStop =
|
||||
e.action == MotionEvent.ACTION_DOWN || e.action == MotionEvent.ACTION_MOVE
|
||||
if (isStop) bannerController.pause() else bannerController.start()
|
||||
|
||||
val touchSlopRecyclerView = binding.recyclerView.parent.parent.parent
|
||||
if (touchSlopRecyclerView is TouchSlopRecyclerView) {
|
||||
touchSlopRecyclerView.touchSlopEnabled = isStop
|
||||
} else throwExceptionInDebug("TouchSlopRecyclerView not found")
|
||||
return false
|
||||
}
|
||||
})
|
||||
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
|
||||
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
var lastStatePosition = -1
|
||||
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
_currentPosition = position
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
super.onPageScrollStateChanged(state)
|
||||
|
||||
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
val view = snapHelper.findSnapView(layoutManager)
|
||||
var curPosition = 0
|
||||
if (view != null) {
|
||||
curPosition = adapter.getDataPosition(layoutManager.getPosition(view))
|
||||
_currentPosition = layoutManager.getPosition(view)
|
||||
}
|
||||
if (lastScrollState == RecyclerView.SCROLL_STATE_DRAGGING && curPosition != lastStatePosition) {
|
||||
// SensorsBridge.trackEvent("BannerSlide", json {
|
||||
// "position" to curPosition
|
||||
// "location" to location
|
||||
// })
|
||||
MtaHelper.onEvent(
|
||||
"首页_新",
|
||||
"轮播_滑动",
|
||||
if (curPosition > lastStatePosition) "左滑" else "右滑"
|
||||
)
|
||||
}
|
||||
lastStatePosition = curPosition
|
||||
lastScrollState = scrollState
|
||||
updateImmersiveColor(
|
||||
slideList.safelyGetInRelease(curPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
} else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||
lastScrollState = scrollState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
|
||||
_currentPosition = position
|
||||
binding.bannerIndicator.onPageScrolled(
|
||||
adapter.getDataPosition(position),
|
||||
positionOffset
|
||||
)
|
||||
|
||||
val currentPosition = adapter.getDataPosition(position)
|
||||
val nextPosition = adapter.getDataPosition(position + 1)
|
||||
|
||||
val currentColor =
|
||||
slideList.safelyGetInRelease(currentPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
val nextColor =
|
||||
slideList.safelyGetInRelease(nextPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: currentColor
|
||||
|
||||
val colorInBetween =
|
||||
ColorUtils.blendARGB(currentColor, nextColor, positionOffset)
|
||||
|
||||
updateImmersiveColor(colorInBetween)
|
||||
}
|
||||
})
|
||||
})
|
||||
adapter.submitList(slideList)
|
||||
} else {
|
||||
if (_slideList != slideList) {
|
||||
adapter.checkResetData(slideList)
|
||||
}
|
||||
}
|
||||
_slideList = slideList
|
||||
|
||||
val currentPosition = if (_currentPosition == -1) {
|
||||
adapter.getInitPosition()
|
||||
} else {
|
||||
_currentPosition
|
||||
}
|
||||
binding.recyclerView.scrollToPosition(currentPosition)
|
||||
|
||||
binding.bannerIndicator.run {
|
||||
pageSize = adapter.getActualSize()
|
||||
notifyDataChanged()
|
||||
}
|
||||
|
||||
val dataPosition = adapter.getDataPosition(currentPosition)
|
||||
updateImmersiveColor(
|
||||
slideList.safelyGetInRelease(dataPosition)?.placeholderColor?.hexStringToIntColor()
|
||||
?: R.color.ui_surface.toColor(binding.root.context)
|
||||
)
|
||||
|
||||
bannerController.start()
|
||||
|
||||
}
|
||||
|
||||
private fun bindCards(item: CustomCommonContentCollectionItem, slideList: List<HomeSubSlide>) {
|
||||
val firstSubSlidePosition = (viewModel.refreshCount * 2) % slideList.size
|
||||
val secondSubSlidePosition = (viewModel.refreshCount * 2 + 1) % slideList.size
|
||||
slideList.getOrNull(firstSubSlidePosition)?.let {
|
||||
firstSubSlide = it
|
||||
it.sequence = firstSubSlidePosition
|
||||
it.repeatCount = (viewModel.refreshCount * 2) / slideList.size
|
||||
bindCard(item, binding.firstCv, it, pageConfigure.exposureSourceList, 0)
|
||||
}
|
||||
slideList.getOrNull(secondSubSlidePosition)?.let {
|
||||
secondSubSlide = it
|
||||
it.sequence = secondSubSlidePosition
|
||||
it.repeatCount = (viewModel.refreshCount * 2 + 1) / slideList.size
|
||||
bindCard(item, binding.secondCv, it, pageConfigure.exposureSourceList, 1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCardTypeChinese(type: String) = when (type) {
|
||||
"column" -> "游戏专题"
|
||||
"column_collection" -> "专题合集"
|
||||
"column_test_v2" -> "新游开测"
|
||||
"server" -> "开服表"
|
||||
"game_explore" -> "发现页"
|
||||
"game_list_detail" -> "游戏单"
|
||||
"common_collection" -> "通用合集"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindCard(
|
||||
item: CustomCommonContentCollectionItem,
|
||||
homeSlideCardItemBinding: HomeSlideCardItemCustomBinding,
|
||||
homeSubSlide: HomeSubSlide,
|
||||
basicExposureSource: List<ExposureSource>,
|
||||
position: Int
|
||||
) {
|
||||
val cardCv = homeSlideCardItemBinding.root
|
||||
val cardContainer = homeSlideCardItemBinding.cardContainer
|
||||
val cardIv = homeSlideCardItemBinding.cardIv
|
||||
val backgroundGroup = homeSlideCardItemBinding.backgroundGroup
|
||||
val titleTv = homeSlideCardItemBinding.titleTv
|
||||
val descTv = homeSlideCardItemBinding.descTv
|
||||
val textContainer = homeSlideCardItemBinding.textContainer
|
||||
val countTv = homeSlideCardItemBinding.countTv
|
||||
val labelIv = homeSlideCardItemBinding.labelIv
|
||||
val gameIconStackIv1 = homeSlideCardItemBinding.gameIconStackIv1
|
||||
val gameIconStackIv2 = homeSlideCardItemBinding.gameIconStackIv2
|
||||
val gameIconStackIv3 = homeSlideCardItemBinding.gameIconStackIv3
|
||||
val gameIconStackGroup = listOf(gameIconStackIv1, gameIconStackIv2, gameIconStackIv3)
|
||||
val gameIconIv1 = homeSlideCardItemBinding.gameIconIv1
|
||||
val gameIconIv2 = homeSlideCardItemBinding.gameIconIv2
|
||||
val gameIconIv3 = homeSlideCardItemBinding.gameIconIv3
|
||||
val gameIconGroup = listOf(gameIconIv1, gameIconIv2, gameIconIv3)
|
||||
|
||||
titleTv.text = homeSubSlide.title
|
||||
titleTv.setTextColor(R.color.text_primary.toColor(titleTv.context))
|
||||
descTv.setTextColor(R.color.text_secondary.toColor(descTv.context))
|
||||
|
||||
val exposureEvent = ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = GameEntity().apply {
|
||||
sequence = position
|
||||
outerSequence = viewModel.refreshCount
|
||||
},
|
||||
basicSource = basicExposureSource,
|
||||
source = listOf(
|
||||
ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"),
|
||||
ExposureSource("右侧卡片", homeSubSlide.id)
|
||||
)
|
||||
)
|
||||
|
||||
// 获取发现卡片游戏
|
||||
if (homeSubSlide.cardType == "game_explore" && homeSubSlide.cardData.games.isEmpty()) {
|
||||
viewModel.slideDiscoveryCardGames.observe(lifecycleOwner, object : Observer<List<GameEntity>> {
|
||||
override fun onChanged(t: List<GameEntity>?) {
|
||||
val currentSubSlide = if (position == 0) firstSubSlide else secondSubSlide
|
||||
if (t != null && homeSubSlide == currentSubSlide) {
|
||||
bindCard(item, homeSlideCardItemBinding, homeSubSlide.apply {
|
||||
cardData.games = t
|
||||
}, basicExposureSource, position)
|
||||
viewModel.slideDiscoveryCardGames.removeObserver(this)
|
||||
}
|
||||
}
|
||||
})
|
||||
viewModel.loadSlideDiscoverCardGames(homeSubSlide)
|
||||
return
|
||||
}
|
||||
|
||||
when (homeSubSlide.cardType) {
|
||||
"column",
|
||||
"column_test_v2",
|
||||
"server",
|
||||
"game_explore" -> {
|
||||
titleTv.visibility = View.VISIBLE
|
||||
descTv.visibility = View.VISIBLE
|
||||
countTv.visibility = View.GONE
|
||||
labelIv.visibility = View.GONE
|
||||
backgroundGroup.visibility = View.GONE
|
||||
gameIconStackGroup.forEach { it.isVisible = homeSubSlide.cardData.games.isNotEmpty() }
|
||||
gameIconGroup.forEach { it.visibility = View.GONE }
|
||||
|
||||
descTv.text = homeSubSlide.cardDesc
|
||||
if (homeSubSlide.cardData.games.size == 1) {
|
||||
gameIconStackIv2.visibility = View.GONE
|
||||
gameIconStackIv1.visibility = View.GONE
|
||||
}
|
||||
if (homeSubSlide.cardData.games.size == 2) {
|
||||
gameIconStackIv1.visibility = View.GONE
|
||||
}
|
||||
homeSubSlide.cardData.games.take(3).forEachIndexed { index, gameEntity ->
|
||||
_item.exposureEventList.add(
|
||||
getGameExposureEvent(
|
||||
item.data,
|
||||
homeSubSlide,
|
||||
gameEntity,
|
||||
position,
|
||||
basicExposureSource
|
||||
)
|
||||
)
|
||||
when (index) {
|
||||
0 -> gameIconStackIv3.displayGameIcon(gameEntity, true)
|
||||
1 -> gameIconStackIv2.displayGameIcon(gameEntity, true)
|
||||
2 -> gameIconStackIv1.displayGameIcon(gameEntity, true)
|
||||
}
|
||||
}
|
||||
ConstraintSet().apply {
|
||||
clone(cardContainer)
|
||||
clear(textContainer.id, ConstraintSet.TOP)
|
||||
clear(textContainer.id, ConstraintSet.BOTTOM)
|
||||
connect(textContainer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
|
||||
connect(textContainer.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
|
||||
}.applyTo(cardContainer)
|
||||
}
|
||||
|
||||
"game_list_detail" -> {
|
||||
titleTv.visibility = View.VISIBLE
|
||||
descTv.visibility = View.GONE
|
||||
backgroundGroup.visibility = View.GONE
|
||||
gameIconStackGroup.forEach { it.visibility = View.GONE }
|
||||
gameIconGroup.forEach { it.isVisible = homeSubSlide.cardData.games.isNotEmpty() }
|
||||
|
||||
if (homeSubSlide.cardData.games.size == 1) {
|
||||
gameIconIv2.visibility = View.GONE
|
||||
gameIconIv3.visibility = View.GONE
|
||||
}
|
||||
if (homeSubSlide.cardData.games.size == 2) {
|
||||
gameIconIv3.visibility = View.GONE
|
||||
}
|
||||
homeSubSlide.cardData.games.take(3).forEachIndexed { index, gameEntity ->
|
||||
_item.exposureEventList.add(
|
||||
getGameExposureEvent(
|
||||
item.data,
|
||||
homeSubSlide,
|
||||
gameEntity,
|
||||
position,
|
||||
basicExposureSource
|
||||
)
|
||||
)
|
||||
|
||||
when (index) {
|
||||
0 -> {
|
||||
gameIconIv1.displayGameIcon(gameEntity, true)
|
||||
gameIconIv1.setOnClickListener {
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(
|
||||
homeSubSlide,
|
||||
viewModel.refreshCount,
|
||||
"游戏"
|
||||
)
|
||||
childEventHelper.navigateToGameDetailPage(index, gameEntity, "右侧卡片")
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
gameIconIv2.displayGameIcon(gameEntity, true)
|
||||
gameIconIv2.setOnClickListener {
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(
|
||||
homeSubSlide,
|
||||
viewModel.refreshCount,
|
||||
"游戏"
|
||||
)
|
||||
childEventHelper.navigateToGameDetailPage(index, gameEntity, "右侧卡片")
|
||||
}
|
||||
}
|
||||
|
||||
2 -> {
|
||||
gameIconIv3.displayGameIcon(gameEntity, true)
|
||||
gameIconIv3.setOnClickListener {
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(
|
||||
homeSubSlide,
|
||||
viewModel.refreshCount,
|
||||
"游戏"
|
||||
)
|
||||
childEventHelper.navigateToGameDetailPage(index, gameEntity, "右侧卡片")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
countTv.isVisible = homeSubSlide.cardData.gameTotal.game > 3
|
||||
countTv.typeface = Typeface.createFromAsset(countTv.context.assets, Constants.DIN_FONT_PATH)
|
||||
countTv.text = "+${homeSubSlide.cardData.gameTotal.game - 3}"
|
||||
labelIv.isVisible = homeSubSlide.cardData.stamp.isNotEmpty()
|
||||
labelIv.setImageDrawable(
|
||||
when (homeSubSlide.cardData.stamp) {
|
||||
"official" -> R.drawable.label_official.toDrawable(labelIv.context)
|
||||
"special_choice" -> R.drawable.label_premium.toDrawable(labelIv.context)
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
ConstraintSet().apply {
|
||||
clone(cardContainer)
|
||||
clear(textContainer.id, ConstraintSet.TOP)
|
||||
clear(textContainer.id, ConstraintSet.BOTTOM)
|
||||
connect(
|
||||
textContainer.id,
|
||||
ConstraintSet.TOP,
|
||||
ConstraintSet.PARENT_ID,
|
||||
ConstraintSet.TOP,
|
||||
8F.dip2px()
|
||||
)
|
||||
if (homeSubSlide.cardData.stamp.isEmpty()) {
|
||||
clear(countTv.id, ConstraintSet.START)
|
||||
connect(countTv.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 10F.dip2px())
|
||||
} else {
|
||||
clear(countTv.id, ConstraintSet.END)
|
||||
connect(countTv.id, ConstraintSet.START, gameIconIv3.id, ConstraintSet.END, 4F.dip2px())
|
||||
}
|
||||
}.applyTo(cardContainer)
|
||||
}
|
||||
|
||||
"column_collection", "common_collection" -> {
|
||||
titleTv.visibility = View.VISIBLE
|
||||
descTv.visibility = View.VISIBLE
|
||||
countTv.visibility = View.GONE
|
||||
labelIv.visibility = View.GONE
|
||||
|
||||
backgroundGroup.visibility = View.VISIBLE
|
||||
val visibility = if (isFirst) View.VISIBLE else View.GONE
|
||||
homeSlideCardItemBinding.cardMask.visibility = visibility
|
||||
homeSlideCardItemBinding.cardGradientMask.visibility = visibility
|
||||
homeSlideCardItemBinding.cardIv.visibility = View.VISIBLE
|
||||
|
||||
gameIconStackGroup.forEach { it.visibility = View.GONE }
|
||||
gameIconGroup.forEach { it.visibility = View.GONE }
|
||||
descTv.text = homeSubSlide.cardDesc
|
||||
// 防止重复加载图片导致闪烁
|
||||
if (homeSubSlide.image != cardIv.getTag(R.string.tag_img_url_id)) {
|
||||
ImageUtils.display(cardIv, homeSubSlide.image, false, AlphaGradientProcess())
|
||||
cardIv.setTag(R.string.tag_img_url_id, homeSubSlide.image)
|
||||
}
|
||||
ConstraintSet().apply {
|
||||
clone(cardContainer)
|
||||
clear(textContainer.id, ConstraintSet.TOP)
|
||||
clear(textContainer.id, ConstraintSet.BOTTOM)
|
||||
connect(textContainer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
|
||||
connect(textContainer.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
|
||||
}.applyTo(cardContainer)
|
||||
_item.exposureEventList.add(exposureEvent)
|
||||
}
|
||||
}
|
||||
|
||||
cardCv.setOnClickListener {
|
||||
val clickEvent = ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = GameEntity().apply {
|
||||
sequence = position
|
||||
outerSequence = viewModel.refreshCount
|
||||
},
|
||||
basicSource = basicExposureSource,
|
||||
source = listOf(
|
||||
ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"),
|
||||
ExposureSource("右侧卡片", homeSubSlide.id)
|
||||
),
|
||||
event = ExposureType.CLICK
|
||||
)
|
||||
if (homeSubSlide.toLinkEntity().type != "game") {
|
||||
ExposureManager.log(clickEvent)
|
||||
}
|
||||
SensorsBridge.trackEvent("RightSideCardClick", json {
|
||||
"position" to homeSubSlide.sequence
|
||||
"title" to homeSubSlide.title
|
||||
"card_id" to homeSubSlide.id
|
||||
})
|
||||
com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(homeSubSlide, viewModel.refreshCount, "卡片")
|
||||
childEventHelper.navigateToLinkPage(homeSubSlide.toLinkEntity(), "右侧卡片", clickEvent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGameExposureEvent(
|
||||
collectionData: CustomPageData.CommonContentCollection,
|
||||
homeSubSlide: HomeSubSlide,
|
||||
subSlideId: String,
|
||||
gameEntity: GameEntity,
|
||||
position: Int,
|
||||
basicExposureSource: List<ExposureSource>
|
||||
@ -579,16 +176,16 @@ class CustomHomeSlideWithCardsViewHolder(
|
||||
"通用内容合集",
|
||||
"${collectionData.name}+${collectionData.layoutChinese}+${collectionData.id}"
|
||||
),
|
||||
ExposureSource("右侧卡片", homeSubSlide.id)
|
||||
ExposureSource("右侧卡片", subSlideId)
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewAttach(parent: RecyclerView?) {
|
||||
bannerController.onViewAttachedToWindow(parent)
|
||||
slideWithCardsUi.onViewAttach(parent)
|
||||
}
|
||||
|
||||
override fun onViewDetach(parent: RecyclerView?) {
|
||||
bannerController.onViewDetachedFromWindow(parent)
|
||||
slideWithCardsUi.onViewDetach(parent)
|
||||
}
|
||||
|
||||
private fun updateImmersiveColor(color: Int) {
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
package com.gh.gamecenter.home.custom.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.gh.gamecenter.common.entity.ExposureEntity
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.view.GridSpacingItemDecoration
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.databinding.RecyclerNavigationCustomBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.home.custom.CustomPageViewModel
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomGameNavigationAdapter
|
||||
import com.gh.gamecenter.home.custom.createExposureEvent
|
||||
import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper
|
||||
import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageItem
|
||||
import com.gh.gamecenter.home.custom.ui.CommonContentNavigationUi
|
||||
|
||||
/**
|
||||
* 通用内容合集-导航栏
|
||||
@ -24,27 +22,21 @@ class CustomNavigationViewHolder(
|
||||
val binding: RecyclerNavigationCustomBinding
|
||||
) : BaseCustomViewHolder(viewModel, binding.root) {
|
||||
|
||||
private val layoutManager by lazy {
|
||||
GridLayoutManager(itemView.context, 4)
|
||||
}
|
||||
|
||||
override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) {
|
||||
CommonContentCollectionEventHelper(viewModel)
|
||||
}
|
||||
|
||||
private val adapter by lazy {
|
||||
CustomGameNavigationAdapter(itemView.context, childEventHelper)
|
||||
private val navigationUi by lazy {
|
||||
CommonContentNavigationUi(binding, object : CommonContentNavigationUi.OnNavigationListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
childEventHelper.navigateToLinkPage(link, text, exposureEvent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun bindView(item: CustomPageItem) {
|
||||
super.bindView(item)
|
||||
if (item is CustomCommonContentCollectionItem) {
|
||||
if (binding.rvNavigation.adapter == null) {
|
||||
binding.rvNavigation.layoutManager = layoutManager
|
||||
binding.rvNavigation.addItemDecoration(GridSpacingItemDecoration(4, 8f.dip2px(), false, 16f.dip2px()))
|
||||
binding.rvNavigation.adapter = adapter
|
||||
binding.rvNavigation.isNestedScrollingEnabled = false
|
||||
}
|
||||
|
||||
val navigationList = item.data.navigations
|
||||
|
||||
@ -61,7 +53,10 @@ class CustomNavigationViewHolder(
|
||||
it.outerSequence = item.position
|
||||
},
|
||||
listOf(
|
||||
ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"),
|
||||
ExposureSource(
|
||||
"通用内容合集",
|
||||
"${item.data.name}+${item.data.layoutChinese}+${item.data.id}"
|
||||
),
|
||||
ExposureSource("导航栏", entity.entryName)
|
||||
),
|
||||
pageConfigure.exposureSourceList,
|
||||
@ -73,7 +68,7 @@ class CustomNavigationViewHolder(
|
||||
|
||||
}
|
||||
item.exposureEventList = exposureEventList
|
||||
adapter.setData(navigationList, exposureEventList)
|
||||
navigationUi.bind(navigationList, exposureEventList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,11 @@ import android.util.SparseBooleanArray
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.view.ImageContainerView
|
||||
import com.gh.common.view.ImageContainerView.Companion.toImageContainerData
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.constant.ItemViewType
|
||||
@ -20,6 +25,9 @@ import com.gh.gamecenter.forum.home.ArticleItemVideoView
|
||||
import com.gh.gamecenter.personalhome.PersonalItemViewHolder
|
||||
import com.gh.gamecenter.personalhome.home.UserHistoryViewModel
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.forum.home.AnswerArticleVideoViewEventHelper
|
||||
import com.gh.gamecenter.forum.home.ArticleItemVideoView.Companion.toArticleVideoData
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
|
||||
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
|
||||
@ -47,9 +55,10 @@ class MyPostAdapter(
|
||||
view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)
|
||||
FooterViewHolder(view)
|
||||
}
|
||||
|
||||
else -> {
|
||||
view = mLayoutInflater.inflate(R.layout.community_answer_item, parent, false)
|
||||
PersonalItemViewHolder(CommunityAnswerItemBinding.bind(view))
|
||||
PersonalItemViewHolder(mContext, CommunityAnswerItemBinding.bind(view), itemClickCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,177 +76,7 @@ class MyPostAdapter(
|
||||
private fun bindNormalItem(holder: PersonalItemViewHolder, position: Int) {
|
||||
val historyEntity = mEntityList[position]
|
||||
|
||||
holder.bindPersonalItem(historyEntity, mEntrance)
|
||||
|
||||
val answer = AnswerEntity()
|
||||
answer.images = historyEntity.images
|
||||
answer.imagesInfo = historyEntity.imagesInfo
|
||||
answer.videos = historyEntity.videos
|
||||
answer.type = historyEntity.type
|
||||
answer.id = historyEntity.id
|
||||
answer.communityId = historyEntity.community.id
|
||||
answer.communityName = historyEntity.community.name
|
||||
answer.status = historyEntity.status ?: "pending"
|
||||
answer.user.id = historyEntity.user?.id ?: ""
|
||||
|
||||
holder.binding.imageContainer.bindData(answer, mEntrance)
|
||||
bindVideoData(holder.binding, historyEntity.transformForumVideoEntity())
|
||||
|
||||
holder.binding.run {
|
||||
topLine.goneIf(position == 0)
|
||||
forumNameLl.visibleIf(historyEntity.community.id.isNotEmpty())
|
||||
ImageUtils.display(userBadgeIcon, historyEntity.user?.badge?.icon)
|
||||
|
||||
statusTv.goneIf(!(historyEntity.status == "pending" || historyEntity.status == "fail") && !historyEntity.me.isFollower)
|
||||
statusTv.setText(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.string.follow_status
|
||||
historyEntity.status == "pending" -> R.string.content_pending_status
|
||||
else -> R.string.fail_status
|
||||
}
|
||||
)
|
||||
statusTv.setTextColor(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.color.theme_alpha_80
|
||||
historyEntity.status == "pending" -> R.color.text_tertiary
|
||||
else -> R.color.text_CCFF5269
|
||||
}.toColor(mContext)
|
||||
)
|
||||
statusTv.setDrawableStart(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.drawable.ic_forum_follow
|
||||
historyEntity.status == "pending" -> R.drawable.ic_forum_pending
|
||||
else -> R.drawable.icon_forum_fail
|
||||
}
|
||||
)
|
||||
title.goneIf(historyEntity.type.contains("answer"))
|
||||
title.text =
|
||||
if ((historyEntity.type.contains("question") || historyEntity.type.contains("article") || historyEntity.type.contains(
|
||||
"video"
|
||||
))
|
||||
) historyEntity.title else historyEntity.question.title
|
||||
questionTitle.goneIf(!historyEntity.type.contains("answer"))
|
||||
questionTitle.text = historyEntity.question.title
|
||||
content.goneIf(historyEntity.brief.isEmpty() && historyEntity.des.isEmpty() && historyEntity.description.isEmpty())
|
||||
content.text = when {
|
||||
historyEntity.type.contains("video") -> historyEntity.des
|
||||
historyEntity.type.contains("question") -> historyEntity.description
|
||||
else -> historyEntity.brief
|
||||
}
|
||||
forumNameTv.text = historyEntity.community.name
|
||||
|
||||
when (historyEntity.type) {
|
||||
"question" -> {
|
||||
val titleStr = " ${historyEntity.title}"
|
||||
val drawable = R.drawable.ic_ask_label.toDrawable()
|
||||
drawable?.run {
|
||||
setBounds(0, 0, 16F.dip2px(), 16F.dip2px())
|
||||
title.text = SpanBuilder(titleStr).image(0, 1, this).build()
|
||||
}
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
val briefStr = " ${historyEntity.brief}"
|
||||
val drawable = R.drawable.ic_answer_label.toDrawable()
|
||||
drawable?.run {
|
||||
setBounds(0, 0, 16F.dip2px(), 16F.dip2px())
|
||||
content.text = SpanBuilder(briefStr).image(0, 1, this).build()
|
||||
}
|
||||
}
|
||||
|
||||
"community_article" -> {
|
||||
if (historyEntity.getPassVideos().isNotEmpty() && historyEntity.images.isNotEmpty()) {
|
||||
val titleStr = title.text
|
||||
val videoSpan =
|
||||
SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build()
|
||||
title.text = SpannableStringBuilder()
|
||||
.append(titleStr)
|
||||
.append(videoSpan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userIcon.visibility = View.GONE
|
||||
userName.visibility = View.GONE
|
||||
userBadgeIcon.visibility = View.GONE
|
||||
userBadgeName.visibility = View.GONE
|
||||
concernBtn.visibility = View.GONE
|
||||
timeContainer.visibility = View.GONE
|
||||
title.layoutParams = (title.layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||
topMargin = 20F.dip2px()
|
||||
}
|
||||
|
||||
title.setOnClickListener {
|
||||
holder.itemView.performClick()
|
||||
}
|
||||
}
|
||||
holder.itemView.setOnClickListener {
|
||||
itemClickCallback.invoke(historyEntity, position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindVideoData(binding: CommunityAnswerItemBinding, entity: ForumVideoEntity) {
|
||||
binding.run {
|
||||
if (entity.url.isEmpty()) {
|
||||
horizontalVideoView.visibility = View.GONE
|
||||
verticalVideoView.visibility = View.GONE
|
||||
} else {
|
||||
val videoInfo = entity.videoInfo
|
||||
val visibleView = if (videoInfo.height > videoInfo.width) {
|
||||
horizontalVideoView.visibility = View.GONE
|
||||
verticalVideoView.visibility = View.VISIBLE
|
||||
verticalVideoView
|
||||
} else {
|
||||
horizontalVideoView.visibility = View.VISIBLE
|
||||
verticalVideoView.visibility = View.GONE
|
||||
horizontalVideoView
|
||||
}
|
||||
|
||||
val orientationUtils = OrientationUtils(mContext as Activity, visibleView)
|
||||
orientationUtils.isEnable = false
|
||||
GSYVideoOptionBuilder()
|
||||
.setIsTouchWiget(false)
|
||||
.setUrl(entity.url)
|
||||
.setRotateViewAuto(false)
|
||||
.setCacheWithPlay(true)
|
||||
.setRotateWithSystem(false)
|
||||
.setReleaseWhenLossAudio(true)
|
||||
.setLooping(false)
|
||||
.setShowFullAnimation(false)
|
||||
.setEnlargeImageRes(R.drawable.ic_game_detail_enter_full_screen)
|
||||
.setShrinkImageRes(R.drawable.ic_game_detail_exit_full_screen)
|
||||
.setVideoAllCallBack(object : GSYSampleCallBack() {
|
||||
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
|
||||
orientationUtils.backToProtVideo()
|
||||
visibleView.uploadVideoStreamingPlaying("退出全屏")
|
||||
}
|
||||
})
|
||||
.build(visibleView)
|
||||
visibleView.run {
|
||||
updateVideoData(entity)
|
||||
updateThumb(entity.poster)
|
||||
updateDurationTv(entity.duration)
|
||||
|
||||
setVideoStatus(entity.status)
|
||||
|
||||
fullscreenButton.setOnClickListener {
|
||||
val horizontalVideoView = startWindowFullscreen(mContext, true, true) as? ArticleItemVideoView
|
||||
if (horizontalVideoView == null) {
|
||||
toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤")
|
||||
return@setOnClickListener
|
||||
}
|
||||
orientationUtils.resolveByClick()
|
||||
horizontalVideoView.uuid = uuid
|
||||
horizontalVideoView.updateVideoData(entity)
|
||||
horizontalVideoView.updateThumb(entity.poster)
|
||||
horizontalVideoView.violenceUpdateMuteStatus()
|
||||
horizontalVideoView.setFullViewStatus()
|
||||
uploadVideoStreamingPlaying("开始播放")
|
||||
uploadVideoStreamingPlaying("点击全屏")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.bindPersonalItem(historyEntity, mEntrance, position)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
|
||||
@ -20,6 +20,7 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.MotionEventCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@ -43,6 +44,7 @@ import com.gh.gamecenter.common.base.activity.BaseActivity;
|
||||
import com.gh.gamecenter.common.base.fragment.ToolbarFragment;
|
||||
import com.gh.gamecenter.common.constant.Constants;
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts;
|
||||
import com.gh.gamecenter.common.entity.NormalShareEntity;
|
||||
import com.gh.gamecenter.common.eventbus.EBNetworkState;
|
||||
import com.gh.gamecenter.common.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource;
|
||||
@ -316,18 +318,11 @@ public class NewsDetailFragment extends ToolbarFragment {
|
||||
if (adapter != null && adapter.getNewsDetailEntity() != null && !ClickUtils.isFastDoubleClick(R.id.menu_question_post)) {
|
||||
DataCollectionUtils.uploadClick(requireContext(), "分享", "新闻详情"
|
||||
, adapter.getNewsDetailEntity().getTitle());
|
||||
|
||||
String url = getString(R.string.share_news_article_url, adapter.getNewsDetailEntity().getShortId());
|
||||
|
||||
String shareIcon;
|
||||
if (gameEntity == null) {
|
||||
shareIcon = getString(R.string.gh_icon_url);
|
||||
} else {
|
||||
shareIcon = gameEntity.getIcon();
|
||||
}
|
||||
((BaseActivity) requireActivity()).showShare(url, shareIcon, adapter.getNewsDetailEntity().getTitle(),
|
||||
"来自光环助手(最强卡牌神器)", ShareUtils.ShareEntrance.news, adapter.getNewsDetailEntity().getId());
|
||||
NewsDetailEntity entity = adapter.getNewsDetailEntity();
|
||||
String shortId = adapter.getNewsDetailEntity().getShortId();
|
||||
NewsShareDialog.show((AppCompatActivity) requireActivity(), shortId, entity.getId(), gameEntity.getIcon(), entity.getTitle());
|
||||
}
|
||||
|
||||
break;
|
||||
case R.id.menu_collect:
|
||||
CheckLoginUtils.checkLogin(requireContext(), "资讯文章详情-收藏", () -> {
|
||||
|
||||
@ -0,0 +1,165 @@
|
||||
package com.gh.gamecenter.newsdetail
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.children
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.BaseDraggableDialogFragment
|
||||
import com.gh.gamecenter.common.entity.NormalShareEntity
|
||||
import com.gh.gamecenter.common.eventbus.EBShare
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.DialogNewsDetailShareBinding
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class NewsShareDialog : BaseDraggableDialogFragment() {
|
||||
|
||||
private lateinit var mBinding: DialogNewsDetailShareBinding
|
||||
private var mShareEntity: NormalShareEntity? = null
|
||||
private var mShareUtils: ShareUtils? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mShareEntity = requireArguments().getParcelable(KEY_SHARE)
|
||||
mShareUtils = getShareUtils()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return DialogNewsDetailShareBinding.inflate(inflater, container, false)
|
||||
.apply { mBinding = this }.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mBinding.run {
|
||||
tvTitle.text = mShareEntity?.shareTitle ?: ""
|
||||
|
||||
vWechatBackground.setOnClickListener {
|
||||
logEvent("微信")
|
||||
mShareUtils?.wechatShare()
|
||||
}
|
||||
|
||||
vWechatMomentsBackground.setOnClickListener {
|
||||
logEvent("朋友圈")
|
||||
mShareUtils?.wechatMomentsShare()
|
||||
}
|
||||
|
||||
vQqBackground.setOnClickListener {
|
||||
logEvent("QQ好友")
|
||||
mShareUtils?.qqShare()
|
||||
}
|
||||
|
||||
vQqZoneBackground.setOnClickListener {
|
||||
logEvent("QQ空间")
|
||||
mShareUtils?.qZoneShare()
|
||||
}
|
||||
|
||||
vWeiboBackground.setOnClickListener {
|
||||
logEvent("新浪微博")
|
||||
mShareUtils?.sinaWeiboShare()
|
||||
}
|
||||
|
||||
vSmsBackground.setOnClickListener {
|
||||
logEvent("短信")
|
||||
EventBus.getDefault().post(EBShare(ShareUtils.shareEntrance, "短信"))
|
||||
mShareUtils?.shortMessageShare()
|
||||
}
|
||||
|
||||
vCopyLinkBackground.setOnClickListener {
|
||||
logEvent("复制链接")
|
||||
EventBus.getDefault().post(EBShare(ShareUtils.shareEntrance, "复制链接"))
|
||||
mShareUtils?.copyLink(mShareUtils?.shareUrl ?: "")
|
||||
}
|
||||
|
||||
tvCancel.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getShareUtils(): ShareUtils {
|
||||
val shareUtils = ShareUtils.getInstance(requireContext())
|
||||
mShareEntity?.run {
|
||||
shareUtils.shareParamsDetail(
|
||||
requireActivity(),
|
||||
shareUrl,
|
||||
shareIcon,
|
||||
shareTitle,
|
||||
shareSummary,
|
||||
shareEntrance,
|
||||
id
|
||||
)
|
||||
}
|
||||
return shareUtils
|
||||
}
|
||||
|
||||
private fun logEvent(shareType: String) {
|
||||
NewLogUtils.logViewOrClickGameCollectionDetail(
|
||||
"click_game_collect_detail_share",
|
||||
mShareEntity?.shareTitle ?: "",
|
||||
mShareEntity?.id ?: "",
|
||||
shareType
|
||||
)
|
||||
SensorsBridge.trackEvent(
|
||||
"GameCollectDetailShareClick",
|
||||
"game_collect_title", mShareEntity?.shareTitle ?: "",
|
||||
"game_collect_id", mShareEntity?.id ?: "",
|
||||
"share_type", shareType
|
||||
)
|
||||
}
|
||||
|
||||
override fun getRootView(): View = mBinding.root
|
||||
override fun getDragCloseView(): View = mBinding.dragClose
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mBinding.run {
|
||||
container.background = R.drawable.game_detail_more_dialog_background.toDrawable(requireContext())
|
||||
tvCancel.background = R.drawable.bg_common_button_light_fill_gray.toDrawable(requireContext())
|
||||
dividerLine1.setBackgroundColor(R.color.ui_divider.toColor(requireContext()))
|
||||
tvCancel.setTextColor(R.color.text_secondary.toColor(requireContext()))
|
||||
tvTitle.setTextColor(R.color.text_secondary.toColor(requireContext()))
|
||||
handleView.background = R.drawable.download_dialog_close_hint.toDrawable(requireContext())
|
||||
clShare.children.forEach {
|
||||
if (it is TextView) {
|
||||
it.setTextColor(R.color.text_tertiary.toColor(requireContext()))
|
||||
}
|
||||
}
|
||||
tvCopyLink.setTextColor(R.color.text_tertiary.toColor(requireContext()))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_SHARE = "share"
|
||||
const val REQUEST_CODE = 1105
|
||||
|
||||
@JvmStatic
|
||||
fun show(activity: AppCompatActivity, shortId: String?, id: String?, gameIcon: String?, title: String?) {
|
||||
val entity = NormalShareEntity(
|
||||
id ?: "",
|
||||
activity.getString(R.string.share_news_article_url, shortId),
|
||||
gameIcon ?: activity.getString(R.string.gh_icon_url),
|
||||
title ?: "",
|
||||
activity.getString(R.string.news_share_description),
|
||||
ShareUtils.ShareEntrance.news,
|
||||
null
|
||||
)
|
||||
NewsShareDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(KEY_SHARE, entity)
|
||||
}
|
||||
}.show(
|
||||
activity.supportFragmentManager,
|
||||
NewsShareDialog::class.java.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,37 +1,261 @@
|
||||
package com.gh.gamecenter.personalhome
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.View
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.NewsUtils
|
||||
import com.gh.common.view.ImageContainerView
|
||||
import com.gh.common.view.ImageContainerView.Companion.toImageContainerData
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.callback.ConfirmListener
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.core.utils.SpanBuilder
|
||||
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.forum.detail.ForumDetailActivity
|
||||
import com.gh.gamecenter.forum.home.AnswerArticleVideoViewEventHelper
|
||||
import com.gh.gamecenter.forum.home.ArticleItemVideoView.Companion.toArticleVideoData
|
||||
import com.gh.gamecenter.qa.answer.BaseAnswerOrArticleItemViewHolder
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
|
||||
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
|
||||
|
||||
class PersonalItemViewHolder(val binding: CommunityAnswerItemBinding) :
|
||||
class PersonalItemViewHolder(
|
||||
val context: Context,
|
||||
val binding: CommunityAnswerItemBinding,
|
||||
private val itemClickCallback: (historyEntity: PersonalHistoryEntity, position: Int) -> Unit
|
||||
) :
|
||||
BaseAnswerOrArticleItemViewHolder(binding.root) {
|
||||
|
||||
fun bindPersonalItem(entity: PersonalHistoryEntity, entrance: String) {
|
||||
fun bindPersonalItem(historyEntity: PersonalHistoryEntity, entrance: String, position: Int) {
|
||||
commentCount.isClickable = true
|
||||
bindCommendAndVote(entity, entrance)
|
||||
bindCommendAndVote(historyEntity, entrance)
|
||||
|
||||
if (entity.community.type == "official_bbs") {
|
||||
forumIcon?.displayGameIcon(entity.community.icon, null)
|
||||
if (historyEntity.community.type == "official_bbs") {
|
||||
forumIcon?.displayGameIcon(historyEntity.community.icon, null)
|
||||
} else {
|
||||
forumIcon?.displayGameIcon(
|
||||
entity.community.game?.getIcon(),
|
||||
entity.community.game?.iconSubscript,
|
||||
entity.community.game?.iconFloat
|
||||
historyEntity.community.game?.getIcon(),
|
||||
historyEntity.community.game?.iconSubscript,
|
||||
historyEntity.community.game?.iconFloat
|
||||
)
|
||||
}
|
||||
|
||||
forumNameContainer?.setOnClickListener {
|
||||
MtaHelper.onEvent(getEventId(entrance), getKey(entrance), entity.community.name)
|
||||
MtaHelper.onEvent(getEventId(entrance), getKey(entrance), historyEntity.community.name)
|
||||
itemView.context.startActivity(
|
||||
ForumDetailActivity.getIntent(
|
||||
itemView.context,
|
||||
entity.community.id,
|
||||
historyEntity.community.id,
|
||||
entrance
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val answer = AnswerEntity()
|
||||
answer.images = historyEntity.images
|
||||
answer.imagesInfo = historyEntity.imagesInfo
|
||||
answer.videos = historyEntity.videos
|
||||
answer.type = historyEntity.type
|
||||
answer.id = historyEntity.id
|
||||
answer.communityId = historyEntity.community.id
|
||||
answer.communityName = historyEntity.community.name
|
||||
answer.status = historyEntity.status
|
||||
answer.user.id = historyEntity.user.id ?: ""
|
||||
|
||||
binding.imageContainer.bindData(
|
||||
answer.toImageContainerData(),
|
||||
object : ImageContainerView.OnImageContainerEventListener {
|
||||
override fun onImageClick(
|
||||
images: List<String>,
|
||||
position: Int,
|
||||
imageViewList: ArrayList<SimpleDraweeView>
|
||||
) {
|
||||
val intent = ImageViewerActivity.getIntent(
|
||||
context, answer.images.toArrayList(), position, imageViewList,
|
||||
if (answer.type == "community_article") answer else null, entrance, true
|
||||
)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onVideoCLick(videoId: String) {
|
||||
DirectUtils.directToVideoDetail(
|
||||
context,
|
||||
videoId,
|
||||
VideoDetailContainerViewModel.Location.VIDEO_HOT.value,
|
||||
showComment = false,
|
||||
entrance = entrance,
|
||||
path = ""
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
bindVideoData(historyEntity.transformForumVideoEntity())
|
||||
|
||||
binding.run {
|
||||
topLine.goneIf(position == 0)
|
||||
forumNameLl.visibleIf(historyEntity.community.id.isNotEmpty())
|
||||
userIcon.display(historyEntity.user.border, historyEntity.user.icon, historyEntity.user.auth?.icon)
|
||||
userName.text = historyEntity.user.name
|
||||
ImageUtils.display(userBadgeIcon, historyEntity.user.badge?.icon)
|
||||
userBadgeIcon.goneIf(historyEntity.user.badge == null)
|
||||
userBadgeName.text = historyEntity.user.badge?.name
|
||||
time.text =
|
||||
(if (historyEntity.me.isFollower || historyEntity.status == "pending" || historyEntity.status == "fail") " · " else "") + NewsUtils.getFormattedTime(
|
||||
historyEntity.time!!
|
||||
)
|
||||
statusTv.goneIf(!(historyEntity.status == "pending" || historyEntity.status == "fail") && !historyEntity.me.isFollower)
|
||||
statusTv.setText(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.string.follow_status
|
||||
historyEntity.status == "pending" -> R.string.content_pending_status
|
||||
else -> R.string.fail_status
|
||||
}
|
||||
)
|
||||
statusTv.setTextColor(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.color.theme_alpha_80
|
||||
historyEntity.status == "pending" -> R.color.text_tertiary
|
||||
else -> R.color.text_CCFF5269
|
||||
}.toColor(context)
|
||||
)
|
||||
statusTv.setDrawableStart(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.drawable.ic_forum_follow
|
||||
historyEntity.status == "pending" -> R.drawable.ic_forum_pending
|
||||
else -> R.drawable.icon_forum_fail
|
||||
}
|
||||
)
|
||||
authTv.goneIf(historyEntity.user?.auth == null) {
|
||||
authTv.text = historyEntity.user?.auth?.text
|
||||
time.setPadding(8F.dip2px(), 0, 0, 0)
|
||||
}
|
||||
if (statusTv.visibility == View.VISIBLE) {
|
||||
authTv.setPadding(8F.dip2px(), 0, 0, 0)
|
||||
}
|
||||
title.goneIf(historyEntity.type.contains("answer"))
|
||||
title.text =
|
||||
if ((historyEntity.type.contains("question") || historyEntity.type.contains("article") || historyEntity.type.contains(
|
||||
"video"
|
||||
))
|
||||
) historyEntity.title else historyEntity.question.title
|
||||
questionTitle.goneIf(!historyEntity.type.contains("answer"))
|
||||
questionTitle.text = historyEntity.question.title
|
||||
content.goneIf(historyEntity.brief.isEmpty() && historyEntity.des.isEmpty() && historyEntity.description.isEmpty())
|
||||
content.text = when {
|
||||
historyEntity.type.contains("video") -> historyEntity.des
|
||||
historyEntity.type.contains("question") -> historyEntity.description
|
||||
else -> historyEntity.brief
|
||||
}
|
||||
forumNameTv.text = historyEntity.community.name
|
||||
|
||||
when (historyEntity.type) {
|
||||
"question" -> {
|
||||
val titleStr = " ${historyEntity.title}"
|
||||
val drawable = R.drawable.ic_ask_label.toDrawable()
|
||||
drawable?.run {
|
||||
setBounds(0, 0, 16F.dip2px(), 16F.dip2px())
|
||||
title.text = SpanBuilder(titleStr).image(0, 1, this).build()
|
||||
}
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
val briefStr = " ${historyEntity.brief}"
|
||||
val drawable = R.drawable.ic_answer_label.toDrawable()
|
||||
drawable?.run {
|
||||
setBounds(0, 0, 16F.dip2px(), 16F.dip2px())
|
||||
content.text = SpanBuilder(briefStr).image(0, 1, this).build()
|
||||
}
|
||||
}
|
||||
|
||||
"community_article" -> {
|
||||
if (historyEntity.getPassVideos().isNotEmpty() && historyEntity.images.isNotEmpty()) {
|
||||
val titleStr = title.text
|
||||
val videoSpan =
|
||||
SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build()
|
||||
title.text = SpannableStringBuilder()
|
||||
.append(titleStr)
|
||||
.append(videoSpan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
title.setOnClickListener {
|
||||
itemView.performClick()
|
||||
}
|
||||
|
||||
historyEntity.user.run {
|
||||
userBadgeName.setOnClickListener { userBadgeIcon.performClick() }
|
||||
userBadgeIcon.setOnClickListener {
|
||||
DialogUtils.showViewBadgeDialog(context, badge, object : ConfirmListener {
|
||||
override fun onConfirm() {
|
||||
DirectUtils.directToBadgeWall(context, id, name, icon)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 禁止click事件穿透
|
||||
userIcon.setOnClickListener {}
|
||||
}
|
||||
itemView.setOnClickListener {
|
||||
itemClickCallback.invoke(historyEntity, position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindVideoData(entity: ForumVideoEntity) {
|
||||
binding.run {
|
||||
if (entity.url.isEmpty()) {
|
||||
horizontalVideoView.visibility = View.GONE
|
||||
verticalVideoView.visibility = View.GONE
|
||||
} else {
|
||||
val videoInfo = entity.videoInfo
|
||||
val visibleView = if (videoInfo.height > videoInfo.width) {
|
||||
horizontalVideoView.visibility = View.GONE
|
||||
verticalVideoView.visibility = View.VISIBLE
|
||||
verticalVideoView
|
||||
} else {
|
||||
horizontalVideoView.visibility = View.VISIBLE
|
||||
verticalVideoView.visibility = View.GONE
|
||||
horizontalVideoView
|
||||
}
|
||||
|
||||
val orientationUtils = OrientationUtils(context as Activity, visibleView)
|
||||
orientationUtils.isEnable = false
|
||||
GSYVideoOptionBuilder()
|
||||
.setIsTouchWiget(false)
|
||||
.setUrl(entity.url)
|
||||
.setRotateViewAuto(false)
|
||||
.setCacheWithPlay(true)
|
||||
.setRotateWithSystem(false)
|
||||
.setReleaseWhenLossAudio(true)
|
||||
.setLooping(false)
|
||||
.setShowFullAnimation(false)
|
||||
.setEnlargeImageRes(R.drawable.ic_game_detail_enter_full_screen)
|
||||
.setShrinkImageRes(R.drawable.ic_game_detail_exit_full_screen)
|
||||
.setVideoAllCallBack(object : GSYSampleCallBack() {
|
||||
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
|
||||
orientationUtils.backToProtVideo()
|
||||
}
|
||||
})
|
||||
.build(visibleView)
|
||||
visibleView.updateVideoData(
|
||||
entity.toArticleVideoData(),
|
||||
AnswerArticleVideoViewEventHelper(entity, orientationUtils)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,9 +7,13 @@ import android.util.SparseBooleanArray
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.NewsUtils
|
||||
import com.gh.common.view.ImageContainerView
|
||||
import com.gh.common.view.ImageContainerView.Companion.toImageContainerData
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.PersonalHomeRatingViewHolder
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
@ -24,9 +28,12 @@ import com.gh.gamecenter.databinding.PersonalHomeRatingBinding
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.forum.home.AnswerArticleVideoViewEventHelper
|
||||
import com.gh.gamecenter.forum.home.ArticleItemVideoView
|
||||
import com.gh.gamecenter.forum.home.ArticleItemVideoView.Companion.toArticleVideoData
|
||||
import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity
|
||||
import com.gh.gamecenter.personalhome.PersonalItemViewHolder
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
|
||||
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
|
||||
@ -56,13 +63,15 @@ class UserHistoryAdapter(
|
||||
view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)
|
||||
FooterViewHolder(view)
|
||||
}
|
||||
|
||||
ItemViewType.RATING_ITEM -> {
|
||||
view = mLayoutInflater.inflate(R.layout.personal_home_rating, parent, false)
|
||||
PersonalHomeRatingViewHolder(PersonalHomeRatingBinding.bind(view))
|
||||
}
|
||||
|
||||
else -> {
|
||||
view = mLayoutInflater.inflate(R.layout.community_answer_item, parent, false)
|
||||
PersonalItemViewHolder(CommunityAnswerItemBinding.bind(view))
|
||||
PersonalItemViewHolder(mContext, CommunityAnswerItemBinding.bind(view), itemClickCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,196 +90,7 @@ class UserHistoryAdapter(
|
||||
private fun bindNormalItem(holder: PersonalItemViewHolder, position: Int) {
|
||||
val historyEntity = mEntityList[holder.adapterPosition]
|
||||
|
||||
holder.bindPersonalItem(historyEntity, mEntrance)
|
||||
|
||||
val answer = AnswerEntity()
|
||||
answer.images = historyEntity.images
|
||||
answer.imagesInfo = historyEntity.imagesInfo
|
||||
answer.videos = historyEntity.videos
|
||||
answer.type = historyEntity.type
|
||||
answer.id = historyEntity.id
|
||||
answer.communityId = historyEntity.community.id
|
||||
answer.communityName = historyEntity.community.name
|
||||
answer.status = historyEntity.status ?: "pending"
|
||||
answer.user.id = historyEntity.user?.id ?: ""
|
||||
|
||||
holder.binding.imageContainer.bindData(answer, mEntrance)
|
||||
bindVideoData(holder.binding, historyEntity.transformForumVideoEntity())
|
||||
|
||||
holder.binding.run {
|
||||
topLine.goneIf(position == 0)
|
||||
forumNameLl.visibleIf(historyEntity.community.id.isNotEmpty())
|
||||
userIcon.display(historyEntity.user?.border, historyEntity.user?.icon, historyEntity.user?.auth?.icon)
|
||||
userName.text = historyEntity.user?.name
|
||||
ImageUtils.display(userBadgeIcon, historyEntity.user?.badge?.icon)
|
||||
userBadgeIcon.goneIf(historyEntity.user?.badge == null)
|
||||
userBadgeName.text = historyEntity.user?.badge?.name
|
||||
time.text =
|
||||
(if (historyEntity.me.isFollower || historyEntity.status == "pending" || historyEntity.status == "fail") " · " else "") + NewsUtils.getFormattedTime(
|
||||
historyEntity.time!!
|
||||
)
|
||||
statusTv.goneIf(!(historyEntity.status == "pending" || historyEntity.status == "fail") && !historyEntity.me.isFollower)
|
||||
statusTv.setText(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.string.follow_status
|
||||
historyEntity.status == "pending" -> R.string.content_pending_status
|
||||
else -> R.string.fail_status
|
||||
}
|
||||
)
|
||||
statusTv.setTextColor(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.color.theme_alpha_80
|
||||
historyEntity.status == "pending" -> R.color.text_tertiary
|
||||
else -> R.color.text_CCFF5269
|
||||
}.toColor(mContext)
|
||||
)
|
||||
statusTv.setDrawableStart(
|
||||
when {
|
||||
historyEntity.me.isFollower -> R.drawable.ic_forum_follow
|
||||
historyEntity.status == "pending" -> R.drawable.ic_forum_pending
|
||||
else -> R.drawable.icon_forum_fail
|
||||
}
|
||||
)
|
||||
authTv.goneIf(historyEntity.user?.auth == null) {
|
||||
authTv.text = historyEntity.user?.auth?.text
|
||||
time.setPadding(8F.dip2px(), 0, 0, 0)
|
||||
}
|
||||
if (statusTv.visibility == View.VISIBLE) {
|
||||
authTv.setPadding(8F.dip2px(), 0, 0, 0)
|
||||
}
|
||||
title.goneIf(historyEntity.type.contains("answer"))
|
||||
title.text =
|
||||
if ((historyEntity.type.contains("question") || historyEntity.type.contains("article") || historyEntity.type.contains(
|
||||
"video"
|
||||
))
|
||||
) historyEntity.title else historyEntity.question.title
|
||||
questionTitle.goneIf(!historyEntity.type.contains("answer"))
|
||||
questionTitle.text = historyEntity.question.title
|
||||
content.goneIf(historyEntity.brief.isEmpty() && historyEntity.des.isEmpty() && historyEntity.description.isEmpty())
|
||||
content.text = when {
|
||||
historyEntity.type.contains("video") -> historyEntity.des
|
||||
historyEntity.type.contains("question") -> historyEntity.description
|
||||
else -> historyEntity.brief
|
||||
}
|
||||
forumNameTv.text = historyEntity.community.name
|
||||
|
||||
when (historyEntity.type) {
|
||||
"question" -> {
|
||||
val titleStr = " ${historyEntity.title}"
|
||||
val drawable = R.drawable.ic_ask_label.toDrawable()
|
||||
drawable?.run {
|
||||
setBounds(0, 0, 16F.dip2px(), 16F.dip2px())
|
||||
title.text = SpanBuilder(titleStr).image(0, 1, this).build()
|
||||
}
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
val briefStr = " ${historyEntity.brief}"
|
||||
val drawable = R.drawable.ic_answer_label.toDrawable()
|
||||
drawable?.run {
|
||||
setBounds(0, 0, 16F.dip2px(), 16F.dip2px())
|
||||
content.text = SpanBuilder(briefStr).image(0, 1, this).build()
|
||||
}
|
||||
}
|
||||
|
||||
"community_article" -> {
|
||||
if (historyEntity.getPassVideos().isNotEmpty() && historyEntity.images.isNotEmpty()) {
|
||||
val titleStr = title.text
|
||||
val videoSpan =
|
||||
SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build()
|
||||
title.text = SpannableStringBuilder()
|
||||
.append(titleStr)
|
||||
.append(videoSpan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
title.setOnClickListener {
|
||||
holder.itemView.performClick()
|
||||
}
|
||||
|
||||
historyEntity.user?.run {
|
||||
userBadgeName.setOnClickListener { userBadgeIcon.performClick() }
|
||||
userBadgeIcon.setOnClickListener {
|
||||
DialogUtils.showViewBadgeDialog(mContext, badge, object : ConfirmListener {
|
||||
override fun onConfirm() {
|
||||
DirectUtils.directToBadgeWall(mContext, id, name, icon)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 禁止click事件穿透
|
||||
userIcon.setOnClickListener {}
|
||||
}
|
||||
holder.itemView.setOnClickListener {
|
||||
itemClickCallback.invoke(historyEntity, position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindVideoData(binding: CommunityAnswerItemBinding, entity: ForumVideoEntity) {
|
||||
binding.run {
|
||||
if (entity.url.isEmpty()) {
|
||||
horizontalVideoView.visibility = View.GONE
|
||||
verticalVideoView.visibility = View.GONE
|
||||
} else {
|
||||
val videoInfo = entity.videoInfo
|
||||
val visibleView = if (videoInfo.height > videoInfo.width) {
|
||||
horizontalVideoView.visibility = View.GONE
|
||||
verticalVideoView.visibility = View.VISIBLE
|
||||
verticalVideoView
|
||||
} else {
|
||||
horizontalVideoView.visibility = View.VISIBLE
|
||||
verticalVideoView.visibility = View.GONE
|
||||
horizontalVideoView
|
||||
}
|
||||
|
||||
val orientationUtils = OrientationUtils(mContext as Activity, visibleView)
|
||||
orientationUtils.isEnable = false
|
||||
GSYVideoOptionBuilder()
|
||||
.setIsTouchWiget(false)
|
||||
.setUrl(entity.url)
|
||||
.setRotateViewAuto(false)
|
||||
.setCacheWithPlay(true)
|
||||
.setRotateWithSystem(false)
|
||||
.setReleaseWhenLossAudio(true)
|
||||
.setLooping(false)
|
||||
.setShowFullAnimation(false)
|
||||
.setEnlargeImageRes(R.drawable.ic_game_detail_enter_full_screen)
|
||||
.setShrinkImageRes(R.drawable.ic_game_detail_exit_full_screen)
|
||||
.setVideoAllCallBack(object : GSYSampleCallBack() {
|
||||
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
|
||||
orientationUtils.backToProtVideo()
|
||||
visibleView.uploadVideoStreamingPlaying("退出全屏")
|
||||
}
|
||||
})
|
||||
.build(visibleView)
|
||||
visibleView.run {
|
||||
updateVideoData(entity)
|
||||
updateThumb(entity.poster)
|
||||
updateDurationTv(entity.duration)
|
||||
|
||||
setVideoStatus(entity.status)
|
||||
|
||||
fullscreenButton.setOnClickListener {
|
||||
val horizontalVideoView = startWindowFullscreen(mContext, true, true) as? ArticleItemVideoView
|
||||
if (horizontalVideoView == null) {
|
||||
toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤")
|
||||
return@setOnClickListener
|
||||
}
|
||||
orientationUtils.resolveByClick()
|
||||
horizontalVideoView.uuid = uuid
|
||||
horizontalVideoView.updateVideoData(entity)
|
||||
horizontalVideoView.updateThumb(entity.poster)
|
||||
horizontalVideoView.violenceUpdateMuteStatus()
|
||||
horizontalVideoView.setFullViewStatus()
|
||||
uploadVideoStreamingPlaying("开始播放")
|
||||
uploadVideoStreamingPlaying("点击全屏")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.bindPersonalItem(historyEntity, mEntrance, position)
|
||||
}
|
||||
|
||||
private fun bindRatingItem(holder: PersonalHomeRatingViewHolder, position: Int) {
|
||||
|
||||
@ -15,6 +15,7 @@ import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.*
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.entity.VoteEntity
|
||||
import com.gh.gamecenter.forum.detail.ForumDetailActivity
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
@ -22,6 +23,7 @@ import com.gh.gamecenter.qa.comment.CommentActivity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.CommunityItemData
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.Count
|
||||
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity
|
||||
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
|
||||
@ -52,7 +54,12 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
val forumIcon: GameIconView? = itemView.findViewById(R.id.forumIcon)
|
||||
|
||||
|
||||
open fun bindCommendAndVote(entity: CommunityItemData, entrance: String, path: String? = null, position: Int? = null) {
|
||||
open fun bindCommendAndVote(
|
||||
entity: CommunityItemData,
|
||||
entrance: String,
|
||||
path: String? = null,
|
||||
position: Int? = null
|
||||
) {
|
||||
binNormalView(entity)
|
||||
|
||||
commentCount.setOnClickListener {
|
||||
@ -68,6 +75,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
)
|
||||
itemView.context.startActivity(intent)
|
||||
}
|
||||
|
||||
"video" -> {
|
||||
val communityId = if (!entity.communityId.isNullOrEmpty()) entity.communityId
|
||||
?: "" else entity.community.id
|
||||
@ -77,6 +85,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
val intent = CommentActivity.getAnswerCommentIntent(
|
||||
itemView.context,
|
||||
@ -86,6 +95,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
)
|
||||
itemView.context.startActivity(intent)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val intent = NewQuestionDetailActivity.getCommentIntent(
|
||||
itemView.context, entity.id, entrance, ""
|
||||
@ -223,11 +233,17 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
}
|
||||
}
|
||||
|
||||
if (entrance == "社区-关注") {
|
||||
SensorsBridge.trackFollowPageContentClick("帖子", entity.title)
|
||||
}
|
||||
|
||||
|
||||
when (entity.type) {
|
||||
"question" -> {
|
||||
val intent = NewQuestionDetailActivity.getCommentIntent(it.context, entity.id, entrance, "")
|
||||
itemView.context.startActivity(intent)
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
val intent = NewQuestionDetailActivity.getCommentIntent(
|
||||
it.context,
|
||||
@ -238,6 +254,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
)
|
||||
itemView.context.startActivity(intent)
|
||||
}
|
||||
|
||||
"video" -> {
|
||||
itemView.context.startActivity(
|
||||
ForumVideoDetailActivity.getIntent(
|
||||
@ -248,6 +265,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val communityId = entity.community.id
|
||||
val intent = ArticleDetailActivity.getCommentIntent(
|
||||
@ -311,10 +329,12 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
voteCountContainer.visibility = View.GONE
|
||||
commentCount.text = if (entity.count.answer > 0) entity.count.answer.toString() else "回答"
|
||||
}
|
||||
|
||||
"answer" -> {
|
||||
commentCount.text = if (entity.count.reply > 0) entity.count.reply.toString() else "评论"
|
||||
voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同"
|
||||
}
|
||||
|
||||
else -> {
|
||||
commentCount.text = if (entity.count.comment > 0) entity.count.comment.toString() else "评论"
|
||||
voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同"
|
||||
@ -360,8 +380,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
entity.count.vote = entity.count.vote + 1
|
||||
entity.me.isVoted = true
|
||||
entity.vote(true)
|
||||
voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同"
|
||||
setVoteAndCommentStyle(entity)
|
||||
}
|
||||
@ -396,7 +415,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
entity.me.isAnswerVoted = true
|
||||
}
|
||||
ToastUtils.showToast("已赞同")
|
||||
entity.count.vote = entity.count.vote + 1
|
||||
entity.vote(true)
|
||||
voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同"
|
||||
setVoteAndCommentStyle(entity)
|
||||
}
|
||||
@ -412,16 +431,19 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
Utils.toast(itemView.context, R.string.ask_vote_hint)
|
||||
true
|
||||
}
|
||||
|
||||
403036 -> {
|
||||
Utils.toast(itemView.context, R.string.ask_vote_limit_hint)
|
||||
true
|
||||
}
|
||||
|
||||
404001 -> {
|
||||
Utils.toast(itemView.context, "内容可能已被删除")
|
||||
entity.active = false
|
||||
setVoteAndCommentStyle(entity)
|
||||
true
|
||||
}
|
||||
|
||||
else -> {
|
||||
setVoteAndCommentStyle(entity)
|
||||
false
|
||||
@ -443,8 +465,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
entity.count.vote = entity.count.vote - 1
|
||||
entity.me.isVoted = false
|
||||
entity.vote(false)
|
||||
voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同"
|
||||
setVoteAndCommentStyle(entity)
|
||||
Utils.toast(itemView.context, "取消点赞")
|
||||
@ -473,12 +494,12 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Response<VoteEntity>() {
|
||||
override fun onResponse(response: VoteEntity?) {
|
||||
entity.count.vote = entity.count.vote - 1
|
||||
if (entity.type == "community_article") {
|
||||
entity.me.isCommunityArticleVote = false
|
||||
} else {
|
||||
entity.me.isAnswerVoted = false
|
||||
}
|
||||
entity.vote(false)
|
||||
voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同"
|
||||
setVoteAndCommentStyle(entity)
|
||||
Utils.toast(itemView.context, "取消赞同")
|
||||
@ -495,16 +516,19 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
|
||||
Utils.toast(itemView.context, R.string.ask_vote_hint)
|
||||
true
|
||||
}
|
||||
|
||||
403036 -> {
|
||||
Utils.toast(itemView.context, R.string.ask_vote_limit_hint)
|
||||
true
|
||||
}
|
||||
|
||||
404001 -> {
|
||||
Utils.toast(itemView.context, "内容可能已被删除")
|
||||
entity.active = false
|
||||
setVoteAndCommentStyle(entity)
|
||||
true
|
||||
}
|
||||
|
||||
else -> {
|
||||
setVoteAndCommentStyle(entity)
|
||||
false
|
||||
|
||||
@ -8,10 +8,14 @@ import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.NewsUtils
|
||||
import com.gh.common.view.ImageContainerView
|
||||
import com.gh.common.view.ImageContainerView.Companion.toImageContainerData
|
||||
import com.gh.gamecenter.CollectionActivity
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.callback.ConfirmListener
|
||||
@ -23,6 +27,7 @@ import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
|
||||
/**
|
||||
* 因为社区回答和社区文章的样式完全一样,所以就统一处理吧!有需要时可以直接复制一份作为单独的社区文章样式
|
||||
@ -123,7 +128,36 @@ class CommunityAnswerItemViewHolder(val binding: CommunityAnswerItemBinding) :
|
||||
}
|
||||
|
||||
binding.userIcon.display(entity.user.border, entity.user.icon, entity.user.auth?.icon)
|
||||
binding.imageContainer.bindData(entity, entrance, path)
|
||||
binding.imageContainer.bindData(
|
||||
entity.toImageContainerData(),
|
||||
object : ImageContainerView.OnImageContainerEventListener {
|
||||
override fun onImageClick(
|
||||
images: List<String>,
|
||||
position: Int,
|
||||
imageViewList: ArrayList<SimpleDraweeView>
|
||||
) {
|
||||
if (entity.communityId.isNullOrEmpty()) {
|
||||
entity.communityId = entity.bbs.id
|
||||
}
|
||||
val intent = ImageViewerActivity.getIntent(
|
||||
itemView.context, entity.images.toArrayList(), position, imageViewList,
|
||||
if (entity.type == "community_article") entity else null, entrance, true
|
||||
)
|
||||
itemView.context.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onVideoCLick(videoId: String) {
|
||||
DirectUtils.directToVideoDetail(
|
||||
itemView.context,
|
||||
videoId,
|
||||
VideoDetailContainerViewModel.Location.VIDEO_HOT.value,
|
||||
showComment = false,
|
||||
entrance = entrance,
|
||||
path = path
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
val spanBuilder = SpannableStringBuilder()
|
||||
if (entity.type == "question") {
|
||||
|
||||
@ -30,6 +30,8 @@ import com.gh.gamecenter.entity.DeviceDialogEntity;
|
||||
import com.gh.gamecenter.entity.DialogEntity;
|
||||
import com.gh.gamecenter.entity.DiscoveryGameCardEntity;
|
||||
import com.gh.gamecenter.entity.DiscoveryGameCardLabel;
|
||||
import com.gh.gamecenter.entity.FollowCommonContentCollection;
|
||||
import com.gh.gamecenter.entity.FollowDynamicEntity;
|
||||
import com.gh.gamecenter.entity.FollowersOrFansEntity;
|
||||
import com.gh.gamecenter.entity.ForumActivityCategoryEntity;
|
||||
import com.gh.gamecenter.entity.ForumActivityEntity;
|
||||
@ -103,6 +105,7 @@ import com.gh.gamecenter.feature.entity.BackgroundImageEntity;
|
||||
import com.gh.gamecenter.feature.entity.CommentEntity;
|
||||
import com.gh.gamecenter.feature.entity.CommentnumEntity;
|
||||
import com.gh.gamecenter.feature.entity.ConcernEntity;
|
||||
import com.gh.gamecenter.entity.FollowUserEntity;
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity;
|
||||
import com.gh.gamecenter.feature.entity.GameEntity;
|
||||
import com.gh.gamecenter.feature.entity.LibaoEntity;
|
||||
@ -119,6 +122,7 @@ import com.gh.gamecenter.feature.entity.ServerCalendarGame;
|
||||
import com.gh.gamecenter.feature.entity.ServerCalendarNotifySetting;
|
||||
import com.gh.gamecenter.feature.entity.SettingsEntity;
|
||||
import com.gh.gamecenter.feature.entity.SimulatorEntity;
|
||||
import com.gh.gamecenter.feature.entity.UserEntity;
|
||||
import com.gh.gamecenter.feature.entity.ViewsEntity;
|
||||
import com.gh.gamecenter.feature.entity.WXSubscribeMsgConfig;
|
||||
import com.gh.gamecenter.floatingwindow.FloatingWindowEntity;
|
||||
@ -144,6 +148,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import okhttp3.RequestBody;
|
||||
@ -761,6 +766,48 @@ public interface ApiService {
|
||||
@Query("view") String view,
|
||||
@Query("page_size") int pageSize);
|
||||
|
||||
@GET("community/user/{user_id}/my_follow")
|
||||
Single<List<FollowUserEntity>> getMyFollowedUsers(@Path("user_id") String userId,
|
||||
@Query("version") String version,
|
||||
@Query("channel") String channel,
|
||||
@Query("page") int page,
|
||||
@Query("refresh") boolean refresh,
|
||||
@QueryMap Map<String, Object> params);
|
||||
|
||||
@GET("community/user/{user_id}/follow_data")
|
||||
Single<List<FollowDynamicEntity>> getFollowUpdates(@Path("user_id") String userId,
|
||||
@Query("type") String type,
|
||||
@Query("page") int page,
|
||||
@Query("version") String version,
|
||||
@Query("channel") String channel);
|
||||
|
||||
@POST("community/user/{user_id}/my_follow/operate_top")
|
||||
Single<ResponseBody> operateTop(
|
||||
@Path("user_id") String userId,
|
||||
@Query("version") String version,
|
||||
@Query("channel") String channel,
|
||||
@Body RequestBody body);
|
||||
|
||||
@POST("community/user/{user_id}/cancel_tip/{type}/{type_id}")
|
||||
Single<ResponseBody> postRead(@Path("user_id") String userId,
|
||||
@Path("type") String type,
|
||||
@Path("type_id") String typeId);
|
||||
|
||||
@GET("community/follow_tab/recommend_user")
|
||||
Single<List<UserEntity>> getRecommendUser(
|
||||
@Query("version") String version,
|
||||
@Query("channel") String channel
|
||||
);
|
||||
|
||||
/**
|
||||
* 关注-通用内容合集配置
|
||||
*/
|
||||
|
||||
@GET("community/follow_tab/common_collection")
|
||||
Single<List<FollowCommonContentCollection>> getFollowCommonCollection(
|
||||
@Query("version") String version,
|
||||
@Query("channel") String channel,
|
||||
@Query("page") int page);
|
||||
|
||||
@GET("settings")
|
||||
Observable<SettingsEntity> getSettings(@Query("version") String version, @Query("channel") String channel);
|
||||
@ -3244,7 +3291,7 @@ public interface ApiService {
|
||||
*/
|
||||
@POST("messages/wechat/one_time/call_back")
|
||||
Single<ResponseBody> postWxSubscribeMsgCallback(@Query("filter") String filter,
|
||||
@Body RequestBody body);
|
||||
@Body RequestBody body);
|
||||
|
||||
/**
|
||||
* 获取网游插件可能用到的签名
|
||||
|
||||
7
app/src/main/res/anim/no_anim.xml
Normal file
7
app/src/main/res/anim/no_anim.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="400"
|
||||
android:fromYDelta="100%p"
|
||||
android:toYDelta="100%p" />
|
||||
</set>
|
||||
@ -5,6 +5,6 @@
|
||||
<item android:color="@color/text_primary" android:state_focused="true" />
|
||||
<item android:color="@color/text_primary" android:state_selected="true" />
|
||||
<item android:color="@color/text_primary" android:state_checked="true" />
|
||||
<item android:color="@color/text_A2ADB8" />
|
||||
<item android:color="@color/community_forum_more" />
|
||||
|
||||
</selector>
|
||||
BIN
app/src/main/res/drawable-xxxhdpi/bg_forum_home_top.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/bg_forum_home_top.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 484 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user