创建电话号码转换器应用

发布时间 2023-09-25 10:13:07作者: 韩梦芫

在本练习中,你将为电话拨号器应用构造 UI,并实现此 UI 背后的逻辑。

你将构建一个 UI,此 UI 利用 .NET MAUI 和 .NET MAUI Essentials 包的 UI 功能拨打电话。

该应用使用户可在输入字段中键入文本,并将该文本转换为数字。 它将使用电话键盘上显示的字母作为转换的基础。 例如,字母“cab”会转换为“222”,因为数字“2”含有 a、b、c 三个字母。

你将继续使用在上一练习中创建的 Phoneword 解决方案。

向应用添加新的 C# 源文件

  1. 在 Visual Studio 中打开 Phoneword 的解决方案(如果你尚未打开它)。

  2. 在解决方案资源管理器窗口中,右键单击“Phoneword”项目,选择“添加”,然后选择“类”。

  3. 在“添加新项”对话框中,将类文件命名为 PhonewordTranslator.cs,然后选择“添加”。

    “添加新项”对话框的屏幕截图。用户已将类文件命名为 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;
    }
}

 

创建 UI
  1. 在“Phoneword”项目中打开 MainPage.xaml 文件。

  2. 删除 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>

     

  3. 在 ContentPage 中添加一个垂直方向的 VerticalStackLayout 控件,间距为 15 个单位,填充为 20 个单位:

    <ContentPage ... >
        <VerticalStackLayout Spacing="15" Padding="20">
    
        </VerticalStackLayout>
    </ContentPage>

     

  4. 将 Label 控件添加到 StackLayout:

    <ContentPage ... >
        <VerticalStackLayout ...>
            <Label Text = "Enter a Phoneword"
                   FontSize ="20"/>
        </VerticalStackLayout>
    </ContentPage>

     

  5. 将 Entry 控件添加到 StackLayout(标签下方)。 Entry 控件提供一个文本框,用户可以在其中输入数据。 在此代码中,属性 x:Name 赋予控件一个名称。 稍后你将在应用的代码中引用此控件:

    <ContentPage ... >
        <VerticalStackLayout ...>
            <Label .../>
            <Entry x:Name = "PhoneNumberText"
                   Text = "1-555-NETMAUI" />
        </VerticalStackLayout>
    </ContentPage>

     

  6. 添加 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 按钮点击

  1. 在解决方案资源管理器窗口中展开 MainPage.xaml 项并打开 MainPage.xaml.cs 代码隐藏文件。

  2. 在 MainPage 类中,删除 count 变量和 OnCounterClicked 方法。 此类应如下所示:

    namespace Phoneword;
    
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }

     

  3. 将 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 位。

  4. 在 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 按钮创建事件方法

  1. 将 OnCall 事件处理方法添加到 MainPage 类的末尾。 此方法将使用异步操作,因此请将其标记为 async

    public partial class MainPage : ContentPage
    {
    
        ...
        async void OnCall(object sender, System.EventArgs e)
        {
    
        }
    }

     

  2. 在 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
        }
    }

     

测试应用程序

  1. 在 Visual Studio 工具栏中,选择“Windows 计算机”配置文件并开始调试。

  2. 点击“转换”按钮,将默认文本转换为有效的电话号码。 “呼叫”按钮上的描述文字应更改为“呼叫 1-555-6386284”:

    Phoneword UI 的屏幕截图。用户已将文本转换为有效的电话号码。

  3. 点击“呼叫”按钮。 验证是否出现要求你确认操作的提示。 请选择“否”。

    屏幕截图显示 PhoneWord 用户界面中的“拨号”提示。

  4. 返回到 Visual Studio 并停止调试。

拨打电话号码

  1. 在 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 应用程序遵循相同的一般原则,除非你在清单中指定了不同的操作系统依赖功能。

  2. 在“解决方案资源管理器”窗口中,展开“Platforms”文件夹,再展开“Android”文件夹,右键单击 AndroidManifest.xml 文件,然后选择“打开方式”>“自动编辑器选择器(XML)”。 选择“确定” 。

  3. 在“清单”节点中添加以下 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>

     

  4. 保存文件。

  5. 在 Visual Studio 工具栏中,选择“Android Emulator/Pixel 3a - API 30”(或类似的)配置文件并开始调试。

  6. 当应用出现在模拟器中时(这可能需要几分钟时间),请输入电话号码(或接受默认值),选择“转换”,然后选择“呼叫”。

  7. 在“拨号”警报中,选择“是”。 验证 Android 电话拨号器是否显示了应用中提供的号码。

    包含应用提供的号码的 Android 电话拨号器。

  8. 返回到 Visual Studio 并停止调试。

总结

在本练习中,你已使用页和视图向应用程序添加了自定义 UI。 你还使用 Android 中可用的平台特定的 API 添加了对拨打电话的支持。