From 86d000bc197c163e2217ce02bf0623be2269cada Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Sat, 16 Aug 2025 00:19:30 +0800 Subject: [PATCH] refactor: decoupling mListeners in PanguTextWatcher to TextViewDelegate --- .../android/core/PanguTextWatcher.kt | 28 ++----- .../android/core/TextViewDelegate.kt | 75 +++++++++++++++++++ 2 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/core/TextViewDelegate.kt 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 59f1856..9d1dfce 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 @@ -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>() + 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() - 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) {} } \ No newline at end of file diff --git a/pangutext-android/src/main/java/com/highcapable/pangutext/android/core/TextViewDelegate.kt b/pangutext-android/src/main/java/com/highcapable/pangutext/android/core/TextViewDelegate.kt new file mode 100644 index 0000000..c994c70 --- /dev/null +++ b/pangutext-android/src/main/java/com/highcapable/pangutext/android/core/TextViewDelegate.kt @@ -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>() + + /** + * 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() + 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() + } +} \ No newline at end of file