From 7a233d82d8a4fb0ab627de8b8ce9afb8bda07ead Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Tue, 11 Feb 2025 15:55:00 +0800 Subject: [PATCH] feat: add isAutoRemeasureText in PanguTextConfig --- docs-source/src/en/library/android.md | 11 ++++++ docs-source/src/zh-cn/library/android.md | 9 +++++ .../pangutext/android/PanguTextConfig.kt | 14 ++++++++ .../android/core/PanguTextWatcher.kt | 34 +++++++++++++++++++ .../pangutext/android/factory/PanguWidget.kt | 4 ++- .../src/main/res/values/attrs.xml | 2 ++ 6 files changed, 73 insertions(+), 1 deletion(-) diff --git a/docs-source/src/en/library/android.md b/docs-source/src/en/library/android.md index 9b26b58..dc85671 100644 --- a/docs-source/src/en/library/android.md +++ b/docs-source/src/en/library/android.md @@ -272,6 +272,15 @@ config.isEnabled = true // Processing Spanned text is enabled by default, but this feature is experimental. // If issues occur, you can disable it. When disabled, Spanned text will return the original text. config.isProcessedSpanned = true +// Whether to automatically re-measure the text width after processing. +// Note: [PanguText] after injecting text and changing the text, +// the width of [TextView] will not be calculated automatically. +// At this time, this feature will call [TextView.setText] to re-execute the measurements, +// which can fix every time in some dynamic layouts (such as `RecyclerView`) changes in text width, +// but may cause performance issues, you can choose to disable this feature. +// To prevent unnecessary performance overhead, +// this feature only takes effect on [TextView] with `maxLines` set to 1 or `singleLine`. +config.isAutoRemeasureText = true // Set patterns to exclude during formatting using regular expressions. // For example, exclude all URLs. config.excludePatterns.add("https?://\\S+".toRegex()) @@ -319,6 +328,7 @@ If you integrated using the [Inject to LayoutInflater](#inject-to-layoutinflater - `panguText_enabled` corresponds to `PanguTextConfig.isEnabled` - `panguText_processedSpanned` corresponds to `PanguTextConfig.isProcessedSpanned` +- `panguText_autoRemeasureText` corresponds to `PanguTextConfig.isAutoRemeasureText` - `panguText_excludePatterns` corresponds to `PanguTextConfig.excludePatterns`, string array, multiple patterns separated by `|@|` - `panguText_cjkSpacingRatio` corresponds to `PanguTextConfig.cjkSpacingRatio` @@ -332,6 +342,7 @@ If you integrated using the [Inject to LayoutInflater](#inject-to-layoutinflater android:text="Xiaoming今年16岁" app:panguText_enabled="true" app:panguText_processedSpanned="true" + app:panguText_autoRemeasureText="true" app:panguText_excludePatterns="https?://\\S+;\\[.*?]|@|\\[.*?]" app:panguText_cjkSpacingRatio="7.0" /> ``` diff --git a/docs-source/src/zh-cn/library/android.md b/docs-source/src/zh-cn/library/android.md index 223ab5b..a6c4f01 100644 --- a/docs-source/src/zh-cn/library/android.md +++ b/docs-source/src/zh-cn/library/android.md @@ -271,6 +271,13 @@ config.isEnabled = true // Spanned 文本处理默认启用,但此功能尚处于实验性阶段, // 如果发生问题你可以选择禁用,禁用后遇到 Spanned 文本将返回原始文本 config.isProcessedSpanned = true +// 是否要在处理后自动重新测量文本宽度 +// 注意:[PanguText] 注入文本并更改文本后,[TextView] 的宽度将不会自动计算 +// 目前,此功能将调用 [TextView.setText] 重新执行测量结果, +// 该测量可以在某些动态布局 (例如 `RecyclerView`) 中每次修复文本宽度, +// 但可能会导致性能问题,你可以选择禁用此功能 +// 为了防止不必要的性能开销,此功能仅在 `maxlines` 设置为 1 或 `singleLine` 的 [TextView] 上生效 +config.isAutoRemeasureText = true // 设置在格式化过程中以正则形式定义需要排除的内容 // 例如排除全部 URL config.excludePatterns.add("https?://\\S+".toRegex()) @@ -317,6 +324,7 @@ textView.injectPanguText(config = config2) - `panguText_enabled` 对应 `PanguTextConfig.isEnabled` - `panguText_processedSpanned` 对应 `PanguTextConfig.isProcessedSpanned` +- `panguText_autoRemeasureText` 对应 `PanguTextConfig.isAutoRemeasureText` - `panguText_excludePatterns` 对应 `PanguTextConfig.excludePatterns`,字符串数组,多个使用 `|@|` 分隔 - `panguText_cjkSpacingRatio` 对应 `PanguTextConfig.cjkSpacingRatio` @@ -330,6 +338,7 @@ textView.injectPanguText(config = config2) android:text="Xiaoming今年16岁" app:panguText_enabled="true" app:panguText_processedSpanned="true" + app:panguText_autoRemeasureText="true" app:panguText_excludePatterns="https?://\\S+;\\[.*?]|@|\\[.*?]" app:panguText_cjkSpacingRatio="7.0" /> ``` diff --git a/pangutext-android/src/main/java/com/highcapable/pangutext/android/PanguTextConfig.kt b/pangutext-android/src/main/java/com/highcapable/pangutext/android/PanguTextConfig.kt index ecb4531..b38283d 100644 --- a/pangutext-android/src/main/java/com/highcapable/pangutext/android/PanguTextConfig.kt +++ b/pangutext-android/src/main/java/com/highcapable/pangutext/android/PanguTextConfig.kt @@ -22,6 +22,7 @@ package com.highcapable.pangutext.android import android.text.Spanned +import android.widget.TextView import java.io.Serializable /** @@ -53,6 +54,18 @@ class PanguTextConfig internal constructor() : Serializable { */ var isProcessedSpanned = true + /** + * Whether to automatically re-measure the text width after processing. + * + * - Note: [PanguText] after injecting text and changing the text, + * the width of [TextView] will not be calculated automatically. + * At this time, this feature will call [TextView.setText] to re-execute the measurements, + * which can fix every time in some dynamic layouts (such as `RecyclerView`) changes in text width, + * but may cause performance issues, you can choose to disable this feature. + * To prevent unnecessary performance overhead, this feature only takes effect on [TextView] with `maxLines` set to 1 or `singleLine`. + */ + var isAutoRemeasureText = true + /** * The regular expression for text content that needs to be excluded. * [PanguText] processing will be skipped after matching these texts. @@ -87,6 +100,7 @@ class PanguTextConfig internal constructor() : Serializable { fun copy(body: PanguTextConfig.() -> Unit = {}) = PanguTextConfig().also { it.isEnabled = this.isEnabled it.isProcessedSpanned = this.isProcessedSpanned + it.isAutoRemeasureText = this.isAutoRemeasureText it.excludePatterns.addAll(this.excludePatterns) it.cjkSpacingRatio = this.cjkSpacingRatio it.body() diff --git a/pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguTextWatcher.kt b/pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguTextWatcher.kt index 0387225..e61a3f3 100644 --- a/pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguTextWatcher.kt +++ b/pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguTextWatcher.kt @@ -23,10 +23,13 @@ package com.highcapable.pangutext.android.core import android.text.Editable import android.text.TextWatcher +import android.widget.EditText import android.widget.TextView +import com.highcapable.betterandroid.system.extension.tool.SystemVersion import com.highcapable.pangutext.android.PanguText import com.highcapable.pangutext.android.PanguTextConfig import com.highcapable.pangutext.android.extension.injectRealTimePanguText +import com.highcapable.yukireflection.factory.current /** * A [TextWatcher] that automatically applies [PanguText] to the text content. @@ -36,8 +39,39 @@ import com.highcapable.pangutext.android.extension.injectRealTimePanguText * @param config the configuration of [PanguText]. */ class PanguTextWatcher internal constructor(private val base: TextView, private val config: PanguTextConfig) : TextWatcher { + + /** + * The text watchers of the base [TextView]. + * @return [ArrayList]<[TextWatcher]>. + */ + private val textWatchers + get() = base.current(ignored = true).field { + name = "mListeners" + superClass() + }.cast>() + + /** + * Whether to automatically re-measure the text width after processing. + * @return [Boolean] + */ + private val isAutoRemeasureText + get() = config.isAutoRemeasureText && base !is EditText && (base.maxLines == 1 || + SystemVersion.require(SystemVersion.Q, base.maxLines == 1) { base.isSingleLine }) + override fun afterTextChanged(editable: Editable?) { editable?.let { PanguText.format(base.resources, base.textSize, it, config) } + if (!isAutoRemeasureText) return + val currentWatchers = mutableListOf() + textWatchers?.also { + currentWatchers.addAll(it) + // Avoid triggering events again during processing. + it.clear() + } + // Reset the text to trigger remeasurement. + base.text = editable + // Re-add to continue listening to text changes. + textWatchers?.addAll(currentWatchers) + currentWatchers.clear() } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} diff --git a/pangutext-android/src/main/java/com/highcapable/pangutext/android/factory/PanguWidget.kt b/pangutext-android/src/main/java/com/highcapable/pangutext/android/factory/PanguWidget.kt index b4f5e15..edac4e7 100644 --- a/pangutext-android/src/main/java/com/highcapable/pangutext/android/factory/PanguWidget.kt +++ b/pangutext-android/src/main/java/com/highcapable/pangutext/android/factory/PanguWidget.kt @@ -87,6 +87,7 @@ internal object PanguWidget { } else instance.obtainStyledAttributes(attrs, R.styleable.PanguTextHelper) { val isEnabled = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_enabled) val isProcessedSpanned = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_processedSpanned) + val isAutoRemeasureText = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_autoRemeasureText) val cjkSpacingRatio = it.getFloatOrNull(R.styleable.PanguTextHelper_panguText_cjkSpacingRatio) val excludePatterns = it.getStringOrNull(R.styleable.PanguTextHelper_panguText_excludePatterns) ?.split(TEXT_REGEX_SPLITE_SYMBOL)?.mapNotNull { regex -> @@ -95,9 +96,10 @@ internal object PanguWidget { }.getOrNull() }?.toTypedArray() ?: emptyArray() if (isEnabled == false) return instance - if (isProcessedSpanned != null || cjkSpacingRatio != null || excludePatterns.isNotEmpty()) { + if (isProcessedSpanned != null || isAutoRemeasureText != null || cjkSpacingRatio != null || excludePatterns.isNotEmpty()) { val configCopy = config.copy() configCopy.isProcessedSpanned = isProcessedSpanned ?: config.isProcessedSpanned + configCopy.isAutoRemeasureText = isAutoRemeasureText ?: config.isAutoRemeasureText configCopy.cjkSpacingRatio = cjkSpacingRatio ?: config.cjkSpacingRatio if (excludePatterns.isNotEmpty()) { config.excludePatterns.clear() diff --git a/pangutext-android/src/main/res/values/attrs.xml b/pangutext-android/src/main/res/values/attrs.xml index f08ee52..ce980fa 100644 --- a/pangutext-android/src/main/res/values/attrs.xml +++ b/pangutext-android/src/main/res/values/attrs.xml @@ -5,6 +5,8 @@ + +