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&lt;LinearLayout
71    xmlns:android="<http://schemas.android.com/apk/res/android>"
72    android:layout_width="match_parent"
73    android:layout_height="match_parent"&gt;
74  &lt;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" /&gt;
79&lt;/LinearLayout&gt;
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///   &lt;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" /&gt;
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&lt;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"&gt;
269    &lt;!-- 在此处添加其他小部件或布局标签.这些标签被视为线性布局的 "子视图" 或 "子项" --&gt;
270  &lt;/LinearLayout&gt;
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}