tagFlow 展开按钮添加

This commit is contained in:
renhaoting 2025-10-21 14:57:36 +08:00
parent bb87a3d138
commit 2426f6b8bd
8 changed files with 95 additions and 90 deletions

View File

@ -1,6 +1,7 @@
package com.remax.visualnovel.configs
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import androidx.multidex.MultiDex
import androidx.multidex.MultiDexApplication
@ -28,6 +29,9 @@ class NovelApplication : MultiDexApplication() {
fun getCurrentActivity(): Activity? {
return currentActivity?.get()
}
private lateinit var instance: NovelApplication
fun appContext(): Context = instance
}
private val proxies = listOf<ApplicationProxy>(CommonApplicationProxy)
@ -38,6 +42,7 @@ class NovelApplication : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
MultiDex.install(this)
instance = this
proxies.forEach { it.onCreate(this) }
appInitializersProvider.startInit()
}

View File

@ -0,0 +1,11 @@
package com.remax.visualnovel.extension
import com.remax.visualnovel.configs.NovelApplication
private val resources = NovelApplication.appContext().resources
// dp, pixcel 相关
fun Float.dpToPx(): Float = this * resources.displayMetrics.density
fun Int.dpToPx(): Int = this * resources.displayMetrics.density.toInt()
fun Float.spToPx(): Float = this * resources.displayMetrics.scaledDensity

View File

