Compare commits
2 Commits
pack/updat
...
chen/20240
| Author | SHA1 | Date | |
|---|---|---|---|
| d7d91b002e | |||
| 81f96577d0 |
@ -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"-->
|
||||
|
||||
@ -1,11 +1,23 @@
|
||||
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.IConcernContentUtilsProvider
|
||||
import com.gh.gamecenter.databinding.RecyclerGameArticleBinding
|
||||
import com.gh.gamecenter.message.R
|
||||
|
||||
@Route(path = RouteConsts.provider.concernContentUtils, name = "ConcernContentUtils暴露服务")
|
||||
class ConcernContentUtilsProviderImpl : IConcernContentUtilsProvider {
|
||||
@ -19,6 +31,71 @@ class ConcernContentUtilsProviderImpl : IConcernContentUtilsProvider {
|
||||
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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
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")
|
||||
private val _article: Article? = null,
|
||||
@SerializedName("user_post")
|
||||
private val _userPost: ArticleEntity? = null
|
||||
) : Parcelable {
|
||||
|
||||
val type: String
|
||||
get() = _type ?: ""
|
||||
|
||||
val article: Article
|
||||
get() = _article ?: Article()
|
||||
|
||||
val game: GameEntity
|
||||
get() = _game ?: GameEntity()
|
||||
|
||||
val me: MeEntity
|
||||
get() = _me ?: MeEntity()
|
||||
|
||||
val userPost: ArticleEntity
|
||||
get() = _userPost ?: ArticleEntity()
|
||||
|
||||
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
|
||||
) : 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
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,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 +42,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 +56,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 +69,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 +81,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 (!followFilterPopWindow.isShowing && tab?.position == TAB_FOLLOW_INDEX) {
|
||||
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 +135,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 +198,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 +294,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 +308,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 +326,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 +346,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 +356,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)
|
||||
}
|
||||
@ -356,6 +429,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
)
|
||||
}
|
||||
tabLayout.addOnTabSelectedListener(obTabSelectedListener)
|
||||
|
||||
tabLayout.setupWithViewPager(viewPager)
|
||||
indicatorView.run {
|
||||
@ -363,13 +437,20 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,7 +530,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -474,6 +555,11 @@ 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)
|
||||
// 如果关注页为默认选中,刚页面TabLayout还来不及初始化,tabLayout.getTabAt(0)为null,所以需要在创建tabView的时候提前设置
|
||||
setDrawableEnd(R.drawable.ic_follow_arrow_down, 8F.dip2px(), 8F.dip2px())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -568,6 +654,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 +692,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 +706,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 +735,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)
|
||||
@ -730,9 +837,10 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
var SCALE_TAB_IMG_WIDTH = 42F
|
||||
var SCALE_TAB_IMG_HEIGHT = 22F
|
||||
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,51 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
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 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,82 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
import android.content.Context
|
||||
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.common.utils.dip2px
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val ALL_POSITION = 0
|
||||
const val PERSON_POSITION = 1
|
||||
const val GAMES_POSITION = 2
|
||||
|
||||
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,198 @@
|
||||
package com.gh.gamecenter.forum.home.follow
|
||||
|
||||
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: CustomPageData.CommonContentCollection
|
||||
) : FollowItem() {
|
||||
override val itemType: Int
|
||||
get() = if (data.layout == COMMON_CONTENT_COLLECTION_LAYOUT_BANNER) {
|
||||
// 轮播图右边卡片列表, 至少需要2个卡片才能显示
|
||||
if (data.slides.subSlide.size < 2) {
|
||||
FOLLOW_ITEM_TYPE_COMMON_BANNER
|
||||
} else {
|
||||
FOLLOW_ITEM_TYPE_COMMON_BANNER_WITH_CARDS
|
||||
}
|
||||
} else {
|
||||
commonContentCollection[data.layout] ?: FOLLOW_ITEM_TYPE_INVALID
|
||||
}
|
||||
|
||||
override fun doAreItemsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowCommonCollectionItem
|
||||
&& data.id == other.data.id
|
||||
}
|
||||
|
||||
override fun doAreContentsTheSame(other: FollowItem): Boolean {
|
||||
return other is FollowCommonCollectionItem
|
||||
&& data == other.data
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
&& 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,45 @@
|
||||
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
|
||||
|
||||
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()
|
||||
}
|
||||
_countAndKey = Pair(newSize, newKeys)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
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.FollowUserEntity
|
||||
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.model.AllFollowRepository
|
||||
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
|
||||
|
||||
private val _dataList = arrayListOf<AllFollowedNormalItem>()
|
||||
val dataList: List<AllFollowedNormalItem>
|
||||
get() = _dataList
|
||||
|
||||
fun setData(data: List<AllFollowedItem>) {
|
||||
_dataList.clear()
|
||||
_dataList.addAll(data.filterIsInstance<AllFollowedNormalItem>())
|
||||
_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 ->
|
||||
AllFollowRepository.Top(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,115 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.CommonCollectionItemBinding
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity
|
||||
import com.gh.gamecenter.forum.home.follow.ResetDataChangeHelper
|
||||
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 FollowCommonCollectionAdapter(
|
||||
context: Context,
|
||||
private val clickInvoke: (Int, CommonCollectionContentEntity) -> Unit,
|
||||
) :
|
||||
RecyclerView.Adapter<FollowCommonCollectionAdapter.FollowCommonCollectionItemViewHolder>() {
|
||||
|
||||
private val dataChangeHelper = ResetDataChangeHelper<CommonCollectionContentEntity>(context, this)
|
||||
val dataList: List<CommonCollectionContentEntity>
|
||||
get() = dataChangeHelper.dataList
|
||||
|
||||
private lateinit var _item: CustomPageData.CommonContentCollection
|
||||
|
||||
fun submitList(item: CustomPageData.CommonContentCollection) {
|
||||
_item = item
|
||||
dataChangeHelper.submitList(item.data) {
|
||||
it.title
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FollowCommonCollectionItemViewHolder {
|
||||
return FollowCommonCollectionItemViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FollowCommonCollectionItemViewHolder, position: Int) {
|
||||
val layout = _item.layout
|
||||
val item = dataList[position]
|
||||
var width = 0
|
||||
var height = 0
|
||||
when (layout) {
|
||||
COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER -> { // 横向滑动banner
|
||||
width = 244F.dip2px()
|
||||
height = 122F.dip2px()
|
||||
}
|
||||
|
||||
COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD -> { // 横排竖式卡片
|
||||
width = 160F.dip2px()
|
||||
height = 213F.dip2px()
|
||||
}
|
||||
|
||||
COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT -> { // 横排图文列表
|
||||
width = 160F.dip2px()
|
||||
height = 80F.dip2px()
|
||||
}
|
||||
}
|
||||
|
||||
holder.binding.apply {
|
||||
ImageUtils.display(commonCollectionImage, item.image)
|
||||
maskView.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT || (item.title.isEmpty() && item.addedContent1.isNullOrEmpty()))
|
||||
titleTv.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT)
|
||||
titleTv.text = item.title
|
||||
desTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD)
|
||||
desTv.text = item.addedContent1
|
||||
linkTitleTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT)
|
||||
linkTitleTv.text = item.title
|
||||
linkDes1.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT)
|
||||
linkDes1.text = item.addedContent1
|
||||
linkDes1.setTextColor(R.color.text_secondary.toColor(root.context))
|
||||
linkDes2.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT)
|
||||
linkDes2.text = item.addedContent2
|
||||
linkDes2.setTextColor(R.color.text_tertiary.toColor(root.context))
|
||||
|
||||
val maskHeight = if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER) {
|
||||
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
|
||||
|
||||
val rootParams = root.layoutParams as RecyclerView.LayoutParams
|
||||
rootParams.width = width
|
||||
rootParams.leftMargin = if (position == 0) 16F.dip2px() else 0
|
||||
rootParams.rightMargin = if (position == itemCount - 1) 16F.dip2px() else 0
|
||||
root.layoutParams = rootParams
|
||||
|
||||
root.setOnClickListener {
|
||||
clickInvoke(position, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataList.size
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val spanCount = 2
|
||||
}
|
||||
|
||||
class FollowCommonCollectionItemViewHolder(val binding: CommonCollectionItemBinding) :
|
||||
BaseRecyclerViewHolder<Any>(binding.root)
|
||||
}
|
||||
@ -0,0 +1,219 @@
|
||||
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
|
||||
|
||||
// 这里默认已关注,不需要显示关注按钮
|
||||
answer.me.isFollower = true
|
||||
viewHolder.bindForumAnswerItem(answer, entrance, path, position)
|
||||
viewHolder.itemView.setOnClickListener {
|
||||
onItemClick(position, answer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,488 @@
|
||||
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.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) {
|
||||
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(articleId: String) {
|
||||
viewModel.navigateToNewsDetailPage(articleId)
|
||||
}
|
||||
|
||||
override fun onComment(articleId: String) {
|
||||
viewModel.navigateToArticleCommentDetailPage(articleId)
|
||||
}
|
||||
|
||||
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) {
|
||||
holder.bind(it.data, position)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is GameArticleViewHolder -> {
|
||||
getRealTypeItem<FollowArticleItem>(position) {
|
||||
holder.bind(it.data)
|
||||
}
|
||||
}
|
||||
|
||||
is GiftPackViewHolder -> {
|
||||
getRealTypeItem<FollowGiftPackItem>(position) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ReadStatusUpdate(
|
||||
val ids: List<String>
|
||||
)
|
||||
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
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.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.display(item.border, item.icon, item.auth?.icon)
|
||||
|
||||
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.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 }
|
||||
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,205 @@
|
||||
package com.gh.gamecenter.forum.home.follow.adapter
|
||||
|
||||
import android.content.Context
|
||||
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.goneIf
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
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.model.FollowRepository.Companion.FOLLOW_UPDATE_TYPE_USER
|
||||
|
||||
class FollowedHeaderAdapter(
|
||||
private val context: Context,
|
||||
private val listener: OnChildEventListener
|
||||
) :
|
||||
ListAdapter<FollowUserEntity, ViewHolder>(createItemCallback()) {
|
||||
|
||||
/**
|
||||
* 1.当 _selectedPosition 为 -1 时
|
||||
* 不显示 viewAll 按钮
|
||||
* 显示选中框
|
||||
*
|
||||
* 2. 当_selectedPosition 为 -2 时
|
||||
* 不显示 viewAll 按钮
|
||||
* 暂无选中框
|
||||
*/
|
||||
private var _selectedPosition = -1
|
||||
|
||||
private val showAll: Boolean
|
||||
get() = _selectedPosition == -1
|
||||
|
||||
fun submitList(data: List<FollowUserEntity>, selectedPosition: Int = -1) {
|
||||
_selectedPosition = selectedPosition
|
||||
super.submitList(data)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = if (showAll) super.getItemCount() + 1 else super.getItemCount()
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (showAll) {
|
||||
if (position < currentList.size) {
|
||||
if (getItem(position).type == FOLLOW_UPDATE_TYPE_USER) {
|
||||
VIEW_TYPE_BBS
|
||||
} else {
|
||||
VIEW_TYPE_GAME
|
||||
}
|
||||
} else {
|
||||
VIEW_TYPE_VIEW_ALL
|
||||
}
|
||||
} else {
|
||||
if (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(getItem(position), position, _selectedPosition == position, showAll)
|
||||
}
|
||||
|
||||
is FollowedPersonViewHolder -> {
|
||||
holder.bindView(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 fun createItemCallback() = object : ItemCallback<FollowUserEntity>() {
|
||||
override fun areItemsTheSame(oldItem: FollowUserEntity, newItem: FollowUserEntity): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: FollowUserEntity, newItem: FollowUserEntity): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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.ivIcon.displayGameIcon(item.icon, null)
|
||||
val resourceId = if (isSelected) {
|
||||
R.drawable.background_shape_theme_radius_12
|
||||
} else {
|
||||
R.drawable.background_shape_white_radius_12
|
||||
}
|
||||
binding.gIndicator.goneIf(!item.isShowTip)
|
||||
|
||||
binding.vIconBackground.setBackgroundResource(resourceId)
|
||||
|
||||
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.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.setBackgroundResource(resourceId)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -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,263 @@
|
||||
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.DialogHelper
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.toArrayList
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
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 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 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()))
|
||||
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())
|
||||
},
|
||||
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()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_HAS_READ_ID = "key_has_read_id"
|
||||
}
|
||||
|
||||
private class MyItemDecoration(private val context: Context) : ItemDecoration() {
|
||||
|
||||
private val paint = Paint().apply {
|
||||
color = R.color.text_primary.toColor(context)
|
||||
textSize = 14F.dip2px().toFloat()
|
||||
}
|
||||
|
||||
private val overPaint = Paint().apply {
|
||||
color = R.color.ui_surface.toColor(context)
|
||||
}
|
||||
|
||||
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.itemCount})"
|
||||
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,270 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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,264 @@
|
||||
package com.gh.gamecenter.forum.home.follow.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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 com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.utils.clearHtmlFormatCompletely
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.removeInsertedContent
|
||||
import com.gh.gamecenter.common.utils.removeVideoContent
|
||||
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.entity.ArticleDetailEntity
|
||||
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
|
||||
class FollowDynamicListFragment : Fragment() {
|
||||
|
||||
private var userId: String = ""
|
||||
private var bbsId: String = ""
|
||||
|
||||
private val viewModel by viewModels<FollowDynamicListViewModel>()
|
||||
|
||||
private val followDynamicViewModel by activityViewModels<FollowDynamicViewModel>()
|
||||
|
||||
private lateinit var binding: FragmentFollowDynamicListBinding
|
||||
|
||||
private val adapter by lazy {
|
||||
FollowDynamicAdapter(requireContext(), viewModel)
|
||||
}
|
||||
|
||||
private lateinit var launcher: FollowActivityResultLauncher
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
userId = arguments?.getString(KEY_USER_ID) ?: ""
|
||||
bbsId = arguments?.getString(KEY_BBS_ID) ?: ""
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return FragmentFollowDynamicListBinding.inflate(inflater, container, false)
|
||||
.also {
|
||||
binding = it
|
||||
}.root
|
||||
}
|
||||
|
||||
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 {
|
||||
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.adapter = adapter
|
||||
|
||||
binding.srlRefresh.setOnRefreshListener {
|
||||
viewModel.loadFirst(userId, bbsId, true)
|
||||
}
|
||||
|
||||
binding.ivClose.setOnClickListener {
|
||||
followDynamicViewModel.finish()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_USER_ID = "key_user_id"
|
||||
private const val KEY_BBS_ID = "key_bbs_id"
|
||||
|
||||
fun newInstance(userId: String, bbsId: String): FollowDynamicListFragment {
|
||||
val fragment = FollowDynamicListFragment()
|
||||
val bundle = Bundle().apply {
|
||||
putString(KEY_USER_ID, userId)
|
||||
putString(KEY_BBS_ID, bbsId)
|
||||
}
|
||||
fragment.arguments = bundle
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,477 @@
|
||||
package com.gh.gamecenter.forum.home.follow.fragment
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
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.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.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.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.UserViewModel
|
||||
import com.gh.gamecenter.message.view.concern.ConcernFragment
|
||||
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 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 ->
|
||||
val imgs = concernEntity.img
|
||||
if (!imgs.isNullOrEmpty()) {
|
||||
ShareCardPicActivity.startShareCardPicActivity(requireContext(), concernEntity, mEntrance)
|
||||
} else {
|
||||
val shareContent =
|
||||
if (concernEntity.brief != null) {
|
||||
concernEntity.brief ?: ""
|
||||
} else {
|
||||
concernEntity.content ?: ""
|
||||
}
|
||||
startActivity(ShareCardActivity.getIntent(context, concernEntity, shareContent))
|
||||
}
|
||||
})
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
private fun setDataList(dataList: List<FollowItem>) {
|
||||
binding?.run {
|
||||
if (recyclerView.adapter == null) {
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
adapter.submitList(dataList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun scrollToTop() {
|
||||
binding?.recyclerView?.scrollToPosition(0)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onUserFollow(change: EBUserFollow) {
|
||||
adapter.notifyFollowUserChanged(change)
|
||||
}
|
||||
|
||||
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,49 @@
|
||||
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.FollowUserEntity
|
||||
import com.gh.gamecenter.feature.entity.Auth
|
||||
import com.gh.gamecenter.feature.entity.UserEntity
|
||||
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<Top>): 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)
|
||||
|
||||
data class Top(
|
||||
val _id: String,
|
||||
val order: Int
|
||||
)
|
||||
}
|
||||
@ -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,233 @@
|
||||
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.UrlFilterUtils
|
||||
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())
|
||||
.flatMap {
|
||||
loadGiftPackStatusIfNeed(it)
|
||||
}.map(::transformFollowUpdatesToItemData)
|
||||
|
||||
fun loadGiftPackStatusIfNeed(data: List<FollowDynamicEntity>): Single<List<FollowDynamicEntity>> {
|
||||
val libaoList = arrayListOf<LibaoEntity>()
|
||||
data.forEach {
|
||||
if (it.libao != null) {
|
||||
libaoList.add(it.libao)
|
||||
}
|
||||
if (it.libaoExchange != null) {
|
||||
libaoList.add(it.libaoExchange)
|
||||
}
|
||||
}
|
||||
val builder = StringBuilder()
|
||||
var i = 0
|
||||
val size = libaoList.size
|
||||
while (i < size) {
|
||||
builder.append(libaoList[i].id)
|
||||
builder.append("-")
|
||||
i++
|
||||
}
|
||||
if (builder.isEmpty()) {
|
||||
return Single.just(data)
|
||||
}
|
||||
builder.deleteCharAt(builder.length - 1)
|
||||
val ids = builder.toString()
|
||||
return apiService.getLibaoStatus(UrlFilterUtils.getFilterQuery("libao_ids", ids))
|
||||
.map { statusList ->
|
||||
LibaoUtils.initLiBaoEntity(statusList, libaoList)
|
||||
data
|
||||
}.single(listOf())
|
||||
}
|
||||
|
||||
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.add(
|
||||
FollowCommonCollectionItem(
|
||||
it.linkCommonCollection ?: CustomPageData.CommonContentCollection()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
private fun convertSplitCommonContentCollection(item: CustomPageData.CustomsComponent): List<FollowSplitCommonContentCollectionItem> {
|
||||
val linkCommonCollection = item.linkCommonCollection ?: return emptyList()
|
||||
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 -> FollowArticleItem(it.article)
|
||||
|
||||
FOLLOW_UPDATE_TYPE_USER_POST -> FollowPostCardItem(it.userPost)
|
||||
|
||||
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,104 @@
|
||||
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.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: CustomPageData.CommonContentCollection
|
||||
|
||||
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.id,
|
||||
_data.name,
|
||||
"",
|
||||
"",
|
||||
"关注推荐列表",
|
||||
"关注页",
|
||||
linkEntity.title ?: "",
|
||||
entity.addedContent1 ?: "",
|
||||
entity.addedContent2 ?: "",
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.text ?: "",
|
||||
childPosition + 1
|
||||
)
|
||||
NewLogUtils.logCommonCategoryHomeContentClick(
|
||||
entity.title,
|
||||
linkEntity.type ?: "",
|
||||
linkEntity.link ?: "",
|
||||
linkEntity.text ?: "",
|
||||
_data.name,
|
||||
_data.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, "内容卡片", null)
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection) {
|
||||
listener.navigateToCommonCollectionDetailPage(data)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: CustomPageData.CommonContentCollection) {
|
||||
_data = data
|
||||
horizontalSlideUi.bind(_data)
|
||||
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.currentList
|
||||
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,55 @@
|
||||
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.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: CustomPageData.CommonContentCollection
|
||||
|
||||
private val recommendUi by lazy {
|
||||
CommonContentRecommendUi(binding, object : CommonContentRecommendUi.OnRecommendListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(bindingAdapterPosition, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: CustomPageData.CommonContentCollection) {
|
||||
this.data = data
|
||||
val recommends = data.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,79 @@
|
||||
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.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: CustomPageData.CommonContentCollection
|
||||
|
||||
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, 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: CustomPageData.CommonContentCollection) {
|
||||
this.data = data
|
||||
|
||||
val slideList = data.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,74 @@
|
||||
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.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: CustomPageData.CommonContentCollection
|
||||
|
||||
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, text, null)
|
||||
}
|
||||
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(bindingAdapterPosition, link, data, text, exposureEvent)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: CustomPageData.CommonContentCollection) {
|
||||
this.data = data
|
||||
slideWithCardsUi.bind(data, 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,50 @@
|
||||
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.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: CustomPageData.CommonContentCollection
|
||||
|
||||
private val navigationUi by lazy {
|
||||
CommonContentNavigationUi(binding, object : CommonContentNavigationUi.OnNavigationListener {
|
||||
override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) {
|
||||
listener.navigateToLinkPage(bindingAdapterPosition, link, data, text, exposureEvent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun bindView(data: CustomPageData.CommonContentCollection) {
|
||||
this.data = data
|
||||
val navigationList = data.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,122 @@
|
||||
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.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
|
||||
if (articleEntity.type == "bbs_article") articleEntity.type = "community_article"
|
||||
if (articleEntity.type == "bbs_question") articleEntity.type = "question"
|
||||
if (articleEntity.type == "bbs_video") articleEntity.type = "video"
|
||||
|
||||
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
|
||||
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 {
|
||||
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,96 @@
|
||||
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.common.utils.toArrayList
|
||||
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.id)
|
||||
}
|
||||
|
||||
binding.tvComment.setOnClickListener {
|
||||
listener.onComment(article.id)
|
||||
}
|
||||
|
||||
binding.tvShare.setOnClickListener {
|
||||
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
|
||||
brief = article.content
|
||||
game = simpleGame
|
||||
gameIcon = article.game.icon
|
||||
img = article.img
|
||||
}
|
||||
}
|
||||
|
||||
interface OnChildEventListener {
|
||||
|
||||
fun onGameCLick(game: GameEntity)
|
||||
|
||||
fun onArticleClick(articleId: String)
|
||||
|
||||
fun onComment(articleId: String)
|
||||
|
||||
fun onShare(concernEntity: ConcernEntity)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.gh.gamecenter.forum.home.follow.viewholder
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.common.util.NewsUtils
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
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(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,111 @@
|
||||
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.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<AllFollowRepository.Top>) {
|
||||
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进入游戏详情
|
||||
*/
|
||||
|
||||
@ -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
|
||||
binding.recyclerView.scrollToPosition(currentPosition)
|
||||
|
||||
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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,7 @@ 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.FollowDynamicEntity;
|
||||
import com.gh.gamecenter.entity.FollowersOrFansEntity;
|
||||
import com.gh.gamecenter.entity.ForumActivityCategoryEntity;
|
||||
import com.gh.gamecenter.entity.ForumActivityEntity;
|
||||
@ -103,6 +104,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 +121,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 +147,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 +765,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<CustomPageData.CustomsComponent>> 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 +3290,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>
|
||||
BIN
app/src/main/res/drawable-xxxhdpi/ic_all_followed_down.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_all_followed_down.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_all_followed_drag.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_all_followed_drag.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 287 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_all_followed_up.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_all_followed_up.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_follow_detail_indicator.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_follow_detail_indicator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 480 B |
5
app/src/main/res/drawable/bg_gift_pack_code.xml
Normal file
5
app/src/main/res/drawable/bg_gift_pack_code.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/ui_container_1" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="8dp"/>
|
||||
<stroke android:color="@color/ui_divider"
|
||||
android:width="1dp"/>
|
||||
</shape>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="6dp" />
|
||||
<solid android:color="@color/secondary_green" />
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/ic_follow_arrow_down.xml
Normal file
9
app/src/main/res/drawable/ic_follow_arrow_down.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="8dp"
|
||||
android:height="8dp"
|
||||
android:viewportWidth="8"
|
||||
android:viewportHeight="8">
|
||||
<path
|
||||
android:pathData="M6.4395,2C7.0684,2 7.418,2.7274 7.0252,3.2185L4.5856,6.2679C4.2854,6.6432 3.7146,6.6432 3.4144,6.2679L0.9748,3.2185C0.582,2.7274 0.9316,2 1.5605,2H6.4395Z"
|
||||
android:fillColor="@color/text_primary"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_follow_arrow_up.xml
Normal file
9
app/src/main/res/drawable/ic_follow_arrow_up.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="8dp"
|
||||
android:height="8dp"
|
||||
android:viewportWidth="8"
|
||||
android:viewportHeight="8">
|
||||
<path
|
||||
android:pathData="M6.4395,6C7.0684,6 7.418,5.2726 7.0252,4.7815L4.5856,1.7321C4.2854,1.3568 3.7146,1.3568 3.4144,1.7321L0.9748,4.7815C0.582,5.2726 0.9316,6 1.5605,6H6.4395Z"
|
||||
android:fillColor="#333333"/>
|
||||
</vector>
|
||||
18
app/src/main/res/drawable/ic_game_article_share.xml
Normal file
18
app/src/main/res/drawable/ic_game_article_share.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M20,12.78C20,14.814 19.742,16.589 19.487,17.842C19.237,19.076 18.296,20.017 17.062,20.267C15.809,20.522 14.033,20.78 12,20.78C9.966,20.78 8.191,20.522 6.938,20.267C5.704,20.017 4.763,19.076 4.513,17.842C4.258,16.589 4,14.814 4,12.78C4,10.747 4.258,8.971 4.513,7.718C4.763,6.484 5.704,5.543 6.938,5.293C8.191,5.038 9.966,4.78 12,4.78"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#999999"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M16,4.28C15.586,4.28 15.25,4.616 15.25,5.03C15.25,5.444 15.586,5.78 16,5.78V4.28ZM19.25,5.53L19.946,5.251C19.87,5.061 19.719,4.91 19.528,4.834L19.25,5.53ZM19,8.78C19,9.194 19.336,9.53 19.75,9.53C20.164,9.53 20.5,9.194 20.5,8.78H19ZM14.97,8.75C14.677,9.043 14.677,9.517 14.97,9.81C15.263,10.103 15.737,10.103 16.03,9.81L14.97,8.75ZM19.53,6.31C19.823,6.017 19.823,5.543 19.53,5.25C19.237,4.957 18.763,4.957 18.47,5.25L19.53,6.31ZM16,5.78C16.938,5.78 17.695,5.897 18.212,6.012C18.471,6.07 18.668,6.126 18.798,6.167C18.862,6.188 18.91,6.204 18.939,6.214C18.954,6.22 18.964,6.223 18.969,6.226C18.972,6.227 18.973,6.227 18.974,6.227C18.974,6.227 18.974,6.227 18.974,6.227C18.973,6.227 18.973,6.227 18.973,6.227C18.973,6.227 18.972,6.227 18.972,6.227C18.972,6.227 18.972,6.227 18.972,6.227C18.972,6.226 18.972,6.226 19.25,5.53C19.528,4.834 19.528,4.834 19.528,4.834C19.528,4.833 19.528,4.833 19.528,4.833C19.527,4.833 19.527,4.833 19.527,4.833C19.526,4.833 19.525,4.832 19.525,4.832C19.523,4.831 19.521,4.831 19.519,4.83C19.514,4.828 19.509,4.826 19.502,4.823C19.488,4.818 19.469,4.811 19.446,4.803C19.399,4.786 19.333,4.763 19.249,4.737C19.082,4.684 18.842,4.615 18.538,4.548C17.93,4.413 17.062,4.28 16,4.28V5.78ZM19.25,5.53C18.554,5.809 18.554,5.808 18.553,5.808C18.553,5.808 18.553,5.808 18.553,5.808C18.553,5.808 18.553,5.807 18.553,5.807C18.553,5.807 18.553,5.807 18.553,5.806C18.553,5.806 18.553,5.806 18.553,5.806C18.553,5.807 18.553,5.808 18.555,5.811C18.556,5.816 18.56,5.826 18.566,5.841C18.576,5.87 18.593,5.918 18.613,5.982C18.654,6.112 18.71,6.309 18.768,6.568C18.883,7.085 19,7.842 19,8.78H20.5C20.5,7.718 20.367,6.85 20.232,6.242C20.165,5.938 20.096,5.698 20.043,5.531C20.017,5.447 19.994,5.381 19.977,5.334C19.969,5.311 19.962,5.292 19.957,5.278C19.954,5.271 19.952,5.266 19.95,5.261C19.949,5.259 19.949,5.257 19.948,5.255C19.948,5.255 19.947,5.254 19.947,5.253C19.947,5.253 19.947,5.253 19.947,5.252C19.947,5.252 19.947,5.252 19.947,5.252C19.946,5.252 19.946,5.251 19.25,5.53ZM16.03,9.81L19.53,6.31L18.47,5.25L14.97,8.75L16.03,9.81Z"
|
||||
android:strokeAlpha="0.6"
|
||||
android:fillColor="#999999"
|
||||
android:fillAlpha="0.6"/>
|
||||
</vector>
|
||||
68
app/src/main/res/layout/fragment_all_followed.xml
Normal file
68
app/src/main/res/layout/fragment_all_followed.xml
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_surface"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:id="@+id/status_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="12dp"
|
||||
android:src="@drawable/ic_bar_back"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_bar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
style="@style/TextHeadline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/my_follow"
|
||||
android:textColor="@color/text_primary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_back"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_back" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_right"
|
||||
style="@style/TextBody2"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/manager"
|
||||
android:textColor="@color/text_theme"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_back"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_back" />
|
||||
|
||||
<View
|
||||
android:id="@+id/v_line"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0.3dp"
|
||||
android:background="@color/ui_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_back" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_followed"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_line" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/communityHomeContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -35,7 +36,7 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/tabContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:paddingBottom="5dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
@ -55,15 +56,17 @@
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
android:layout_width="184dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginEnd="96dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_back"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_goneMarginStart="4dp"
|
||||
app:tabIndicator="@null"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMinWidth="0dp"
|
||||
app:layout_goneMarginStart="4dp"
|
||||
app:tabMode="scrollable"
|
||||
app:tabTextAppearance="@style/TabLayoutTextAppearance" />
|
||||
|
||||
@ -91,7 +94,8 @@
|
||||
app:layout_constraintBottom_toBottomOf="@+id/tabContainer"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/tabContainer">
|
||||
app:layout_constraintTop_toTopOf="@+id/tabContainer"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/videoLottie"
|
||||
@ -115,6 +119,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="0dp"
|
||||
|
||||
56
app/src/main/res/layout/fragment_follow_dynamic.xml
Normal file
56
app/src/main/res/layout/fragment_follow_dynamic.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:id="@+id/status_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_top_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/ui_background"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/bg_forum_home_top"
|
||||
app:layout_constraintBottom_toBottomOf="@id/rv_followed_header"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_followed_header"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_bar"
|
||||
tools:listitem="@layout/recycler_followed_list_bbs" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_indicator"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="8dp"
|
||||
android:src="@drawable/ic_follow_detail_indicator"
|
||||
app:layout_constraintBottom_toBottomOf="@id/rv_followed_header"
|
||||
app:layout_constraintEnd_toStartOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/view_pager"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0"
|
||||
android:translationY="75dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_followed_header" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
104
app/src/main/res/layout/fragment_follow_dynamic_list.xml
Normal file
104
app/src/main/res/layout/fragment_follow_dynamic_list.xml
Normal file
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/v_header_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/background_shape_white_radius_8_top_only"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/icon_close"
|
||||
app:layout_constraintStart_toStartOf="@id/v_header_background"
|
||||
app:layout_constraintTop_toTopOf="@id/v_header_background" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
style="@style/TextHeadline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_primary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_close"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_header_background"
|
||||
app:layout_constraintStart_toStartOf="@id/v_header_background"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_close"
|
||||
tools:text="@string/follow_detail_person_updates" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_right"
|
||||
style="@style/TextBody2"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/go_to_forum"
|
||||
android:textColor="@color/text_theme"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_close"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_header_background"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_close" />
|
||||
|
||||
<View
|
||||
android:id="@+id/v_top_line"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/ui_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_close" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/srl_refresh"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_top_line">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_dynamic"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_no_connection"
|
||||
layout="@layout/reuse_no_connection"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_top_line" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_loading"
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_top_line" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_no_data"
|
||||
layout="@layout/reuse_none_data"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_top_line" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user