feat: add isAutoRemeasureText in PanguTextConfig

This commit is contained in:
2025-02-11 15:55:00 +08:00
parent 9148d29f37
commit 7a233d82d8
6 changed files with 73 additions and 1 deletions

View File

@@ -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()

View File

@@ -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<ArrayList<TextWatcher>>()
/**
* 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<TextWatcher>()
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) {}

View File

@@ -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()

View File

@@ -5,6 +5,8 @@
<attr name="panguText_enabled" format="boolean" />
<!-- Processed [Spanned] text (experimental). -->
<attr name="panguText_processedSpanned" format="boolean" />
<!-- Whether to automatically re-measure the text width after processing. -->
<attr name="panguText_autoRemeasureText" format="boolean" />
<!-- The regular expression for text content that needs to be excluded. -->
<attr name="panguText_excludePatterns" format="string" />
<!-- The CJK spacing ratio. -->