@ -9,8 +9,6 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.dylanc.loadingstateview.BgColorType
import com.remax.visualnovel.app.base.BaseBindingFragment
import com.remax.visualnovel.databinding.FragmentMainActorBinding
import com.remax.visualnovel.databinding.FragmentMainBookBinding
import com.remax.visualnovel.ui.main.book.BookListViewModel
import com.remax.visualnovel.utils.Routers
import com.remax.visualnovel.widget.custom.TagItem
import dagger.hilt.android.AndroidEntryPoint
@ -56,7 +54,7 @@ class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
TagItem("8", "LastLine"),
)
tagFlowLayout.setTags(tags)
tagFlowLayout.setTagDataList(tags)
tagFlowLayout.setOnTagClickListener { tag ->
Toast.makeText(context, "Clicked: ${tag.text}", Toast.LENGTH_SHORT).show()

View File

@ -7,11 +7,16 @@ import android.graphics.drawable.GradientDrawable
import android.text.TextUtils
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
import com.remax.visualnovel.R
import com.remax.visualnovel.extension.dpToPx
import com.remax.visualnovel.extension.spToPx
import kotlin.math.max
import kotlin.math.min
import androidx.core.content.withStyledAttributes
class TagFlowLayout2 @JvmOverloads constructor(
context: Context,
@ -20,26 +25,27 @@ class TagFlowLayout2 @JvmOverloads constructor(
) : ViewGroup(context, attrs, defStyleAttr) {
// 属性变量
private var horizontalSpacing = 20f.dpToPx()
private var verticalSpacing = 12f.dpToPx()
private var textSize = 14f.spToPx()
private var textColor = Color.WHITE
private var horizontalSpacing = 10f.dpToPx()
private var verticalSpacing = 8f.dpToPx()
private var textSize = 12f.spToPx()
private var textColor = context.resources.getColor(R.color.tag_text_color)
private var tagBackground: Drawable? = null
private var maxLines = Int.MAX_VALUE
private var maxLinesWhileShrink = 1
private var eachLineMaxTagNum = 2
private var expandIndicator: Drawable? = null
private var collapseIndicator: Drawable? = null
private var eachLineMaxTagNum = 2 //一行最大标签宽度数
private var shrinkIndicator: Drawable? = null
// 状态变量
private var isExpanded = false
private var actualLineCount = 0
private var showExpandButton = false
private var eachLineAvailableWidth = 0 // 可用宽度
private var eachLineAvailableWidth = 0
// 数据
private val tagItems = mutableListOf<TagItem>()
private val tagViews = mutableListOf<TextView>()
private lateinit var expandButton: TextView
private lateinit var expandIcon: ImageView
// 监听器
private var onTagClickListener: ((TagItem) -> Unit)? = null
@ -51,26 +57,27 @@ class TagFlowLayout2 @JvmOverloads constructor(
}
private fun initAttributes(attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout2)
context.withStyledAttributes(attrs, R.styleable.TagFlowLayout2) {
horizontalSpacing = typedArray.getDimension(
R.styleable.TagFlowLayout2_tag_horizontal_spacing, horizontalSpacing
)
verticalSpacing = typedArray.getDimension(
R.styleable.TagFlowLayout2_tag_vertical_spacing, verticalSpacing
)
textSize = typedArray.getDimension(
R.styleable.TagFlowLayout2_tag_text_size, textSize
)
textColor = typedArray.getColor(
R.styleable.TagFlowLayout2_tag_text_color, textColor
)
tagBackground = typedArray.getDrawable(R.styleable.TagFlowLayout2_tag_background)
maxLines = typedArray.getInt(R.styleable.TagFlowLayout2_tag_max_lines, Int.MAX_VALUE)
expandIndicator = typedArray.getDrawable(R.styleable.TagFlowLayout2_expand_indicator_drawable)
collapseIndicator = typedArray.getDrawable(R.styleable.TagFlowLayout2_collapse_indicator_drawable)
eachLineMaxTagNum = typedArray.getInt(R.styleable.TagFlowLayout2_each_line_max_num, 2)
typedArray.recycle()
horizontalSpacing = getDimension(
R.styleable.TagFlowLayout2_tag_horizontal_spacing, horizontalSpacing
)
verticalSpacing = getDimension(
R.styleable.TagFlowLayout2_tag_vertical_spacing, verticalSpacing
)
textSize = getDimension(
R.styleable.TagFlowLayout2_tag_text_size, textSize
)
textColor = getColor(
R.styleable.TagFlowLayout2_tag_text_color, textColor
)
tagBackground = getDrawable(R.styleable.TagFlowLayout2_tag_background)
maxLinesWhileShrink = getInt(R.styleable.TagFlowLayout2_tag_max_lines, 1)
eachLineMaxTagNum = getInt(R.styleable.TagFlowLayout2_tag_each_line_max_num, 2)
expandIndicator = getDrawable(R.styleable.TagFlowLayout2_tag_expand_drawable)
shrinkIndicator = getDrawable(R.styleable.TagFlowLayout2_tag_shrink_drawable)
}
if (tagBackground == null) {
@ -79,8 +86,8 @@ class TagFlowLayout2 @JvmOverloads constructor(
if (expandIndicator == null) {
expandIndicator = ContextCompat.getDrawable(context, R.mipmap.tag_flow_expand)
}
if (collapseIndicator == null) {
collapseIndicator = ContextCompat.getDrawable(context, R.mipmap.tag_flow_shrink)
if (shrinkIndicator == null) {
shrinkIndicator = ContextCompat.getDrawable(context, R.mipmap.tag_flow_shrink)
}
}
@ -92,38 +99,26 @@ class TagFlowLayout2 @JvmOverloads constructor(
}
private fun initExpandButton() {
expandButton = TextView(context).apply {
text = "展开"
setCompoundDrawablesWithIntrinsicBounds(null, null, expandIndicator, null)
compoundDrawablePadding = 4.dpToPx()
setTextColor(Color.parseColor("#8A8A8E"))
expandIcon = AppCompatImageView(context).apply {
textSize = 12f
setPadding(12.dpToPx(), 6.dpToPx(), 8.dpToPx(), 6.dpToPx())
background = createExpandButtonBackground()
setPadding(5.dpToPx(), 5.dpToPx(), 5.dpToPx(), 5.dpToPx())
bringToFront()
setOnClickListener {
toggleExpandState()
}
}
addView(expandButton)
addView(expandIcon)
updateExpandButton()
}
private fun createExpandButtonBackground(): Drawable {
val gradientDrawable = GradientDrawable()
gradientDrawable.cornerRadius = 16f.dpToPx()
gradientDrawable.setColor(Color.parseColor("#E5E5EA"))
gradientDrawable.setStroke(1.dpToPx(), Color.parseColor("#C6C6C8"))
return gradientDrawable
}
// 设置标签数据
fun setTags(tags: List<TagItem>) {
fun setTagDataList(tags: List<TagItem>) {
tagItems.clear()
tagViews.forEach { removeView(it) }
tagViews.clear()
tagItems.addAll(tags)
tags.forEach { tag ->
val textView = createTagView(tag)
tagViews.add(textView)
@ -138,7 +133,7 @@ class TagFlowLayout2 @JvmOverloads constructor(
text = tag.text
setTextColor(textColor)
textSize = textSize / resources.displayMetrics.scaledDensity
setPadding(16.dpToPx(), 8.dpToPx(), 16.dpToPx(), 8.dpToPx())
setPadding(10.dpToPx(), 7.dpToPx(), 10.dpToPx(), 7.dpToPx())
setBackgroundResource(R.drawable.tag_flow_item_bg)
isSingleLine = true
ellipsize = TextUtils.TruncateAt.END
@ -172,8 +167,8 @@ class TagFlowLayout2 @JvmOverloads constructor(
var curLineTotalWidth = 0
var curLineTotalHeight = 0
var lineCount = 0
val maxDisplayLines = if (isExpanded) Int.MAX_VALUE else maxLines
var lineIndex = 0
val maxDisplayLines = if (isExpanded) Int.MAX_VALUE else maxLinesWhileShrink
tagViews.forEach { view ->
val childWidth = view.measuredWidth
@ -181,16 +176,15 @@ class TagFlowLayout2 @JvmOverloads constructor(
// 检查是否需要换行(考虑水平间距)
val curLineNeedTotalWidth: Int = if (curLineTotalWidth == 0) childWidth
else curLineTotalWidth + horizontalSpacing.toInt() + childWidth
else curLineTotalWidth + horizontalSpacing.toInt() + childWidth
if (curLineNeedTotalWidth > eachLineAvailableWidth) {
// 换行处理
lineCount++
if (lineCount >= maxDisplayLines) {
if (curLineNeedTotalWidth > eachLineAvailableWidth) { // 换行处理
lineIndex++
if (lineIndex >= maxDisplayLines) {
return@forEach
}
totalNeedHeight += curLineTotalHeight + (if (lineCount >= 1) verticalSpacing.toInt() else 0)
totalNeedHeight += curLineTotalHeight + (if (lineIndex >= 1) verticalSpacing.toInt() else 0)
curLineTotalWidth = childWidth
curLineTotalHeight = childHeight
} else {
@ -200,21 +194,21 @@ class TagFlowLayout2 @JvmOverloads constructor(
}
// 添加最后一行高度
if (lineCount < maxDisplayLines && tagViews.isNotEmpty()) {
if (tagViews.isNotEmpty()) {
totalNeedHeight += curLineTotalHeight
}
// 添加padding
totalNeedHeight += paddingTop + paddingBottom
actualLineCount = lineCount + 1
showExpandButton = actualLineCount > maxLines && !isExpanded
actualLineCount = lineIndex + 1
showExpandButton = actualLineCount > maxLinesWhileShrink && !isExpanded
// 测量展开按钮
if (showExpandButton) {
val buttonWidthSpec = MeasureSpec.makeMeasureSpec(eachLineAvailableWidth, MeasureSpec.AT_MOST)
val buttonHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
expandButton.measure(buttonWidthSpec, buttonHeightSpec)
expandIcon.measure(buttonWidthSpec, buttonHeightSpec)
}
setMeasuredDimension(width, totalNeedHeight)
@ -230,7 +224,7 @@ class TagFlowLayout2 @JvmOverloads constructor(
var currentTop = paddingTop
var currentLineHeight = 0
var lineCount = 0
val maxDisplayLines = if (isExpanded) Int.MAX_VALUE else maxLines
val maxDisplayLines = if (isExpanded) Int.MAX_VALUE else maxLinesWhileShrink
// 布局可见的标签
for (i in tagViews.indices) {
@ -264,22 +258,22 @@ class TagFlowLayout2 @JvmOverloads constructor(
private fun layoutExpandButton(parentWidth: Int, currentTop: Int, currentLineHeight: Int) {
if (showExpandButton) {
expandButton.visibility = VISIBLE
val buttonWidth = expandButton.measuredWidth
val buttonHeight = expandButton.measuredHeight
expandIcon.visibility = VISIBLE
val buttonWidth = expandIcon.measuredWidth
val buttonHeight = expandIcon.measuredHeight
// 计算按钮位置(在当前行右侧)
val buttonLeft = parentWidth - paddingRight - buttonWidth
val buttonTop = currentTop + (currentLineHeight - buttonHeight) / 2
expandButton.layout(
expandIcon.layout(
buttonLeft,
buttonTop,
buttonLeft + buttonWidth,
buttonTop + buttonHeight
)
} else {
expandButton.visibility = GONE
expandIcon.visibility = GONE
}
}
@ -291,12 +285,10 @@ class TagFlowLayout2 @JvmOverloads constructor(
}
private fun updateExpandButton() {
val indicator = if (isExpanded) collapseIndicator else expandIndicator
expandButton.setCompoundDrawablesWithIntrinsicBounds(null, null, indicator, null)
expandButton.text = if (isExpanded) "收起" else "展开"
val indicator = if (isExpanded) shrinkIndicator else expandIndicator
expandIcon.setImageDrawable(indicator)
}
// 公共方法
fun setOnTagClickListener(listener: (TagItem) -> Unit) {
onTagClickListener = listener
}
@ -311,7 +303,7 @@ class TagFlowLayout2 @JvmOverloads constructor(
}
}
fun collapse() {
fun shrink() {
if (isExpanded) {
toggleExpandState()
}
@ -319,15 +311,11 @@ class TagFlowLayout2 @JvmOverloads constructor(
fun isExpanded(): Boolean = isExpanded
// 设置最大标签数
fun setMaxTagsNumEachLine(maxTagNumForEachLine: Int) {
require(maxTagNumForEachLine >= 1 && maxTagNumForEachLine <= 10) { "Ratio must be between 1 and 10" }
require(maxTagNumForEachLine >= 1 && maxTagNumForEachLine <= 10) { "tags numb in one line must be between 1 and 8" }
eachLineMaxTagNum = maxTagNumForEachLine
requestLayout()
}
// 扩展函数
private fun Float.dpToPx(): Float = this * resources.displayMetrics.density
private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).toInt()
private fun Float.spToPx(): Float = this * resources.displayMetrics.scaledDensity
}

View File

@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="15dp" />
<solid android:color="@color/tag_bg_gray" />
<solid android:color="@color/tag_bg_color" />
</shape>

View File

@ -36,9 +36,10 @@
app:tag_horizontal_spacing="10dp"
app:tag_vertical_spacing="8dp"
app:tag_text_size="12sp"
app:tag_max_lines="1"
app:tag_text_color="@color/white"
app:each_line_max_num="2"
app:expand_indicator_drawable="@mipmap/tag_flow_expand"
app:collapse_indicator_drawable="@mipmap/tag_flow_shrink" />
app:tag_each_line_max_num="2"
app:tag_expand_drawable="@mipmap/tag_flow_expand"
app:tag_shrink_drawable="@mipmap/tag_flow_shrink" />
</LinearLayout>

View File

@ -1441,9 +1441,9 @@
<attr name="tag_text_color" format="color" />
<attr name="tag_background" format="reference" />
<attr name="tag_max_lines" format="integer" />
<attr name="expand_indicator_drawable" format="reference" />
<attr name="collapse_indicator_drawable" format="reference" />
<attr name="each_line_max_num" format="integer" />
<attr name="tag_expand_drawable" format="reference" />
<attr name="tag_shrink_drawable" format="reference" />
<attr name="tag_each_line_max_num" format="integer" />
</declare-styleable>

View File

@ -182,7 +182,9 @@
<!-- new added -->
<color name="tag_bg_gray">#ffbac5d2</color>
<color name="tag_bg_color">#ffbac5d2</color>
<color name="tag_text_color">#ffe5f1ff</color>
</resources>