From 2426f6b8bd79632b26ed8dffd80aee03e44b8bc5 Mon Sep 17 00:00:00 2001 From: renhaoting <370797079@qq.com> Date: Tue, 21 Oct 2025 14:57:36 +0800 Subject: [PATCH] =?UTF-8?q?tagFlow=20=E5=B1=95=E5=BC=80=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualnovel/configs/NovelApplication.kt | 5 + .../remax/visualnovel/extension/DimenExt.kt | 11 ++ .../ui/main/actor/ActorListFragment.kt | 4 +- .../widget/custom/TagFlowLayout2.kt | 146 ++++++++---------- .../main/res/drawable/tag_flow_item_bg.xml | 2 +- .../main/res/layout/fragment_main_actor.xml | 7 +- VisualNovel/app/src/main/res/values/attrs.xml | 6 +- .../app/src/main/res/values/colors.xml | 4 +- 8 files changed, 95 insertions(+), 90 deletions(-) create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/extension/DimenExt.kt diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/configs/NovelApplication.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/configs/NovelApplication.kt index 972f396..9c01f19 100644 --- a/VisualNovel/app/src/main/java/com/remax/visualnovel/configs/NovelApplication.kt +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/configs/NovelApplication.kt @@ -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(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() } diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/extension/DimenExt.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/extension/DimenExt.kt new file mode 100644 index 0000000..39f66d7 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/extension/DimenExt.kt @@ -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 \ No newline at end of file diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt index 1373154..3bff812 100644 --- a/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt @@ -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() { TagItem("8", "LastLine"), ) - tagFlowLayout.setTags(tags) + tagFlowLayout.setTagDataList(tags) tagFlowLayout.setOnTagClickListener { tag -> Toast.makeText(context, "Clicked: ${tag.text}", Toast.LENGTH_SHORT).show() diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/widget/custom/TagFlowLayout2.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/widget/custom/TagFlowLayout2.kt index 752f589..e38ed06 100644 --- a/VisualNovel/app/src/main/java/com/remax/visualnovel/widget/custom/TagFlowLayout2.kt +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/widget/custom/TagFlowLayout2.kt @@ -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() private val tagViews = mutableListOf() - 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) { + fun setTagDataList(tags: List) { 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 + } \ No newline at end of file diff --git a/VisualNovel/app/src/main/res/drawable/tag_flow_item_bg.xml b/VisualNovel/app/src/main/res/drawable/tag_flow_item_bg.xml index 3cb27ef..b04ce23 100644 --- a/VisualNovel/app/src/main/res/drawable/tag_flow_item_bg.xml +++ b/VisualNovel/app/src/main/res/drawable/tag_flow_item_bg.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/VisualNovel/app/src/main/res/layout/fragment_main_actor.xml b/VisualNovel/app/src/main/res/layout/fragment_main_actor.xml index 452f402..7fedecc 100644 --- a/VisualNovel/app/src/main/res/layout/fragment_main_actor.xml +++ b/VisualNovel/app/src/main/res/layout/fragment_main_actor.xml @@ -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" /> diff --git a/VisualNovel/app/src/main/res/values/attrs.xml b/VisualNovel/app/src/main/res/values/attrs.xml index 1b12daa..8437096 100644 --- a/VisualNovel/app/src/main/res/values/attrs.xml +++ b/VisualNovel/app/src/main/res/values/attrs.xml @@ -1441,9 +1441,9 @@ - - - + + + diff --git a/VisualNovel/app/src/main/res/values/colors.xml b/VisualNovel/app/src/main/res/values/colors.xml index fa7dd30..13cd74b 100644 --- a/VisualNovel/app/src/main/res/values/colors.xml +++ b/VisualNovel/app/src/main/res/values/colors.xml @@ -182,7 +182,9 @@ - #ffbac5d2 + #ffbac5d2 + #ffe5f1ff + \ No newline at end of file