droid_wrap/android/widget.rs
1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14use crate::{
15 JObjNew, JObjRef, JProxy, JType, Result,
16 android::{
17 content::Context,
18 text::TextWatcher,
19 view::{KeyEvent, ViewGroup, ViewGroup_LayoutParams, ViewGroup_MarginLayoutParams},
20 },
21 java::lang::CharSequence,
22 java_class, java_constructor, java_field, java_implement, java_interface, java_method,
23};
24use std::sync::Arc;
25
26//noinspection SpellCheckingInspection
27/**
28用于输入和修改文本的用户界面元素。定义编辑文本小部件时,必须指定 android.R.styleable.TextView_inputType 属性。例如,对于纯文本输入,将 inputType 设置为 "text":
29<EditText
30 android:id="@+id/plain_text_input"
31 android:layout_height="wrap_content"
32 android:layout_width="match_parent"
33 android:inputType="text"/>
34选择输入类型可配置显示的键盘类型、可接受的字符以及编辑文本的外观。例如,如果您想接受秘密数字(如唯一 PIN 码或序列号),可以将 inputType 设置为 "numericPassword"。inputType 为 "numericPassword" 会导致编辑文本仅接受数字,聚焦时显示数字键盘,并屏蔽输入的文本以保护隐私。有关其他 android.R.styleable.TextView_inputType 设置的示例,请参阅文本字段指南。
35您还可以通过在编辑文本中添加 android.text.TextWatcher 来接收用户更改文本时的回调。例如,当您想在进行更改时添加自动保存功能或验证用户输入的格式时,这很有用。您可以使用 TextView.addTextChangedListener 方法添加文本观察器。此小部件不支持自动调整文本大小。 XML 属性请参阅 EditText 属性、TextView 属性、View 属性
36*/
37#[java_class(name = "android/widget/EditText", extends=TextView)]
38pub struct EditText;
39
40impl EditText {
41 #[doc(hidden)]
42 #[java_constructor]
43 pub fn new(context: &Context) -> Self {}
44
45 /**
46 方便选择。选择所有。
47 */
48 #[java_method]
49 pub fn select_all(&self) {}
50
51 //noinspection SpellCheckingInspection
52 /**
53 方便选择。
54 setSelection(Spannable, int, int)。
55 */
56 #[java_method]
57 pub fn set_selection(&self, start: i32, stop: i32) -> Result<()> {}
58
59 /**
60 子类会重写此功能以指定它们默认具有 KeyListener,即使在 XML 选项中没有特别调用。
61 */
62 pub fn get_default_editable(&self) -> bool {
63 self._based.get_default_editable()
64 }
65}
66
67/**
68向用户显示文本的用户界面元素。要提供用户可编辑的文本,请参阅 EditText。
69以下代码示例展示了一种典型用法,其中包含 XML 布局和用于修改文本视图内容的代码:
70<LinearLayout
71 xmlns:android="<http://schemas.android.com/apk/res/android>"
72 android:layout_width="match_parent"
73 android:layout_height="match_parent">
74 <TextView
75 android:id="@+id/text_view_id"
76 android:layout_height="wrap_content"
77 android:layout_width="wrap_content"
78 android:text="@string/hello" />
79</LinearLayout>
80此代码示例演示了如何修改上一个 XML 布局中定义的文本视图的内容:
81public class MainActivity extends Activity {
82 protected void onCreate(Bundle savedInstanceState) {
83 super.onCreate(savedInstanceState);
84 setContentView(R.layout.activity_main);
85 final TextView helloTextView = (TextView) findViewById(R.id.text_view_id);
86 helloTextView.setText(R.string.user_greeting);
87 }
88}
89要自定义 TextView 的外观,请参阅样式和主题。 XML 属性请参阅 TextView 属性、视图属性
90*/
91#[java_class(name = "android/widget/TextView", extends=super::view::View)]
92pub struct TextView;
93
94impl TextView {
95 #[doc(hidden)]
96 #[java_constructor]
97 pub fn new(context: &Context) -> Self {}
98
99 //noinspection SpellCheckingInspection
100 /**
101 设置要显示的文本。TextView 不接受类似 HTML 的格式,但您可以使用 XML 资源文件中的文本字符串进行这种格式设置。要设置字符串的样式,请将 android.text.style.* 对象附加到 android.text.SpannableString,或参阅可用资源类型文档,了解在 XML 资源文件中设置格式化文本的示例。必要时,TextView 将使用 Spannable.Factory 创建最终或中间 Spannable。同样,它将使用 Editable.Factory 创建最终或中间 Editable。如果传递的文本是 PrecomputedText,但用于创建 PrecomputedText 的参数与此 TextView 不匹配,则会引发 IllegalArgumentException。要确保参数匹配,您可以在调用此方法之前调用 setTextMetricsParams。
102 可能抛出IllegalArgumentException 如果传递的文本是 PrecomputedText,但用于创建 PrecomputedText 的参数与此 TextView 不匹配。
103 `text` 要显示的文本
104 */
105 #[java_method]
106 pub fn set_text<CS: CharSequence>(&self, text: Option<CS>) {}
107
108 //noinspection SpellCheckingInspection
109 /**
110 返回 TextView 正在显示的文本。如果使用 BufferType.SPANNABLE 或 BufferType.EDITABLE 参数调用 setText(CharSequence),则可以将此方法的返回值分别转换为 Spannable 或 Editable。返回值的内容不应修改。如果您想要一个可修改的内容,您应该先制作自己的副本。
111 返回:文本视图显示的文本。
112 */
113 #[java_method]
114 pub fn get_text<CS: CharSequence>(&self) -> Option<CS> {}
115
116 /**
117 设置 TextView 的文本为空时显示的文本。Null 表示使用普通的空文本。Hint 目前不参与确定视图的大小。
118 `hint` 要显示的提示文字。
119 */
120 #[java_method]
121 pub fn set_hint<CS: CharSequence>(&self, hint: Option<CS>) {}
122
123 /**
124 返回TextView的文本为空时显示的提示。
125 */
126 #[java_method]
127 pub fn get_hint<CS: CharSequence>(&self) -> Option<CS> {}
128
129 /**
130 子类会重写此功能以指定它们默认具有 KeyListener,即使在 XML 选项中没有特别调用。
131 */
132 #[java_method]
133 pub fn get_default_editable(&self) -> bool {}
134
135 /**
136 使用为 EditorInfo.inputType 定义的常量设置内容的类型。这将通过调用 setKeyListener(KeyListener) 来更改键侦听器,以匹配给定的内容类型。如果给定的内容类型是 EditorInfo.TYPE_NULL,则不会为此文本视图显示软键盘。
137 请注意,如果更改输入类型的 EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE 标志,则显示的最大行数(请参阅 setMaxLines(int))将被修改。
138 `type` 输入类型。
139 */
140 #[java_method]
141 pub fn set_input_type(&self, r#type: i32) {}
142
143 /**
144 获取可编辑内容的类型。
145 */
146 #[java_method]
147 pub fn get_input_type(&self) -> i32 {}
148
149 /**
150 将 TextWatcher 添加到此 TextView 的文本发生变化时调用其方法的列表中。
151 在 1.0 中,TextWatcher.afterTextChanged 方法在 setText 调用后被错误地未调用。现在,如果有任何文本更改侦听器执行 setText 会强制缓冲区类型为可编辑(否则不会为可编辑)并调用此方法。
152 `watcher` 文字监视器。
153 */
154 #[java_method]
155 pub fn add_text_changed_listener<TW: TextWatcher>(&self, watcher: &TW) {}
156
157 /**
158 从TextView的文本改变时会被调用其方法的TextWatcher列表中移除指定的TextWatcher。
159 `watcher` 文字监视器。
160 */
161 #[java_method]
162 pub fn remove_text_changed_listener<TW: TextWatcher>(&self, watcher: &TW) {}
163
164 /**
165 设置一个特殊侦听器,在文本视图上执行操作时调用。当按下回车键或用户选择提供给 IME 的操作时,将调用此侦听器。
166 设置此项意味着普通硬键事件不会在文本视图中插入换行符,即使它是多行的;但是,按住 ALT 修饰键将允许用户插入换行符。
167 */
168 #[java_method]
169 pub fn set_on_editor_action_listener<L: TextView_OnEditorActionListener>(&self, l: &L) {}
170}
171
172/**
173在编辑器上执行操作时调用的回调的接口定义。
174*/
175#[allow(non_camel_case_types)]
176#[java_interface(name = "android/widget/TextView$OnEditorActionListener")]
177pub trait TextView_OnEditorActionListener {
178 /**
179 执行操作时调用。
180 返回:如果您已使用该操作,则返回 true,否则为 false。
181 `v` 被点击的视图。
182 `action_id` 操作的标识符。这将是您提供的标识符,或 EditorInfo。如果由于按下 Enter 键而调用,则为 IME_NULL。从 Android 14 开始,如果输入限制为一行,则在由 Enter 键触发时还将包含操作标识符。
183 `event` 如果由 Enter 键触发,则这是事件;否则,这是 null。
184 */
185 fn on_editor_action(&self, v: TextView, action_id: i32, event: Option<KeyEvent>) -> bool;
186}
187
188#[doc(hidden)]
189#[allow(non_camel_case_types)]
190#[java_class(name = "android/widget/TextView$OnEditorActionListenerImpl")]
191pub struct TextView_OnEditorActionListenerImpl(
192 Box<dyn Fn(TextView, i32, Option<KeyEvent>) -> bool + Send + Sync>,
193);
194
195impl Default for TextView_OnEditorActionListenerImplDefault {
196 fn default() -> Self {
197 Self(Box::new(|v, action_id, event| {
198 unimplemented!("{:?}, {}, {:?}", v, action_id, event)
199 }))
200 }
201}
202
203impl TextView_OnEditorActionListenerImpl {
204 pub fn from_fn(
205 func: impl Fn(
206 /* v */ TextView,
207 /* action_id */ i32,
208 /* event */ Option<KeyEvent>,
209 ) -> bool
210 + Send
211 + Sync
212 + 'static,
213 ) -> Result<Arc<Self>> {
214 Self::new(TextView_OnEditorActionListenerImplDefault(Box::new(func)))
215 }
216}
217
218#[java_implement]
219impl TextView_OnEditorActionListener for TextView_OnEditorActionListenerImpl {
220 fn on_editor_action(&self, v: TextView, action_id: i32, event: Option<KeyEvent>) -> bool {
221 self.0(v, action_id, event)
222 }
223}
224
225/// 要在活动中显示按钮,请将按钮添加到活动的布局 XML 文件:
226/// <Button
227/// android:id="@+id/button_id"
228/// android:layout_height="wrap_content"
229/// android:layout_width="wrap_content"
230/// android:text="@string/self_destruct" />
231/// 要指定按下按钮时的操作,请在相应的活动代码中对按钮对象设置单击监听器:
232/// public class MyActivity extends Activity {
233/// protected void onCreate(Bundle savedInstanceState) {
234/// super. onCreate(savedInstanceState);
235/// setContentView(R. layout. content_layout_id);
236/// final Button button = findViewById(R. id. button_id);
237/// button.setOnClickListener(new View.OnClickListener() {
238/// public void onClick(View v) {
239/// // 用户按下按钮后,此处的代码在主线程上执行
240/// }
241/// });
242/// }
243/// }
244/// 上面的代码片段创建了一个 android.view.View.OnClickListener 实例,并使用 setOnClickListener(View.OnClickListener) 将侦听器连接到按钮。因此,系统会在用户按下按钮后执行您在 onClick(View) 中编写的代码。
245/// 系统在主线程上执行 onClick 中的代码。这意味着您的 onClick 代码必须快速执行,以避免延迟您的应用对进一步用户操作的响应。有关更多详细信息,请参阅保持您的应用响应迅速。
246/// 每个按钮都使用系统的默认按钮背景进行样式设置,该背景通常因平台的不同版本而异。如果您对默认按钮样式不满意,可以对其进行自定义。有关更多详细信息和代码示例,请参阅按钮样式指南。有关按钮上可用的所有 XML 样式属性,请参阅按钮属性、TextView 属性、视图属性。请参阅样式和主题指南,了解如何实现和组织与样式相关的属性的覆盖。
247#[java_class(name = "android/widget/Button", extends=TextView)]
248pub struct Button;
249
250impl Button {
251 /**
252 从代码创建按钮时要使用的简单构造函数。
253 `context` 按钮正在运行的上下文,它可以访问当前主题,资源等。
254 */
255 #[java_constructor]
256 pub fn new(context: &Context) -> Self {}
257}
258
259/**
260将其他视图水平排列在一列中或垂直排列在一行中的布局。
261以下代码片段展示了如何在布局 XML 文件中包含线性布局:
262<LinearLayout xmlns:android="<http://schemas.android.com/apk/res/android>"
263 android:layout_width="match_parent"
264 android:layout_height="match_parent"
265 android:paddingLeft="16dp"
266 android:paddingRight="16dp"
267 android:orientation="horizontal"
268 android:gravity="center">
269 <!-- 在此处添加其他小部件或布局标签.这些标签被视为线性布局的 "子视图" 或 "子项" -->
270 </LinearLayout>
271设置 android:orientation 以指定子视图是否显示在行或列中。要控制线性布局如何对齐其包含的所有视图,请为 android:gravity 设置一个值。例如,上面的代码片段将 android:gravity 设置为 "center"。
272您设置的值会影响单行或单列内所有子视图的水平和垂直对齐。您可以在各个子视图上设置 android:layout_weight 以指定线性布局如何在其包含的视图之间划分剩余空间。有关示例,请参阅线性布局指南。请参阅 LinearLayout.LayoutParams 以了解您可以在子视图上设置的其他属性,以影响其在包含的线性布局中的位置和大小。
273*/
274#[java_class(name = "android/widget/LinearLayout", extends=ViewGroup)]
275pub struct LinearLayout;
276
277impl LinearLayout {
278 #[doc(hidden)]
279 pub const HORIZONTAL: i32 = 0;
280
281 #[doc(hidden)]
282 pub const VERTICAL: i32 = 1;
283
284 #[doc(hidden)]
285 #[java_constructor]
286 pub fn new(context: &Context) -> Self {}
287
288 /**
289 布局应该是一列还是一行。
290 `orientation` 传递水平或垂直。默认值为水平。
291 */
292 #[java_method]
293 pub fn set_orientation(&self, orientation: i32) {}
294
295 /**
296 返回当前方向。
297 返回:水平或垂直
298 */
299 #[java_method]
300 pub fn get_orientation(&self) -> i32 {}
301}
302
303/**
304与 ViewLinearLayout 相关的每个子布局信息。
305*/
306#[allow(non_camel_case_types)]
307#[java_class(name = "android/widget/LinearLayout$LayoutParams", extends=ViewGroup_MarginLayoutParams)]
308pub struct LinearLayout_LayoutParams;
309
310impl LinearLayout_LayoutParams {
311 /// 指示 LinearLayout 中有多少额外空间将分配给与这些 LayoutParams 关联的视图。如果视图不应拉伸,请指定 0。否则,额外的像素将在权重大于 0 的所有视图之间按比例分配。
312 #[java_field]
313 pub fn get_weight(&self) -> f32 {}
314
315 /// 指示 LinearLayout 中有多少额外空间将分配给与这些 LayoutParams 关联的视图。如果视图不应拉伸,请指定 0。否则,额外的像素将在权重大于 0 的所有视图之间按比例分配。
316 #[java_field]
317 pub fn set_weight(&self, value: f32) {}
318
319 /// 与这些 LayoutParams 关联的视图的重力。
320 #[java_field]
321 pub fn get_gravity(&self) -> i32 {}
322
323 /// 与这些 LayoutParams 关联的视图的重力。
324 #[java_field]
325 pub fn set_gravity(&self, value: i32) {}
326
327 #[doc(hidden)]
328 #[java_constructor]
329 pub fn new(width: i32, height: i32) -> Self {}
330
331 /**
332 创建一组具有指定宽度、高度和权重的新布局参数。
333 `width` 宽度,可以是 MATCH_PARENT、WRAP_CONTENT 或固定大小(以像素为单位)
334 `height` 高度,可以是 MATCH_PARENT、WRAP_CONTENT 或固定大小(以像素为单位)
335 `weight` 权重
336 */
337 #[java_constructor]
338 pub fn new_with_weight(width: i32, height: i32, weight: f32) -> Self {}
339
340 #[doc(hidden)]
341 #[allow(unused_qualifications)]
342 #[java_constructor]
343 pub fn from_layout_params(p: &ViewGroup_LayoutParams) -> Self {}
344
345 #[doc(hidden)]
346 #[java_constructor]
347 pub fn from_margin_layout_params(source: &ViewGroup_MarginLayoutParams) -> Self {}
348}
349
350/// 测试android.widget
351#[cfg(feature = "test_android_widget")]
352pub fn test() {
353 use crate::{
354 android::{
355 app::Activity,
356 text::{InputType, InputTypeImpl, TextWatcherImpl},
357 },
358 java::lang::{CharSequenceExt, CharSequenceImpl},
359 };
360 let act = Activity::fetch().unwrap();
361 let edit = EditText::new(&act);
362 assert!(edit.to_string().starts_with("android.widget.EditText"));
363 edit.select_all();
364 // let _ = edit.set_selection(0,2);
365 let editor_listener = TextView_OnEditorActionListenerImpl::from_fn(|_, _, _| true).unwrap();
366 edit.set_on_editor_action_listener(editor_listener.as_ref());
367
368 let text = TextView::new(&act);
369 assert!(text.to_string().starts_with("android.widget.TextView"));
370 text.set_text("你好".to_char_sequence::<CharSequenceImpl>().ok());
371 assert_eq!(
372 "你好".to_char_sequence().ok(),
373 text.get_text::<CharSequenceImpl>()
374 );
375 text.set_hint("世界".to_char_sequence::<CharSequenceImpl>().ok());
376 assert_eq!(
377 "世界".to_char_sequence::<CharSequenceImpl>().ok(),
378 text.get_hint()
379 );
380 text.set_input_type(InputTypeImpl::TYPE_CLASS_DATETIME);
381 assert_eq!(InputTypeImpl::TYPE_CLASS_DATETIME, text.get_input_type());
382 let button = Button::new(&act);
383 button.set_text("测试按钮".to_char_sequence::<CharSequenceImpl>().ok());
384 let layout = LinearLayout::new(&act);
385 layout.set_orientation(LinearLayout::VERTICAL);
386 assert_eq!(LinearLayout::VERTICAL, layout.get_orientation());
387 let params = LinearLayout_LayoutParams::new_with_weight(
388 ViewGroup_LayoutParams::MATCH_PARENT,
389 ViewGroup_LayoutParams::MATCH_PARENT,
390 1.0,
391 );
392 assert_eq!(1.0, params.get_weight());
393 let watcher = TextWatcherImpl::from_fn(
394 |s, start, count, after| {
395 dbg!((s, start, count, after));
396 },
397 |s, start, before, count| {
398 dbg!((s, start, before, count));
399 },
400 |s| {
401 dbg!(s);
402 },
403 )
404 .unwrap();
405 button.add_text_changed_listener(watcher.as_ref());
406 button.remove_text_changed_listener(watcher.as_ref());
407}