Skip to content

Commit 86d000b

Browse files
committed
refactor: decoupling mListeners in PanguTextWatcher to TextViewDelegate
1 parent 50b74c1 commit 86d000b

File tree

2 files changed

+81
-22
lines changed

2 files changed

+81
-22
lines changed

pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguTextWatcher.kt

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ import android.text.TextWatcher
2626
import android.widget.EditText
2727
import android.widget.TextView
2828
import com.highcapable.betterandroid.system.extension.tool.AndroidVersion
29-
import com.highcapable.kavaref.KavaRef.Companion.asResolver
3029
import com.highcapable.pangutext.android.PanguText
3130
import com.highcapable.pangutext.android.PanguTextConfig
31+
import com.highcapable.pangutext.android.core.TextViewDelegate.Companion.delegate
3232
import com.highcapable.pangutext.android.extension.injectRealTimePanguText
3333

3434
/**
@@ -40,15 +40,7 @@ import com.highcapable.pangutext.android.extension.injectRealTimePanguText
4040
*/
4141
class PanguTextWatcher internal constructor(private val base: TextView, private val config: PanguTextConfig) : TextWatcher {
4242

43-
/**
44-
* The text watchers of the base [TextView].
45-
* @return [ArrayList]<[TextWatcher]>.
46-
*/
47-
private val textWatchers
48-
get() = base.asResolver().optional(silent = true).firstFieldOrNull {
49-
name = "mListeners"
50-
superclass()
51-
}?.getQuietly<ArrayList<TextWatcher>>()
43+
private val delegate = base.delegate
5244

5345
/**
5446
* Whether to automatically re-measure the text width after processing.
@@ -62,20 +54,12 @@ class PanguTextWatcher internal constructor(private val base: TextView, private
6254
editable?.let { PanguText.format(base.resources, base.textSize, it, config) }
6355
if (!isAutoRemeasureText) return
6456

65-
val currentWatchers = mutableListOf<TextWatcher>()
66-
textWatchers?.also {
67-
currentWatchers.addAll(it)
68-
// Avoid triggering events again during processing.
69-
it.clear()
57+
delegate.withoutTextWatchers {
58+
// Reset the text to trigger remeasurement.
59+
base.text = editable
7060
}
71-
72-
// Reset the text to trigger remeasurement.
73-
base.text = editable
74-
// Re-add to continue listening to text changes.
75-
textWatchers?.addAll(currentWatchers)
76-
77-
currentWatchers.clear()
7861
}
62+
7963
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
8064
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
8165
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* PanguText - A typographic solution for the optimal alignment of CJK characters, English words, and half-width digits.
3+
* Copyright (C) 2019 HighCapable
4+
* https://github.com/BetterAndroid/PanguText
5+
*
6+
* Apache License Version 2.0
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* https://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*
20+
* This file is created by fankes on 2025/8/15.
21+
*/
22+
package com.highcapable.pangutext.android.core
23+
24+
import android.text.TextWatcher
25+
import android.widget.TextView
26+
import com.highcapable.kavaref.KavaRef.Companion.resolve
27+
28+
/**
29+
* A delegate for [TextView] to manage its text watchers.
30+
* @param instance the [TextView] instance.
31+
*/
32+
internal class TextViewDelegate private constructor(private val instance: TextView) {
33+
34+
companion object {
35+
36+
private val mListeners by lazy {
37+
TextView::class.resolve()
38+
.optional(silent = true)
39+
.firstFieldOrNull { name = "mListeners" }
40+
}
41+
42+
/**
43+
* Create the [TextViewDelegate] for the given [TextView] instance.
44+
* @return [TextViewDelegate]
45+
*/
46+
val TextView.delegate get() = TextViewDelegate(this)
47+
}
48+
49+
/**
50+
* The text watchers of the [TextView].
51+
* @return [ArrayList]<[TextWatcher]>.
52+
*/
53+
private val textWatchers
54+
get() = mListeners?.copy()?.of(instance)?.getQuietly<ArrayList<TextWatcher>>()
55+
56+
/**
57+
* Execute the given action without triggering text watchers.
58+
* @param action the action to execute without triggering text watchers.
59+
*/
60+
inline fun withoutTextWatchers(action: () -> Unit) {
61+
val currentWatchers = mutableListOf<TextWatcher>()
62+
textWatchers?.also {
63+
currentWatchers.addAll(it)
64+
// Avoid triggering events again during processing.
65+
it.clear()
66+
}
67+
68+
// Run action.
69+
action()
70+
// Re-add to continue listening to text changes.
71+
textWatchers?.addAll(currentWatchers)
72+
73+
currentWatchers.clear()
74+
}
75+
}

0 commit comments

Comments
 (0)