356 lines
13 KiB
Kotlin
356 lines
13 KiB
Kotlin
package com.gh.common.view
|
|
|
|
import android.content.Context
|
|
import android.util.AttributeSet
|
|
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.gamecenter.R
|
|
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.ImageInfo
|
|
import com.gh.gamecenter.login.user.UserManager
|
|
|
|
class ImageContainerView : LinearLayout {
|
|
|
|
private var data: ImageContainerData? = null
|
|
|
|
//三图默认宽度
|
|
private var mDefaultWidth = 0f
|
|
|
|
//单图固定宽度
|
|
private var mFixdWidth = 0f
|
|
|
|
//最小比例
|
|
private var mMinRatio = 3 / 4f
|
|
|
|
//最大比例
|
|
private var mMaxRatio = 3 / 2f
|
|
|
|
//长图比例
|
|
private var mLongPictureRatio = 9 / 18f
|
|
|
|
//图片之间的间距
|
|
private val mItemSpace = 4f.dip2px()
|
|
private var mOffset = 0
|
|
private var index = 0
|
|
|
|
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) {
|
|
initView(attrs)
|
|
}
|
|
|
|
private fun initView(attrs: AttributeSet?) {
|
|
orientation = HORIZONTAL
|
|
val ta = context.obtainStyledAttributes(attrs, R.styleable.ImageContainerView)
|
|
mOffset = ta.getDimensionPixelSize(R.styleable.ImageContainerView_offset, 0)
|
|
calculateWidth()
|
|
ta.recycle()
|
|
}
|
|
|
|
private fun calculateWidth() {
|
|
mDefaultWidth = (DisplayUtils.getScreenWidth() - mOffset.toFloat() - mItemSpace * 2) / 3
|
|
mFixdWidth = (DisplayUtils.getScreenWidth() - mOffset.toFloat() - mItemSpace * 2) * 2 / 3
|
|
}
|
|
|
|
fun setOffset(offset: Float) {
|
|
mOffset = offset.dip2px()
|
|
calculateWidth()
|
|
}
|
|
|
|
fun bindData(
|
|
data: ImageContainerData,
|
|
listener: OnImageContainerEventListener? = null
|
|
) {
|
|
this.data = data
|
|
onImageContainerEventListener = listener
|
|
imageViewList.clear()
|
|
removeAllViews()
|
|
index = 0
|
|
if (!data.show) {
|
|
visibility = View.GONE
|
|
return
|
|
}
|
|
visibility = View.VISIBLE
|
|
if (data.isPostCard) {
|
|
//若文章内容含有图片及视频,则信息流卡片,仅展示图片,且标题后带有‘有视频’标签
|
|
//若文章内容仅含有图片,则信息流卡片,仅展示图片,无标签
|
|
//若文章内容仅含有视频,则信息流卡片,仅展示视频,无标签
|
|
when {
|
|
data.images.isNotEmpty() -> {
|
|
data.images.take(3)
|
|
.forEach {
|
|
bindImage(it.url, it.width, it.height, data.images.size == 1)
|
|
}
|
|
}
|
|
|
|
data.video != null -> {
|
|
val video = data.video
|
|
bindVideo(video, video.width, video.height, true)
|
|
}
|
|
|
|
else -> {
|
|
// do noting
|
|
}
|
|
}
|
|
} else {
|
|
//若问答内容含有图片及视频,则信息流卡片,同时展示图片及视频,且参考以往排序逻辑(视频优先放置第一位),无标签
|
|
//若问答内容仅含有图片,则信息流卡片,仅展示图片,无标签
|
|
//若问答内容仅含有视频,则信息流卡片,仅展示视频,无标签
|
|
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 {
|
|
data.images.take(3)
|
|
.forEach {
|
|
bindImage(it.url, it.width, it.height, data.images.size == 1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
} else {
|
|
ItemCommunityImageBinding.inflate(LayoutInflater.from(context), null, false)
|
|
}
|
|
if (oldView == null) {
|
|
addView(binding.root)
|
|
}
|
|
binding.durationOrNumTv.visibility = View.VISIBLE
|
|
binding.videoPlay.visibility = View.VISIBLE
|
|
binding.labelIcon.visibility = View.GONE
|
|
binding.durationOrNumTv.text = video.duration
|
|
displayImage(binding, video.poster, width.toFloat(), height.toFloat(), isChangeRatio, true)
|
|
binding.root.setOnClickListener {
|
|
debounceActionWithInterval(it.id, 1000) {
|
|
onImageContainerEventListener?.onVideoCLick(video.id)
|
|
}
|
|
}
|
|
index++
|
|
}
|
|
|
|
private fun bindImage(
|
|
url: String,
|
|
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)
|
|
} else {
|
|
ItemCommunityImageBinding.inflate(LayoutInflater.from(context), null, false)
|
|
}
|
|
binding.root.tag = index
|
|
if (oldView == null) {
|
|
addView(binding.root)
|
|
}
|
|
imageViewList.add(binding.image)
|
|
binding.durationOrNumTv.visibility = View.GONE
|
|
binding.videoPlay.visibility = View.GONE
|
|
displayImage(binding, url, width.toFloat(), height.toFloat(), isChangeRatio)
|
|
binding.root.setOnClickListener {
|
|
if (data?.status == "pending" || data?.status == "fail") return@setOnClickListener
|
|
debounceActionWithInterval(it.id, 1000) {
|
|
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
|
|
)
|
|
}
|
|
|
|
}
|
|
}
|
|
index++
|
|
}
|
|
|
|
private fun displayImage(
|
|
binding: ItemCommunityImageBinding,
|
|
url: String,
|
|
width: Float,
|
|
height: Float,
|
|
isChangeRatio: Boolean,
|
|
isVideo: Boolean = false
|
|
) {
|
|
val params = binding.root.layoutParams as LayoutParams
|
|
val hierarchy = binding.image.hierarchy
|
|
binding.labelIcon.visibility = View.GONE
|
|
if (width != 0f && height != 0f) {
|
|
val picRatio = width / height
|
|
if (isChangeRatio) {
|
|
when {
|
|
picRatio <= mMinRatio -> {
|
|
params.height = (mFixdWidth / mMinRatio).toInt()
|
|
if (binding.image.tag != url) {
|
|
ImageUtils.display(binding.image, url, false, TopCutProcess(mMinRatio))
|
|
}
|
|
}
|
|
|
|
picRatio >= mMaxRatio -> {
|
|
params.height = (mFixdWidth / mMaxRatio).toInt()
|
|
ImageUtils.display(binding.image, url, false)
|
|
}
|
|
|
|
else -> {
|
|
params.height = (mFixdWidth / picRatio).toInt()
|
|
ImageUtils.display(binding.image, url, false)
|
|
}
|
|
}
|
|
params.width = mFixdWidth.toInt()
|
|
} else {
|
|
params.width = mDefaultWidth.toInt()
|
|
params.height = mDefaultWidth.toInt()
|
|
if (width > height) {
|
|
val loadImageWidth = width * (mDefaultWidth.toInt() / height)
|
|
binding.image.setTag(ImageUtils.TAG_TARGET_WIDTH, loadImageWidth.toInt())
|
|
}
|
|
ImageUtils.display(binding.image, url, false)
|
|
}
|
|
//长图
|
|
if (!isVideo && picRatio <= mLongPictureRatio) {
|
|
binding.labelIcon.visibility = View.VISIBLE
|
|
binding.labelIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_long_pic_label))
|
|
}
|
|
} else {
|
|
params.width = mDefaultWidth.toInt()
|
|
params.height = mDefaultWidth.toInt()
|
|
ImageUtils.display(binding.image, url, false)
|
|
}
|
|
|
|
if (url.endsWith(".gif")) {
|
|
binding.gifBorder.visibility = View.VISIBLE
|
|
binding.labelIcon.visibility = View.VISIBLE
|
|
binding.labelIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_gif_label))
|
|
}
|
|
|
|
binding.pendingView.run {
|
|
when (data?.status) {
|
|
"pending" -> {
|
|
visibility = View.VISIBLE
|
|
text = com.gh.gamecenter.feature.R.string.pending_status.toResString()
|
|
}
|
|
|
|
"fail" -> {
|
|
visibility = View.VISIBLE
|
|
text = R.string.fail_status.toResString()
|
|
}
|
|
|
|
else -> {
|
|
visibility = View.GONE
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
val imageCount = data?.images?.size ?: 0
|
|
if (!isVideo && index == 2 && imageCount > 3) {
|
|
binding.labelIcon.visibility = View.GONE
|
|
binding.durationOrNumTv.visibility = View.VISIBLE
|
|
binding.durationOrNumTv.text = "+${imageCount - 3}"
|
|
}
|
|
|
|
hierarchy.actualImageScaleType = ScalingUtils.ScaleType.CENTER_CROP
|
|
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)
|
|
}
|
|
} |