Skip to content

Commit 2f5c44b

Browse files
committed
refactor: merge to BetterAndroid new usage
1 parent 34c1d66 commit 2f5c44b

File tree

13 files changed

+106
-22
lines changed

13 files changed

+106
-22
lines changed

demo-android/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ android {
4242
dependencies {
4343
implementation(projects.pangutextAndroid)
4444
implementation(com.highcapable.betterandroid.ui.component)
45+
implementation(com.highcapable.betterandroid.ui.component.adapter)
4546
implementation(com.highcapable.betterandroid.ui.extension)
4647
implementation(com.highcapable.betterandroid.system.extension)
4748
implementation(androidx.core.core.ktx)

demo-android/src/main/java/com/highcapable/pangutext/demo/ui/ListActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class ListActivity : BaseActivity<ActivityListBinding>() {
3535

3636
override fun onCreate(savedInstanceState: Bundle?) {
3737
super.onCreate(savedInstanceState)
38+
3839
binding.recyclerView.bindAdapter<String> {
3940
onBindData { listData }
4041
onBindItemView<AdapterListBinding> { binding, text, _ ->

demo-android/src/main/java/com/highcapable/pangutext/demo/ui/MainActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
4646

4747
override fun onCreate(savedInstanceState: Bundle?) {
4848
super.onCreate(savedInstanceState)
49+
4950
binding.root.handleOnWindowInsetsChanged(animated = true) { linearLayout, insetsWrapper ->
5051
linearLayout.setInsetsPadding(insetsWrapper.safeDrawing)
5152
}
53+
5254
listOf(
5355
binding.textViewPanguText,
5456
binding.textViewPanguTextCjkSpacingRatio,

demo-android/src/main/java/com/highcapable/pangutext/demo/ui/base/BaseActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ open class BaseActivity<VB : ViewBinding> : AppBindingActivity<VB>() {
3232
override fun onPrepareContentView(savedInstanceState: Bundle?): LayoutInflater {
3333
val inflater = super.onPrepareContentView(savedInstanceState)
3434
PanguTextFactory2.inject(inflater)
35+
3536
return inflater
3637
}
3738
}

gradle/sweet-dependency/sweet-dependency-config.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ plugins:
3131
libraries:
3232
com.highcapable.betterandroid:
3333
ui-component:
34-
version: 1.0.7
34+
version: 1.0.8
35+
ui-component-adapter:
36+
version: 1.0.0
3537
ui-extension:
36-
version: 1.0.6
38+
version: 1.0.7
3739
system-extension:
38-
version: 1.0.2
40+
version: 1.0.3
3941
com.highcapable.kavaref:
4042
kavaref-core:
4143
version: 1.0.1

pangutext-android/src/main/java/com/highcapable/pangutext/android/PanguText.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ object PanguText {
8888
fun format(resources: Resources, @Px textSize: Float, text: CharSequence, config: PanguTextConfig = globalConfig): CharSequence {
8989
if (!config.isEnabled) return text
9090
if (text.isBlank()) return text
91+
9192
val formatted = format(text, PH, config)
9293
return text.applySpans(formatted, resources, textSize, config)
9394
}
@@ -110,9 +111,11 @@ object PanguText {
110111
@JvmStatic
111112
fun format(text: CharSequence, whiteSpace: Char = ' ', config: PanguTextConfig = globalConfig): CharSequence {
112113
if (!config.isEnabled) return text
114+
113115
// In any case, always perform a cleanup operation before accepting text.
114116
val processed = text.clearSpans()
115117
val patterns = config.excludePatterns.toTypedArray()
118+
116119
return if ((config.isProcessedSpanned || text !is Spanned) && text.isNotBlank() && text.length > 1)
117120
PanguPatterns.matchAndReplace(processed, whiteSpace, *patterns)
118121
else processed
@@ -136,27 +139,34 @@ object PanguText {
136139
whiteSpace: Char = PH
137140
): CharSequence {
138141
val builder = SpannableStringBuilder(formatted)
142+
139143
formatted.forEachIndexed { index, c ->
140144
// Add spacing to the previous character.
141145
if (c == whiteSpace && index in 0..formatted.lastIndex) {
142146
val span = PanguMarginSpan.Placeholder()
143147
builder.setSpan(span, index - 1, index, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
144148
}
145149
}
150+
146151
// Delete the placeholder character.
147152
for (i in (builder.length - 1) downTo 0) {
148153
if (builder[i] == whiteSpace) builder.delete(i, i + 1)
149154
}
155+
150156
// Find the [PanguMarginSpan.Placeholder] subscript in [builder] and use [PanguMarginSpan] to set it to [original].
151157
val builderSpans = builder.getSpans(0, builder.length, classOf<PanguMarginSpan.Placeholder>())
152158
val spannable = if (this !is Spannable) SpannableString(this) else this
159+
153160
// Add new [PanguMarginSpan].
154161
builderSpans.forEach {
155162
val start = builder.getSpanStart(it)
156163
val end = builder.getSpanEnd(it)
164+
157165
val span = PanguMarginSpan.create(resources, textSize, config.cjkSpacingRatio)
158166
spannable.setSpan(span, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
159-
}; builder.clear()
167+
}
168+
169+
builder.clear()
160170
return spannable
161171
}
162172

@@ -170,12 +180,16 @@ object PanguText {
170180
*/
171181
private fun CharSequence.clearSpans(): CharSequence {
172182
if (this !is Spannable || isBlank() || !hasSpan<PanguMarginSpan>()) return this
183+
173184
getSpans(0, length, classOf<PanguMarginSpan>()).forEach { span ->
174185
val start = getSpanStart(span)
175-
val end = getSpanEnd(span)
186+
val end = getSpanEnd(span)
187+
176188
// Clear the [PanguMarginSpan].
177189
if (start < length && end > 0) removeSpan(span)
178-
}; return this
190+
}
191+
192+
return this
179193
}
180194

181195
/**
@@ -186,6 +200,7 @@ object PanguText {
186200
private inline fun <reified T : CharacterStyle> CharSequence.hasSpan(): Boolean {
187201
val spannable = this as? Spanned ?: return false
188202
val spans = spannable.getSpans(0, spannable.length, classOf<T>())
203+
189204
return spans.isNotEmpty()
190205
}
191206
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,24 @@ internal class PanguMarginSpan(@Px val margin: Int) : ReplacementSpan() {
7676
// Get background color.
7777
val color = span.backgroundColor
7878
val originalColor = paint.color
79+
7980
// Save the current [paint] color.
8081
paint.color = color
82+
8183
// Get the width of the text.
8284
val textWidth = paint.measureText(text, start, end)
85+
8386
// Draw background rectangle.
8487
canvas.drawRect(x, top.toFloat(), x + textWidth + margin, bottom.toFloat(), paint)
88+
8589
// Restore original color.
8690
paint.color = originalColor
8791
}
8892
span is CharacterStyle && paint is TextPaint -> span.updateDrawState(paint)
8993
}
90-
}; text?.let { canvas.drawText(it, start, end, x, y.toFloat(), paint) }
94+
}
95+
96+
text?.let { canvas.drawText(it, start, end, x, y.toFloat(), paint) }
9197
}
9298

9399
/**

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import android.text.Editable
2525
import android.text.TextWatcher
2626
import android.widget.EditText
2727
import android.widget.TextView
28-
import com.highcapable.betterandroid.system.extension.tool.SystemVersion
28+
import com.highcapable.betterandroid.system.extension.tool.AndroidVersion
2929
import com.highcapable.kavaref.KavaRef.Companion.asResolver
3030
import com.highcapable.pangutext.android.PanguText
3131
import com.highcapable.pangutext.android.PanguTextConfig
@@ -56,21 +56,24 @@ class PanguTextWatcher internal constructor(private val base: TextView, private
5656
*/
5757
private val isAutoRemeasureText
5858
get() = config.isAutoRemeasureText && base !is EditText && (base.maxLines == 1 ||
59-
SystemVersion.require(SystemVersion.Q, base.maxLines == 1) { base.isSingleLine })
59+
AndroidVersion.require(AndroidVersion.Q, base.maxLines == 1) { base.isSingleLine })
6060

6161
override fun afterTextChanged(editable: Editable?) {
6262
editable?.let { PanguText.format(base.resources, base.textSize, it, config) }
6363
if (!isAutoRemeasureText) return
64+
6465
val currentWatchers = mutableListOf<TextWatcher>()
6566
textWatchers?.also {
6667
currentWatchers.addAll(it)
6768
// Avoid triggering events again during processing.
6869
it.clear()
6970
}
71+
7072
// Reset the text to trigger remeasurement.
7173
base.text = editable
7274
// Re-add to continue listening to text changes.
7375
textWatchers?.addAll(currentWatchers)
76+
7477
currentWatchers.clear()
7578
}
7679
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

pangutext-android/src/main/java/com/highcapable/pangutext/android/extension/PanguText.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ fun PanguTextConfig(copyFromGlobal: Boolean = true, body: PanguTextConfig.() ->
5757
@JvmOverloads
5858
fun TextView.injectPanguText(injectHint: Boolean = true, config: PanguTextConfig = PanguText.globalConfig) {
5959
if (!config.isEnabled) return
60+
6061
setTextWithPangu(this.text, config)
6162
if (injectHint) setHintWithPangu(this.hint, config)
6263
}
@@ -75,11 +76,15 @@ fun TextView.injectPanguText(injectHint: Boolean = true, config: PanguTextConfig
7576
@JvmOverloads
7677
fun TextView.injectRealTimePanguText(injectHint: Boolean = true, config: PanguTextConfig = PanguText.globalConfig) {
7778
if (!config.isEnabled) return
79+
7880
val observerKey = R.id.tag_inject_real_time_pangu_text
7981
val isRepeated = getTag<Boolean>(observerKey) == true
82+
8083
// It will no longer be executed if it exceeds one time.
8184
if (isRepeated) return
85+
8286
injectPanguText(injectHint, config)
87+
8388
var currentHint = this.hint
8489
val textWatcher = PanguTextWatcher(base = this, config)
8590
val listener = ViewTreeObserver.OnGlobalLayoutListener {
@@ -88,13 +93,17 @@ fun TextView.injectRealTimePanguText(injectHint: Boolean = true, config: PanguTe
8893
self.setHintWithPangu(self.hint, config)
8994
currentHint = self.hint
9095
}
96+
9197
setTag(observerKey, true)
9298
doOnAttach {
9399
addTextChangedListener(textWatcher)
100+
94101
// Add a global layout listener to monitor the hint text changes.
95102
if (injectHint) viewTreeObserver?.addOnGlobalLayoutListener(listener)
103+
96104
doOnDetach {
97105
removeTextChangedListener(textWatcher)
106+
98107
// Remove the global layout listener when the view is detached.
99108
if (injectHint) viewTreeObserver?.removeOnGlobalLayoutListener(listener)
100109
setTag(observerKey, false)
@@ -112,7 +121,10 @@ fun TextView.injectRealTimePanguText(injectHint: Boolean = true, config: PanguTe
112121
@JvmOverloads
113122
fun TextView.setTextWithPangu(text: CharSequence?, config: PanguTextConfig = PanguText.globalConfig) {
114123
if (!config.isEnabled) return
115-
this.text = text?.let { PanguText.format(resources, textSize, it, config) }
124+
125+
this.text = text?.let {
126+
PanguText.format(resources, textSize, it, config)
127+
}
116128
}
117129

118130
/**
@@ -125,5 +137,8 @@ fun TextView.setTextWithPangu(text: CharSequence?, config: PanguTextConfig = Pan
125137
@JvmOverloads
126138
fun TextView.setHintWithPangu(text: CharSequence?, config: PanguTextConfig = PanguText.globalConfig) {
127139
if (!config.isEnabled) return
128-
this.hint = text?.let { PanguText.format(resources, textSize, it, config) }
140+
141+
this.hint = text?.let {
142+
PanguText.format(resources, textSize, it, config)
143+
}
129144
}

pangutext-android/src/main/java/com/highcapable/pangutext/android/extension/Replacement.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,36 @@ import java.util.regex.Matcher
4141
internal fun CharSequence.replaceAndPreserveSpans(regex: Regex, replacement: String, vararg excludePatterns: Regex) =
4242
runCatching {
4343
val builder = SpannableStringBuilder(this)
44+
4445
val matcher = regex.toPattern().matcher(this)
4546
val excludeMatchers = excludePatterns.map { it.toPattern().matcher(this) }
4647
val excludeIndexs = mutableSetOf<Pair<Int, Int>>()
48+
4749
excludeMatchers.forEach {
4850
while (it.find()) excludeIndexs.add(it.start() to it.end())
4951
}
52+
5053
var offset = 0
54+
5155
// Offset adjustment to account for changes in the text length after replacements.
5256
while (matcher.find()) {
5357
val start = matcher.start() + offset
5458
val end = matcher.end() + offset
59+
5560
// Skip the replacement if the matched range is excluded.
5661
// The character range offset is adjusted by 1 to avoid the exclusion of the matched range.
5762
if (excludeIndexs.any { it.first <= start + 1 && it.second >= end - 1 }) continue
63+
5864
// Perform the replacement.
5965
val replacementText = matcher.buildReplacementText(replacement)
66+
6067
builder.replace(start, end, replacementText)
68+
6169
// Adjust offset based on the length of the replacement.
6270
offset += replacementText.length - (end - start)
63-
}; builder
71+
}
72+
73+
builder
6474
}.onFailure {
6575
Log.w(PangutextAndroidProperties.PROJECT_NAME, "Failed to replace span text content.", it)
6676
}.getOrNull() ?: this
@@ -74,23 +84,29 @@ internal fun CharSequence.replaceAndPreserveSpans(regex: Regex, replacement: Str
7484
private fun Matcher.buildReplacementText(replacement: String): String {
7585
val matcher = this
7686
var result = replacement
87+
7788
// Check for group references (like $1, $2, ...).
7889
val pattern = "\\$(\\d+)".toRegex()
7990
result = pattern.replace(result) { matchResult ->
8091
val groupIndex = matchResult.groupValues[1].toInt()
92+
8193
if (groupIndex <= matcher.groupCount())
8294
matcher.group(groupIndex) ?: ""
8395
else ""
8496
}
97+
8598
// Check for named groups (like ${groupName}).
8699
val namedGroupPattern = "\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}".toRegex()
87100
result = namedGroupPattern.replace(result) { matchResult ->
88101
val groupName = matchResult.groupValues[1]
89102
val groupIndex = matcher.getNamedGroupIndex(groupName)
103+
90104
if (groupIndex >= 0)
91105
matcher.group(groupIndex) ?: ""
92106
else ""
93-
}; return result
107+
}
108+
109+
return result
94110
}
95111

96112
/**
@@ -105,5 +121,6 @@ private fun Matcher.getNamedGroupIndex(groupName: String): Int {
105121
.firstFieldOrNull {
106122
name = "namedGroups"
107123
}?.of(this)?.getQuietly<Map<String, Int>>()
124+
108125
return namedGroups?.get(groupName) ?: -1
109126
}

0 commit comments

Comments
 (0)