mirror of
https://github.com/BetterAndroid/PanguText.git
synced 2025-09-04 01:35:37 +08:00
feat: add PanguTextPatcher
This commit is contained in:
@@ -150,6 +150,39 @@ However, please inject before the `LayoutInflater` instance is used to load the
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
#### Using the Patching Tool
|
||||||
|
|
||||||
|
You can use `PanguTextPatcher` to patch existing `View` or `ViewGroup` instances.
|
||||||
|
|
||||||
|
Patch the entire root layout, and `PanguTextPatcher` will automatically patch all `TextView` or its subclasses under the root layout.
|
||||||
|
|
||||||
|
> The following example
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Assume you have a root layout.
|
||||||
|
val root: ViewGroup
|
||||||
|
// Patch the root layout.
|
||||||
|
PanguTextPatcher.patch(root)
|
||||||
|
```
|
||||||
|
|
||||||
|
Patch a single `View`, which is of type `TextView` or a subclass of `TextView`.
|
||||||
|
|
||||||
|
> The following example
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Assume this is your TextView.
|
||||||
|
val textView: TextView
|
||||||
|
// Patch a single View.
|
||||||
|
PanguTextPatcher.patch(textView)
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
|
||||||
|
When using `PanguTextPatcher` in recycled layouts such as `RecyclerView`, `ListView`, or `ViewPager`, you need to patch the `itemView` in `onCreateViewHolder` or `onBindViewHolder`,
|
||||||
|
otherwise it will not take effect.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
#### Manual Injection or Text Formatting
|
#### Manual Injection or Text Formatting
|
||||||
|
|
||||||
`PanguText` also supports manual injection, allowing you to inject it into the desired `TextView` or `EditText`.
|
`PanguText` also supports manual injection, allowing you to inject it into the desired `TextView` or `EditText`.
|
||||||
@@ -356,6 +389,7 @@ Don't forget to add the declaration `xmlns:app="http://schemas.android.com/apk/r
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
In custom `View`, you can extend your `View` to implement the `PanguTextView` interface to achieve the same functionality.
|
In custom `View`, you can extend your `View` to implement the `PanguTextView` interface to achieve the same functionality.
|
||||||
|
This feature is also effective for the [Using the Patching Tool](#using-the-patching-tool) method.
|
||||||
|
|
||||||
> The following example
|
> The following example
|
||||||
|
|
||||||
|
@@ -152,6 +152,38 @@ class MainActivity : Activity() {
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
#### 使用修补工具
|
||||||
|
|
||||||
|
你可以使用 `PanguTextPatcher` 修补现有的 `View` 或 `ViewGroup` 实例。
|
||||||
|
|
||||||
|
修补整个根布局,`PanguTextPatcher` 会自动修补根布局下的所有 `TextView` 或继承于其的组件。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 假设你有一个根布局
|
||||||
|
val root: ViewGroup
|
||||||
|
// 修补根布局
|
||||||
|
PanguTextPatcher.patch(root)
|
||||||
|
```
|
||||||
|
|
||||||
|
修补单个 `View`,类型为 `TextView` 或继承于 `TextView` 的组件。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 假设这就是你的 TextView
|
||||||
|
val textView: TextView
|
||||||
|
// 修补单个 View
|
||||||
|
PanguTextPatcher.patch(textView)
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
|
||||||
|
在 `RecyclerView`、`ListView`、`ViewPager` 等回收式布局中使用 `PanguTextPatcher` 时,你需要在 `onCreateViewHolder` 或 `onBindViewHolder` 中获取到 `itemView` 后进行修补,否则不会生效。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
#### 手动注入或格式化文本
|
#### 手动注入或格式化文本
|
||||||
|
|
||||||
`PanguText` 同样支持手动注入,你可以在需要的 `TextView` 或 `EditText` 上手动进行注入。
|
`PanguText` 同样支持手动注入,你可以在需要的 `TextView` 或 `EditText` 上手动进行注入。
|
||||||
@@ -351,7 +383,7 @@ textView.injectPanguText(config = config2)
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
在自定义 `View` 中,你可以将你的 `View` 继承于 `PanguTextView` 接口以同样实现上述功能。
|
在自定义 `View` 中,你可以将你的 `View` 继承于 `PanguTextView` 接口以同样实现上述功能,此功能对 [使用修补工具](#使用修补工具) 方案同样有效。
|
||||||
|
|
||||||
> 示例如下
|
> 示例如下
|
||||||
|
|
||||||
|
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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/3/4.
|
||||||
|
*/
|
||||||
|
package com.highcapable.pangutext.android.factory
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.highcapable.betterandroid.ui.extension.view.walkThroughChildren
|
||||||
|
import com.highcapable.pangutext.android.PanguText
|
||||||
|
import com.highcapable.pangutext.android.PanguTextConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patcher for [PanguText].
|
||||||
|
*/
|
||||||
|
object PanguTextPatcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch [PanguText] to the view.
|
||||||
|
* @param view the view or view group.
|
||||||
|
* @param config the configuration of [PanguText].
|
||||||
|
*/
|
||||||
|
@JvmOverloads
|
||||||
|
@JvmStatic
|
||||||
|
fun patch(view: View, config: PanguTextConfig = PanguText.globalConfig) {
|
||||||
|
when (view) {
|
||||||
|
is TextView -> PanguWidget.startInjection(view, config = config)
|
||||||
|
is ViewGroup ->
|
||||||
|
view.walkThroughChildren()
|
||||||
|
.filterIsInstance<TextView>()
|
||||||
|
.forEach { PanguWidget.startInjection(it, config = config) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -85,44 +85,59 @@ internal object PanguWidget {
|
|||||||
}
|
}
|
||||||
// Ignore if the instance is not a [TextView].
|
// Ignore if the instance is not a [TextView].
|
||||||
if (instance !is TextView) return null
|
if (instance !is TextView) return null
|
||||||
var config = PanguText.globalConfig
|
return startInjection(instance, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the injection of [PanguText] to the given [TextView].
|
||||||
|
* @param instance the instance of [TextView].
|
||||||
|
* @param attrs the attributes.
|
||||||
|
* @param config the configuration of [PanguText].
|
||||||
|
* @return [TV]
|
||||||
|
*/
|
||||||
|
inline fun <reified TV : TextView> startInjection(
|
||||||
|
instance: TV,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
config: PanguTextConfig = PanguText.globalConfig
|
||||||
|
): TV {
|
||||||
|
var sConfig = config
|
||||||
if (instance is PanguTextView) {
|
if (instance is PanguTextView) {
|
||||||
val configCopy = config.copy()
|
val configCopy = sConfig.copy()
|
||||||
instance.configurePanguText(configCopy)
|
instance.configurePanguText(configCopy)
|
||||||
config = configCopy
|
sConfig = configCopy
|
||||||
if (!config.isEnabled) return instance
|
if (!sConfig.isEnabled) return instance
|
||||||
} else instance.obtainStyledAttributes(attrs, R.styleable.PanguTextHelper) {
|
} else instance.obtainStyledAttributes(attrs, R.styleable.PanguTextHelper) {
|
||||||
val isEnabled = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_enabled)
|
val isEnabled = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_enabled)
|
||||||
val isProcessedSpanned = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_processedSpanned)
|
val isProcessedSpanned = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_processedSpanned)
|
||||||
val isAutoRemeasureText = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_autoRemeasureText)
|
val isAutoRemeasureText = it.getBooleanOrNull(R.styleable.PanguTextHelper_panguText_autoRemeasureText)
|
||||||
val cjkSpacingRatio = it.getFloatOrNull(R.styleable.PanguTextHelper_panguText_cjkSpacingRatio)
|
val cjkSpacingRatio = it.getFloatOrNull(R.styleable.PanguTextHelper_panguText_cjkSpacingRatio)
|
||||||
val excludePatterns = it.getStringOrNull(R.styleable.PanguTextHelper_panguText_excludePatterns)
|
val excludePatterns = it.getStringOrNull(R.styleable.PanguTextHelper_panguText_excludePatterns)
|
||||||
?.split(TEXT_REGEX_SPLITE_SYMBOL)?.mapNotNull { regex ->
|
?.split(TEXT_REGEX_SPLITE_SYMBOL)?.mapNotNull { regex ->
|
||||||
runCatching { regex.toRegex() }.onFailure { th ->
|
runCatching { regex.toRegex() }.onFailure { th ->
|
||||||
Log.e(PangutextAndroidProperties.PROJECT_NAME, "Invalid exclude pattern of $instance: $regex", th)
|
Log.e(PangutextAndroidProperties.PROJECT_NAME, "Invalid exclude pattern of $instance: $regex", th)
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}?.toTypedArray() ?: emptyArray()
|
}?.toTypedArray() ?: emptyArray()
|
||||||
if (isEnabled == false) return instance
|
if (isEnabled == false) return instance
|
||||||
if (isProcessedSpanned != null || isAutoRemeasureText != null || cjkSpacingRatio != null || excludePatterns.isNotEmpty()) {
|
if (isProcessedSpanned != null || isAutoRemeasureText != null || cjkSpacingRatio != null || excludePatterns.isNotEmpty()) {
|
||||||
val configCopy = config.copy()
|
val configCopy = sConfig.copy()
|
||||||
configCopy.isProcessedSpanned = isProcessedSpanned ?: config.isProcessedSpanned
|
configCopy.isProcessedSpanned = isProcessedSpanned ?: sConfig.isProcessedSpanned
|
||||||
configCopy.isAutoRemeasureText = isAutoRemeasureText ?: config.isAutoRemeasureText
|
configCopy.isAutoRemeasureText = isAutoRemeasureText ?: sConfig.isAutoRemeasureText
|
||||||
configCopy.cjkSpacingRatio = cjkSpacingRatio ?: config.cjkSpacingRatio
|
configCopy.cjkSpacingRatio = cjkSpacingRatio ?: sConfig.cjkSpacingRatio
|
||||||
if (excludePatterns.isNotEmpty()) {
|
if (excludePatterns.isNotEmpty()) {
|
||||||
config.excludePatterns.clear()
|
sConfig.excludePatterns.clear()
|
||||||
config.excludePatterns.addAll(excludePatterns)
|
sConfig.excludePatterns.addAll(excludePatterns)
|
||||||
}; config = configCopy
|
}; sConfig = configCopy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (instance.javaClass.name) {
|
when (instance.javaClass.name) {
|
||||||
// Specialize those components because loading "hint" style after [doOnAttachRepeatable] causes problems.
|
// Specialize those components because loading "hint" style after [doOnAttachRepeatable] causes problems.
|
||||||
"com.google.android.material.textfield.TextInputEditText",
|
"com.google.android.material.textfield.TextInputEditText",
|
||||||
"com.google.android.material.textfield.MaterialAutoCompleteTextView" -> {
|
"com.google.android.material.textfield.MaterialAutoCompleteTextView" -> {
|
||||||
instance.injectPanguText(config = config)
|
instance.injectPanguText(config = sConfig)
|
||||||
instance.doOnAttachRepeatable(config) { it.injectRealTimePanguText(injectHint = false, config) }
|
instance.doOnAttachRepeatable(sConfig) { it.injectRealTimePanguText(injectHint = false, sConfig) }
|
||||||
}
|
}
|
||||||
else -> instance.doOnAttachRepeatable(config) {
|
else -> instance.doOnAttachRepeatable(sConfig) {
|
||||||
it.injectRealTimePanguText(config = config)
|
it.injectRealTimePanguText(config = sConfig)
|
||||||
}
|
}
|
||||||
}; return instance
|
}; return instance
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user