创建电话号码转换器应用
在本练习中,你将为电话拨号器应用构造 UI,并实现此 UI 背后的逻辑。
你将构建一个 UI,此 UI 利用 .NET MAUI 和 .NET MAUI Essentials 包的 UI 功能拨打电话。
该应用使用户可在输入字段中键入文本,并将该文本转换为数字。 它将使用电话键盘上显示的字母作为转换的基础。 例如,字母“cab”会转换为“222”,因为数字“2”含有 a、b、c 三个字母。
你将继续使用在上一练习中创建的 Phoneword 解决方案。
向应用添加新的 C# 源文件
-
在 Visual Studio 中打开 Phoneword 的解决方案(如果你尚未打开它)。
-
在解决方案资源管理器窗口中,右键单击“Phoneword”项目,选择“添加”,然后选择“类”。
-
在“添加新项”对话框中,将类文件命名为 PhonewordTranslator.cs,然后选择“添加”。
添加转换逻辑
将类文件的内容替换为以下代码并保存文件。 PhonewordTranslator
类中的静态方法 ToNumber
会将号码从字母数字文本转换成常规数字电话号码。
using System.Text; namespace Core; public static class PhonewordTranslator { public static string ToNumber(string raw) { if (string.IsNullOrWhiteSpace(raw)) return null; raw = raw.ToUpperInvariant(); var newNumber = new StringBuilder(); foreach (var c in raw) { if (" -0123456789".Contains(c)) newNumber.Append(c); else { var result = TranslateToNumber(c); if (result != null) newNumber.Append(result); // Bad character? else return null; } } return newNumber.ToString(); } static bool Contains(this string keyString, char c) { return keyString.IndexOf(c) >= 0; } static readonly string[] digits = { "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ" }; static int? TranslateToNumber(char c) { for (int i = 0; i < digits.Length; i++) { if (digits[i].Contains(c)) return 2 + i; } return null; } }
-
在“Phoneword”项目中打开 MainPage.xaml 文件。
-
删除
ScrollView
控件及其内容,只留下ContentPage
控件:<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Phoneword.MainPage"> </ContentPage>
-
在 ContentPage 中添加一个垂直方向的
VerticalStackLayout
控件,间距为 15 个单位,填充为 20 个单位:<ContentPage ... > <VerticalStackLayout Spacing="15" Padding="20"> </VerticalStackLayout> </ContentPage>
-
将
Label
控件添加到 StackLayout:<ContentPage ... > <VerticalStackLayout ...> <Label Text = "Enter a Phoneword" FontSize ="20"/> </VerticalStackLayout> </ContentPage>
-
将
Entry
控件添加到 StackLayout(标签下方)。Entry
控件提供一个文本框,用户可以在其中输入数据。 在此代码中,属性x:Name
赋予控件一个名称。 稍后你将在应用的代码中引用此控件:<ContentPage ... > <VerticalStackLayout ...> <Label .../> <Entry x:Name = "PhoneNumberText" Text = "1-555-NETMAUI" /> </VerticalStackLayout> </ContentPage>
-
添加 Entry 控件之后,将两个
Button
控件添加到 VerticalStackLayout。 这两个按钮当前都不起作用,而且第二个按钮最初处于禁用状态。 在下一个任务中,你将添加代码来处理这两个按钮的Clicked
事件:<ContentPage ... > <VerticalStackLayout ...> <Label .../> <Entry ... /> <Button x:Name = "TranslateButton" Text = "Translate" Clicked = "OnTranslate"/> <Button x:Name = "CallButton" Text = "Call" IsEnabled = "False" Clicked = "OnCall"/> </VerticalStackLayout> </ContentPage>
响应 TranslateButton 按钮点击
-
在解决方案资源管理器窗口中展开 MainPage.xaml 项并打开 MainPage.xaml.cs 代码隐藏文件。
-
在
MainPage
类中,删除count
变量和OnCounterClicked
方法。 此类应如下所示:namespace Phoneword; public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); } }
-
将
translatedNumber
字符串变量和以下OnTranslate
方法添加到MainPage
类,位于构造函数之后。OnTranslate
方法从Entry
控件的Text
属性中检索电话号码,并将其传递给之前创建的PhonewordTranslator
类的静态ToNumber
方法。public partial class MainPage : ContentPage { ... string translatedNumber; private void OnTranslate(object sender, EventArgs e) { string enteredNumber = PhoneNumberText.Text; translatedNumber = Core.PhonewordTranslator.ToNumber(enteredNumber); if (!string.IsNullOrEmpty(translatedNumber)) { // TODO: } else { // TODO: } } }
备注
在下一步中,你将填写此代码的缺失 TODO 位。
-
在
OnTranslate
方法中,添加代码以更改“呼叫”按钮的Text
属性,以便在成功转换后包含电话号码。 可以使用存储在 translatedNumber 字段中的值。 此外,根据转换的成功情况启用和禁用按钮。 例如,如果TranslateNumber
返回 NULL,则禁用该按钮,但如果成功,则启用该按钮。private void OnTranslate(object sender, EventArgs e) { string enteredNumber = PhoneNumberText.Text; translatedNumber = Core.PhonewordTranslator.ToNumber(enteredNumber); if (!string.IsNullOrEmpty(translatedNumber)) { CallButton.IsEnabled = true; CallButton.Text = "Call " + translatedNumber; } else { CallButton.IsEnabled = false; CallButton.Text = "Call"; } }
为 CallButton 按钮创建事件方法
-
将
OnCall
事件处理方法添加到MainPage
类的末尾。 此方法将使用异步操作,因此请将其标记为async
:public partial class MainPage : ContentPage { ... async void OnCall(object sender, System.EventArgs e) { } }
-
在
OnCall
方法中,使用 Page.DisplayAlert 方法提示用户,询问他们是否想要拨号。DisplayAlert
的参数是用于“接受”和“取消”按钮文本的标题、消息以及两个字符串。 它会返回一个布尔,指示是否已按下“接受”按钮来关闭对话框。async void OnCall(object sender, System.EventArgs e) { if (await this.DisplayAlert( "Dial a Number", "Would you like to call " + translatedNumber + "?", "Yes", "No")) { // TODO: dial the phone } }
测试应用程序
-
在 Visual Studio 工具栏中,选择“Windows 计算机”配置文件并开始调试。
-
点击“转换”按钮,将默认文本转换为有效的电话号码。 “呼叫”按钮上的描述文字应更改为“呼叫 1-555-6386284”:
-
点击“呼叫”按钮。 验证是否出现要求你确认操作的提示。 请选择“否”。
-
返回到 Visual Studio 并停止调试。
拨打电话号码
-
在 MainPage.xaml.cs 代码隐藏文件中,编辑 OnCall 方法,并用以下
try/catch
块替换 TODO 注释:async void OnCall(object sender, System.EventArgs e) { if (await this.DisplayAlert( "Dial a Number", "Would you like to call " + translatedNumber + "?", "Yes", "No")) { try { if (PhoneDialer.Default.IsSupported) PhoneDialer.Default.Open(translatedNumber); } catch (ArgumentNullException) { await DisplayAlert("Unable to dial", "Phone number was not valid.", "OK"); } catch (Exception) { // Other error has occurred. await DisplayAlert("Unable to dial", "Phone dialing failed.", "OK"); } } }
Microsoft.Maui.ApplicationModel.Communication 命名空间中的 PhoneDialer 类为 Windows、Android、iOS(和 iPadOS)和 macOS 平台提供了抽象化电话拨号功能(和其他功能)。 静态 Open 方法尝试使用电话拨号器来呼叫作为参数提供的号码。
以下步骤演示如何更新 Android 应用程序清单,以使 Android 能够使用电话拨号器。 Windows、iOS 和 MacCatalyst 应用程序遵循相同的一般原则,除非你在清单中指定了不同的操作系统依赖功能。
-
在“解决方案资源管理器”窗口中,展开“Platforms”文件夹,再展开“Android”文件夹,右键单击 AndroidManifest.xml 文件,然后选择“打开方式”>“自动编辑器选择器(XML)”。 选择“确定” 。
-
在“清单”节点中添加以下 XML 代码片段,位于该节点的现有内容之后。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> ... <queries> <intent> <action android:name="android.intent.action.DIAL" /> <data android:scheme="tel"/> </intent> </queries> </manifest>
-
保存文件。
-
在 Visual Studio 工具栏中,选择“Android Emulator/Pixel 3a - API 30”(或类似的)配置文件并开始调试。
-
当应用出现在模拟器中时(这可能需要几分钟时间),请输入电话号码(或接受默认值),选择“转换”,然后选择“呼叫”。
-
在“拨号”警报中,选择“是”。 验证 Android 电话拨号器是否显示了应用中提供的号码。
-
返回到 Visual Studio 并停止调试。
总结
在本练习中,你已使用页和视图向应用程序添加了自定义 UI。 你还使用 Android 中可用的平台特定的 API 添加了对拨打电话的支持。