droid_wrap/android/text.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
/*
* Copyright (c) 2024. The RigelA open source project team and
* its contributors reserve all rights.
*
* 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
* http://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.
*/
use crate::{
java::lang::{CharSequence, CharSequenceImpl},
JObjNew, JObjRef, JProxy, JType,
};
use droid_wrap_derive::{java_class, java_implement, java_interface, java_method};
use std::sync::Arc;
/**
整数的位定义,定义可编辑对象中保存的文本的基本内容类型。支持的类可以与变体和标志组合以指示所需的行为。
示例
- 密码字段,密码对用户可见: inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
- 多行邮箱地址,自动大写: inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_POSTAL_ADDRESS | TYPE_TEXT_FLAG_MULTI_LINE
- 时间字段: inputType = TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_TIME
*/
#[java_interface(name = "android/text/InputType")]
pub trait InputType {
/// 确定给定文本总体类别的位掩码。当前支持的类别有:TYPE_CLASS_TEXT、TYPE_CLASS_NUMBER、TYPE_CLASS_PHONE、TYPE_CLASS_DATETIME。IME 作者:如果您不了解该类别,则假定 TYPE_CLASS_TEXT 不带任何变体或标志。
const TYPE_MASK_CLASS: i32 = 0x0000000f;
/// 确定基础内容类的变化的位掩码。
const TYPE_MASK_VARIATION: i32 = 0x00000ff0;
/// 提供选项的附加位标志的位掩码。
const TYPE_MASK_FLAGS: i32 = 0x00fff000;
/// 未指定显式类型时的特殊内容类型。这应解释为目标输入连接不够丰富,无法处理和显示候选文本之类的内容,也无法检索当前文本,因此输入法将需要在有限的“生成按键事件”模式下运行(如果支持)。
/// 请注意,某些输入法可能不支持该模式,例如,即使设置了此标志,基于语音的输入法也可能无法生成按键事件。
const TYPE_NULL: i32 = 0x00000000;
/// 普通文本类。该类支持以下标志(只能设置其中一个):TYPE_TEXT_FLAG_CAP_CHARACTERS、TYPE_TEXT_FLAG_CAP_WORDS 和 TYPE_TEXT_FLAG_CAP_SENTENCES。
/// 它还支持以下变体:TYPE_TEXT_VARIATION_NORMAL 和 TYPE_TEXT_VARIATION_URI。如果您无法识别变体,则应假定为普通。
const TYPE_CLASS_TEXT: i32 = 0x00000001;
/// TYPE_CLASS_TEXT 的标志:将所有字符大写。覆盖 TYPE_TEXT_FLAG_CAP_WORDS 和 TYPE_TEXT_FLAG_CAP_SENTENCES。此值明确定义为与 TextUtils#CAP_MODE_CHARACTERS 相同。当然,这只影响有大写和小写字母的语言。
const TYPE_TEXT_FLAG_CAP_CHARACTERS: i32 = 0x00001000;
/// TYPE_CLASS_TEXT 的标志:将每个单词的第一个字符大写。覆盖 TYPE_TEXT_FLAG_CAP_SENTENCES。此值明确定义为与 TextUtils#CAP_MODE_WORDS 相同。当然,这只影响有大写和小写字母的语言。
const TYPE_TEXT_FLAG_CAP_WORDS: i32 = 0x00002000;
/// TYPE_CLASS_TEXT 的标志:将每个句子的第一个字符大写。此值明确定义为与 TextUtils#CAP_MODE_SENTENCES 相同。例如,在英语中,它意味着在句号和空格后大写(请注意,其他语言可能对句号有不同的字符,或者不使用空格,或者使用不同的语法规则)。当然,这只影响有大写和小写字母的语言。
const TYPE_TEXT_FLAG_CAP_SENTENCES: i32 = 0x00004000;
/// TYPE_CLASS_TEXT 的标志:用户正在输入自由格式的文本,该文本应应用自动更正。如果没有此标志,IME 将不会尝试更正拼写错误。除非您真的希望用户在此字段中输入非单词,例如为游戏中的角色选择名称,否则您应该始终设置此标志。与 TYPE_TEXT_FLAG_AUTO_COMPLETE 和 TYPE_TEXT_FLAG_NO_SUGGESTIONS 形成对比:`` 表示 IME 将在用户输入时尝试自动更正拼写错误,但未定义 IME 是否提供显示建议的界面。
const TYPE_TEXT_FLAG_AUTO_CORRECT: i32 = 0x00008000;
//noinspection SpellCheckingInspection
/// TYPE_CLASS_TEXT 的标志:文本编辑器(即应用程序)正在根据其自身的语义自动完成输入的文本,并在用户输入时将其呈现给用户。这通常意味着输入法不应显示候选词,但可以期望编辑器从 android.view.inputmethod.InputMethodSession#displayCompletions InputMethodSession.displayCompletions() 提供自己的完成/候选词,这是编辑器调用 android.view.inputmethod.InputMethodManager#displayCompletions InputMethodManager.displayCompletions() 的结果。
/// 请注意与 TYPE_TEXT_FLAG_AUTO_CORRECT 和 TYPE_TEXT_FLAG_NO_SUGGESTIONS 的对比:`` 表示编辑器应显示一个用于显示建议的界面,但它不提供自己的界面,而是依靠编辑器传递完成/更正。
const TYPE_TEXT_FLAG_AUTO_COMPLETE: i32 = 0x00010000;
/// TYPE_CLASS_TEXT 的标志:可在字段中输入多行文本。如果未设置此标志,则文本字段将限制为一行。当未设置此标志时,IME 还可以选择不显示回车键,因为不需要创建新行。
const TYPE_TEXT_FLAG_MULTI_LINE: i32 = 0x00020000;
/// TYPE_CLASS_TEXT 标志:与此相关的常规文本视图不应该是多行,但是当全屏输入法提供文本时,如果可以的话它应该使用多行。
const TYPE_TEXT_FLAG_IME_MULTI_LINE: i32 = 0x00040000;
/// TYPE_CLASS_TEXT 的标志:输入法不需要显示任何基于字典的候选项。这对于不包含该语言单词且不会从任何基于字典的 补 全 或 更正 中受益的文本视图很有用。设置后,它会覆盖 TYPE_TEXT_FLAG_AUTO_CORRECT 值。请避免使用此选项,除非您确定这是您想要的。许多输入法需要建议才能正常工作,例如基于手势输入的建议。如果您只是不想让 IME 更正拼写错误,请考虑清除 TYPE_TEXT_FLAG_AUTO_CORRECT。
/// 请注意与 TYPE_TEXT_FLAG_AUTO_CORRECT 和 TYPE_TEXT_FLAG_AUTO_COMPLETE 的对比:` ` 表示 IME 不需要显示界面来显示建议。大多数 IME 也会认为这意味着它们不需要尝试自动更正用户正在输入的内容。
const TYPE_TEXT_FLAG_NO_SUGGESTIONS: i32 = 0x00080000;
/// TYPE_CLASS_TEXT 标志:让 IME 知道应用程序需要文本转换建议。文本转换建议适用于具有发音字符和目标字符的音译语言。当用户输入发音字符时,IME 可以向用户提供可能的目标字符。设置此标志后,IME 应通过 Builder#setTextConversionSuggestions(List) 插入文本转换建议,并且 IME 将使用文本转换建议初始化的 TextAttribute 提供给应用程序。要接收附加信息,应用程序需要实现 InputConnection#setComposingText(CharSequence, int, TextAttribute)、InputConnection#setComposingRegion(int, int, TextAttribute) 和 InputConnection#commitText(CharSequence, int, TextAttribute)。
const TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS: i32 = 0x00100000;
/// TYPE_CLASS_TEXT 的默认变体:普通的旧文本。
const TYPE_TEXT_VARIATION_NORMAL: i32 = 0x00000000;
/// type_class_text的变体:输入URI。
const TYPE_TEXT_VARIATION_URI: i32 = 0x00000010;
/// TYPE_CLASS_TEXT 的变体:输入电子邮件地址。
const TYPE_TEXT_VARIATION_EMAIL_ADDRESS: i32 = 0x00000020;
/// TYPE_CLASS_TEXT 的变体:输入电子邮件的主题行。
const TYPE_TEXT_VARIATION_EMAIL_SUBJECT: i32 = 0x00000030;
/// TYPE_CLASS_TEXT 的变体:输入简短、可能非正式的消息,例如即时消息或文本消息。
const TYPE_TEXT_VARIATION_SHORT_MESSAGE: i32 = 0x00000040;
/// TYPE_CLASS_TEXT 的变体:输入较长的、可能正式的消息的内容,例如电子邮件的正文。
const TYPE_TEXT_VARIATION_LONG_MESSAGE: i32 = 0x00000050;
/// TYPE_CLASS_TEXT 的变体:输入一个人的姓名。
const TYPE_TEXT_VARIATION_PERSON_NAME: i32 = 0x00000060;
/// TYPE_CLASS_TEXT 的变体:输入邮寄地址。
const TYPE_TEXT_VARIATION_POSTAL_ADDRESS: i32 = 0x00000070;
/// TYPE_CLASS_TEXT 的变体:输入密码。
const TYPE_TEXT_VARIATION_PASSWORD: i32 = 0x00000080;
/// TYPE_CLASS_TEXT 的变体:输入密码,该密码应该对用户可见。
const TYPE_TEXT_VARIATION_VISIBLE_PASSWORD: i32 = 0x00000090;
/// TYPE_CLASS_TEXT 的变体:在网络表单内输入文本。
const TYPE_TEXT_VARIATION_WEB_EDIT_TEXT: i32 = 0x000000a0;
/// TYPE_CLASS_TEXT 的变体:输入文本来过滤列表的内容等。
const TYPE_TEXT_VARIATION_FILTER: i32 = 0x000000b0;
/// TYPE_CLASS_TEXT 的变体:输入语音发音的文本,例如联系人中的语音姓名字段。这最适用于一种拼写可能有多种语音读法的语言,例如日语。
const TYPE_TEXT_VARIATION_PHONETIC: i32 = 0x000000c0;
//noinspection SpellCheckingInspection
/// TYPE_CLASS_TEXT 的变体:在 Web 表单中输入电子邮件地址。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;如果不是,则当通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被视为 TYPE_TEXT_VARIATION_EMAIL_ADDRESS。
const TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS: i32 = 0x000000d0;
//noinspection SpellCheckingInspection
/// TYPE_CLASS_TEXT 的变体:在 Web 表单中输入密码。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;如果不是,则当通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被视为 TYPE_TEXT_VARIATION_PASSWORD。
const TYPE_TEXT_VARIATION_WEB_PASSWORD: i32 = 0x000000e0;
/// 数字文本类。此类支持以下标志:TYPE_NUMBER_FLAG_SIGNED 和 TYPE_NUMBER_FLAG_DECIMAL。它还支持以下变体:TYPE_NUMBER_VARIATION_NORMAL 和 TYPE_NUMBER_VARIATION_PASSWORD。IME 作者:如果您无法识别变体,则应假定为正常。
const TYPE_CLASS_NUMBER: i32 = 0x00000002;
/// TYPE_CLASS_NUMBER 标志:该数字是有符号的,允许在开头使用正号或负号。
const TYPE_NUMBER_FLAG_SIGNED: i32 = 0x00001000;
/// TYPE_CLASS_NUMBER 标志:数字是十进制,允许小数点提供分数值。
const TYPE_NUMBER_FLAG_DECIMAL: i32 = 0x00002000;
//noinspection SpellCheckingInspection
/// TYPE_CLASS_NUMBER 的默认变体:普通数字文本。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;否则,在通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被丢弃。
const TYPE_NUMBER_VARIATION_NORMAL: i32 = 0x00000000;
//noinspection SpellCheckingInspection
/// TYPE_CLASS_NUMBER 的变体:输入数字密码。此功能已添加到 android.os.Build.VERSION_CODES#HONEYCOMB。IME 必须以此 API 版本或更高版本为目标才能看到此输入类型;否则,在通过 android.view.inputmethod.EditorInfo#makeCompatible(int) EditorInfo.makeCompatible(int) 传递时,此类型的请求将被丢弃。
const TYPE_NUMBER_VARIATION_PASSWORD: i32 = 0x00000010;
/// 电话号码类。此类目前不支持任何变体或标志。
const TYPE_CLASS_PHONE: i32 = 0x00000003;
/// 日期和时间类。它支持以下变体:TYPE_DATETIME_VARIATION_NORMAL TYPE_DATETIME_VARIATION_DATE 和 TYPE_DATETIME_VARIATION_TIME。
const TYPE_CLASS_DATETIME: i32 = 0x00000004;
/// TYPE_CLASS_DATETIME 的默认变体:允许输入日期和时间。
const TYPE_DATETIME_VARIATION_NORMAL: i32 = 0x00000000;
/// TYPE_CLASS_DATETIME 的默认变体:仅允许输入日期。
const TYPE_DATETIME_VARIATION_DATE: i32 = 0x00000010;
/// TYPE_CLASS_DATETIME 的默认变体:只允许输入时间。
const TYPE_DATETIME_VARIATION_TIME: i32 = 0x00000020;
}
#[doc(hidden)]
#[java_class(name = "android/text/InputTypeImpl")]
pub struct InputTypeImpl;
impl InputType for InputTypeImpl {}
/**
这是文本的接口,其内容和标记可以更改(与字符串等不可变文本相反)。如果您创建可编辑的 DynamicLayout,则布局将随着文本的更改而重新排列。
*/
#[java_interface(name = "android/text/Editable")]
pub trait Editable: CharSequence {
#[doc(hidden)]
type Cs: CharSequence;
#[doc(hidden)]
type E: Editable;
/**
用源切片 start…end 的副本替换此 Editable 中指定范围 (st…en) 的文本。目标切片可能为空,在这种情况下操作为插入;源切片可能为空,在这种情况下操作为删除。
在提交更改之前,使用 setFilters 设置的每个过滤器都有机会修改源文本。如果源是 Spanned,则来自源的跨度将保留到 Editable 中。Editable 中完全覆盖替换范围的现有跨度将被保留,但严格在替换范围内的任何跨度将被删除。
如果源包含带有 Spanned.SPAN_PARAGRAPH 标志的跨度,并且它不满足段落边界约束,则不会保留它。作为特殊情况,即使替换了光标所在的整个范围,光标位置也会保留。
返回:对此对象的引用。
*/
fn replace(&self, st: i32, en: i32, source: Self::Cs, start: i32, end: i32) -> Self::E;
/**
方便 replace(st, en, text, 0, text.length())
*/
fn replace_convenience(&self, st: i32, en: i32, text: Self::Cs) -> Self::E;
/**
方便 replace(where, where, text, start, end)
*/
fn insert(&self, r#where: i32, text: Self::Cs, start: i32, end: i32) -> Self::E;
/**
方便 replace(where, where, text, 0, text.length());
*/
fn insert_convenience(&self, r#where: i32, text: Self::Cs) -> Self::E;
/**
方便 replace(st, en, "", 0, 0)
*/
fn delete(&self, st: i32, en: i32) -> Self::E;
/// 方便 replace(length(), length(), text, 0, text.length())
fn append_convenience(&self, text: Self::Cs) -> Self::E;
/// 方便 replace(length(), length(), text, start, end)
fn append(&self, text: Self::Cs, start: i32, end: i32) -> Self::E;
/// 方便 append(String.valueOf(text))
fn append_char(&self, text: char) -> Self::E;
/// 方便 replace(0, length(), "", 0, 0).
/// 请注意,这将清除文本,而不是跨度;如果需要,请使用 clearSpans。
fn clear(&self);
/// 从可编辑对象中移除所有跨度,就像在每个跨度上调用 removeSpan 一样。
fn clear_spans(&self);
}
#[doc(hidden)]
#[java_class(name = "android/text/EditableImpl", extends=CharSequenceImpl)]
pub struct EditableImpl;
impl CharSequence for EditableImpl {
fn length(&self) -> i32 {
self._based.length()
}
fn char_at(&self, index: i32) -> Result<char, droid_wrap_utils::Error> {
self._based.char_at(index)
}
}
#[java_implement]
impl Editable for EditableImpl {
type Cs = CharSequenceImpl;
type E = Self;
#[java_method(type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
fn replace(
&self,
st: i32,
en: i32,
source: <Self as Editable>::Cs,
start: i32,
end: i32,
) -> Self::E {
}
#[java_method(overload=replace, type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
fn replace_convenience(&self, st: i32, en: i32, text: <Self as Editable>::Cs) -> Self::E {}
#[java_method(type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
fn insert(&self, r#where: i32, text: <Self as Editable>::Cs, start: i32, end: i32) -> Self::E {}
#[java_method(overload=insert, type_bound=(<Self as Editable>::Cs, CharSequence), type_bound=(Self::E, Editable))]
fn insert_convenience(&self, r#where: i32, text: <Self as Editable>::Cs) -> Self::E {}
#[java_method(type_bound=(Self::E, Editable))]
fn delete(&self, st: i32, en: i32) -> Self::E {}
#[java_method(overload=append, type_bound=(<Self as Editable>::Cs, CharSequence),type_bound=(Self::E, Editable))]
fn append_convenience(&self, text: <Self as Editable>::Cs) -> Self::E {}
#[java_method(type_bound=(<Self as Editable>::Cs, CharSequence),type_bound=(Self::E, Editable))]
fn append(&self, text: <Self as Editable>::Cs, start: i32, end: i32) -> Self::E {}
#[java_method(type_bound=(Self::E, Editable))]
fn append_char(&self, text: char) -> Self::E {}
#[java_method]
fn clear(&self) {}
#[java_method]
fn clear_spans(&self) {}
}
/**
当此类型的对象附加到 Editable 时,其方法将在文本改变时被调用。
*/
#[java_interface(name = "android/text/TextWatcher")]
pub trait TextWatcher {
#[doc(hidden)]
type Cs: CharSequence;
#[doc(hidden)]
type E: Editable;
/// 调用此方法是为了通知您,在 s 中,从 start 开始的 count 个字符即将被长度为 after 的新文本替换。
/// 尝试从此回调更改 s 是错误的。
fn before_text_changed(&self, s: Self::Cs, start: i32, count: i32, after: i32);
/// 调用此方法是为了通知您,在 s 中,从 start 开始的 count 个字符刚刚替换了之前长度为 before 的旧文本。
/// 尝试通过此回调更改 s 是错误的。
fn on_text_changed(&self, s: Self::Cs, start: i32, before: i32, count: i32);
//noinspection SpellCheckingInspection
/// 调用此方法是为了通知您,在 s 中的某个地方,文本已发生更改。从此回调对 s 进行进一步更改是合法的,但请注意不要陷入无限循环,因为您所做的任何更改都会导致此方法再次递归调用。
/// (您不会被告知更改发生的位置,因为其他 afterTextChanged() 方法可能已经进行了其他更改并使偏移量无效。但如果您需要在此处知道,您可以在 onTextChanged 中使用 Spannable.setSpan 来标记您的位置,然后从此处查找 span 结束的位置。
fn after_text_changed(&self, s: Self::E);
}
#[doc(hidden)]
#[java_class(name = "android/text/TextWatcherImpl")]
pub struct TextWatcherImpl {
before_text_changed:
Box<dyn Fn(<TextWatcherImpl as TextWatcher>::Cs, i32, i32, i32) + Send + Sync>,
on_text_changed: Box<dyn Fn(<TextWatcherImpl as TextWatcher>::Cs, i32, i32, i32) + Send + Sync>,
after_text_changed: Box<dyn Fn(<TextWatcherImpl as TextWatcher>::E) + Send + Sync>,
}
impl TextWatcherImpl {
pub fn from_fn(
before_text_changed: impl Fn(
/* s */ <Self as TextWatcher>::Cs,
/* start */ i32,
/* count */ i32,
/* after */ i32,
) + Send
+ Sync
+ 'static,
on_text_changed: impl Fn(
/* s */ <Self as TextWatcher>::Cs,
/* start */ i32,
/* before */ i32,
/* count */ i32,
) + Send
+ Sync
+ 'static,
after_text_changed: impl Fn(/* s */ <Self as TextWatcher>::E) + Send + Sync + 'static,
) -> Arc<Self> {
Self::new(TextWatcherImplDefault {
before_text_changed: Box::new(before_text_changed),
on_text_changed: Box::new(on_text_changed),
after_text_changed: Box::new(after_text_changed),
})
}
}
impl Default for TextWatcherImplDefault {
fn default() -> Self {
Self {
before_text_changed: Box::new(|_, _, _, _| ()),
on_text_changed: Box::new(|_, _, _, _| ()),
after_text_changed: Box::new(|_| ()),
}
}
}
#[java_implement]
impl TextWatcher for TextWatcherImpl {
type Cs = CharSequenceImpl;
type E = EditableImpl;
fn before_text_changed(
&self,
s: <Self as TextWatcher>::Cs,
start: i32,
count: i32,
after: i32,
) {
(self.before_text_changed)(s, start, count, after)
}
fn on_text_changed(&self, s: <Self as TextWatcher>::Cs, start: i32, before: i32, count: i32) {
(self.on_text_changed)(s, start, before, count)
}
fn after_text_changed(&self, s: <Self as TextWatcher>::E) {
(self.after_text_changed)(s)
}
}
/// 测试android.text
#[cfg(feature = "test_android_text")]
pub fn test() {
let watcher = TextWatcherImpl::from_fn(
|_s, _start, _count, _after| (),
|_s, _start, _before, _count| (),
|_s| (),
);
dbg!(watcher);
}