refactor: decoupling mListeners in PanguTextWatcher to TextViewDelegate

This commit is contained in:
2025-08-16 00:19:30 +08:00
parent 50b74c1cb7
commit 86d000bc19
2 changed files with 81 additions and 22 deletions

View File

@@ -26,9 +26,9 @@ import android.text.TextWatcher
import android.widget.EditText
import android.widget.TextView
import com.highcapable.betterandroid.system.extension.tool.AndroidVersion
import com.highcapable.kavaref.KavaRef.Companion.asResolver
import com.highcapable.pangutext.android.PanguText
import com.highcapable.pangutext.android.PanguTextConfig
import com.highcapable.pangutext.android.core.TextViewDelegate.Companion.delegate
import com.highcapable.pangutext.android.extension.injectRealTimePanguText
/**
@@ -40,15 +40,7 @@ import com.highcapable.pangutext.android.extension.injectRealTimePanguText
*/
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.asResolver().optional(silent = true).firstFieldOrNull {
name = "mListeners"
superclass()
}?.getQuietly<ArrayList<TextWatcher>>()
private val delegate = base.delegate
/**
* Whether to automatically re-measure the text width after processing.
@@ -62,20 +54,12 @@ class PanguTextWatcher internal constructor(private val base: TextView, private
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()
delegate.withoutTextWatchers {
// Reset the text to trigger remeasurement.
base.text = editable
}
// 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

@@ -0,0 +1,75 @@
/*
* PanguText - A typographic solution for the optimal alignment of CJK characters, English words, and half-width digits.
* Copyright (C) 2019 HighCapable
* https://github.com/BetterAndroid/PanguText
*
* Apache License Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is created by fankes on 2025/8/15.
*/
package com.highcapable.pangutext.android.core
import android.text.TextWatcher
import android.widget.TextView
import com.highcapable.kavaref.KavaRef.Companion.resolve
/**
* A delegate for [TextView] to manage its text watchers.
* @param instance the [TextView] instance.
*/
internal class TextViewDelegate private constructor(private val instance: TextView) {
companion object {
private val mListeners by lazy {
TextView::class.resolve()
.optional(silent = true)
.firstFieldOrNull { name = "mListeners" }
}
/**
* Create the [TextViewDelegate] for the given [TextView] instance.
* @return [TextViewDelegate]
*/
val TextView.delegate get() = TextViewDelegate(this)
}
/**
* The text watchers of the [TextView].
* @return [ArrayList]<[TextWatcher]>.
*/
private val textWatchers
get() = mListeners?.copy()?.of(instance)?.getQuietly<ArrayList<TextWatcher>>()
/**
* Execute the given action without triggering text watchers.
* @param action the action to execute without triggering text watchers.
*/
inline fun withoutTextWatchers(action: () -> Unit) {
val currentWatchers = mutableListOf<TextWatcher>()
textWatchers?.also {
currentWatchers.addAll(it)
// Avoid triggering events again during processing.
it.clear()
}
// Run action.
action()
// Re-add to continue listening to text changes.
textWatchers?.addAll(currentWatchers)
currentWatchers.clear()
}
}