65.9K
CodeProject 正在变化。 阅读更多。
Home

生活整理应用程序

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2012 年 10 月 1 日

CPOL

11分钟阅读

viewsIcon

10515

一个智能任务列表,与它所设计的超级本一样便携和紧凑。

引言

本应用程序是App创新竞赛的一个参赛作品。这是一个闭源项目,因此我不会发布完整的源代码。但是,我将解释其中的难点,并分享我不得不克服的一些障碍。本文末尾将提供有关应用程序开发进程和未来发展方向的更多信息。“生活管理器”应用程序充分利用了超级本的先进功能,包括使用GPS传感器、触摸屏和滑动手势。应用程序基本已经建成,主要问题也已解决。计划在下个月改进界面,进行传感器实地测试,并消除错误。计划于11月1日发布到商店。

生活管理器概述

我在使用并放弃了多个任务列表应用程序后开发了“生活管理器”。 每个应用程序都很好,但都缺少一些关键组件。 有些应用程序很适合处理临时任务。 有些则可以跟踪目标,但无法将这些目标与我的日常任务关联起来。 “生活管理器”是我尝试将所有这些概念整合到一个应用程序中的尝试。

该应用程序具有以下功能:

  • 易于使用的界面
  • 灵活的布局,适用于横向、纵向、贴靠和填充视图
  • 根据您的GPS位置筛选任务列表
  • 临时任务、循环任务和基于目标的任务
  • 智能任务,可以在特定(或未知)应用程序中创建 (例如,当您在路上时,提醒您为孩子购买纪念品 的任务)

超级本是为那些无法被束缚在办公桌上工作的忙碌专业人士设计的。“生活管理器”应用程序正是这一理念的延伸。您无需尝试将所有任务分开,而是可以将它们存储在同一个地方,同时通过物理位置将它们分开。当您在新的位置打开超级本时,该位置的任务将加载并准备就绪。

开发环境

我从头开始将此应用程序开发为原生的Windows 8 Metro应用程序。我的开发环境包括在Macbook Pro上的VMWare Fusion中运行的Windows 8 RC安装。由于我运行的是Windows 8 RC版本,因此我不得不运行Visual Studio 2012的RC版本,尽管我拥有RTM版本。这是因为RTM版本无法安装在RC版本上(如果尝试安装,它会抛出.NET框架错误)。我也没有我需要的传感器,这意味着我不得不使用模拟器来测试触摸、滑动和GPS。


这种环境的最终结果是,我遇到了许多错误、故障和陷阱,这些都减慢了开发过程。在讲解应用程序时,我将尝试指出这些问题以及我为解决它们所做的工作。希望Windows 8的RTM版本和Visual Studio 2012的RTM版本能修复这些问题,但我怀疑它能全部修复。

开发Windows 8 Metro应用程序

开发Metro应用程序时,您需要知道的第一件事是必须使用Windows 8构建它。Windows级别发生了一些更改,这些更改无法在较早的Windows环境中复制。这是一个可以理解的限制,但也很难处理,因为Windows 8的RTM版本尚未向公众发布。您可以通过MSDN和Technet获取它,但尚未上市销售。您还必须使用Visual Studio 2012作为您的IDE。当您为Windows Metro开发时,您有三个选择。您可以使用C++、Javascript/HTML5或XAML和.NET进行开发。我决定使用XAML和C#进行开发。

布局更改

响应应用程序大小和方向的变化是Metro应用程序的关键部分。应用程序需要规划四种主要状态。第一种状态是标准水平全屏视图。这是您的应用程序通常的运行方式。以下是全屏模式下生活管理器的截图

但是,有时您的应用程序可能会启动或旋转到横向视图。操作系统将通知您的应用程序分辨率已更改,但您需要确保您的应用程序在更改后的视图中看起来良好。微软推崇的模式是缩小标题和左边距,以更好地利用可用空间。这是我实现的旋转视图



第三和第四种状态称为贴靠和填充。这是Windows的新功能,允许一个窗口贴靠到屏幕的一侧,另一个窗口填充其余空间。贴靠视图仅占用320像素。大多数应用程序为此视图都有特定的外观,因为您只能显示有限的信息。对于我的应用程序,我选择只显示任务列表


当我的应用程序进入填充状态时,与全屏状态相比,变化不大。在我的应用程序中,右侧面板只是稍微调整以适应丢失的空间


所有这些不同的状态都可能在应用程序的生命周期中发生。为了确保您的应用程序充分利用这些变化,您需要监视状态更改事件。在我的应用程序中,我为每个视图更改移动了几个部分。这是我用来侦听大小更改事件的代码

Window.Current.SizeChanged += MainPage_SizeChanged;

获取对该事件的调用很棘手,因为自Windows 8开发的首个预览版以来,它的引用方式至少改变了三次。以下是我发现特别有助于我理清这一切的Stack Overflow问题:http://stackoverflow.com/questions/10362566/how-to-programmatically-respond-to-snap-in-windows-8-metro

一旦事件被连接起来,当它触发时您就需要做一些事情。以下是该事件的一个基本实现

private void MainPage_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
    switch (Windows.UI.ViewManagement.ApplicationView.Value)
    {
        case Windows.UI.ViewManagement.ApplicationViewState.Filled:
            widthSpacer.Visibility = Visibility.Collapsed;
            heightSpacer.Visibility = Visibility.Visible;
            break;
        case Windows.UI.ViewManagement.ApplicationViewState.FullScreenLandscape:
            widthSpacer.Visibility = Visibility.Visible;
            heightSpacer.Visibility = Visibility.Visible;
            break;
        case Windows.UI.ViewManagement.ApplicationViewState.FullScreenPortrait:
            widthSpacer.Visibility = Visibility.Collapsed;
            heightSpacer.Visibility = Visibility.Visible;
            break;
        case Windows.UI.ViewManagement.ApplicationViewState.Snapped:
            widthSpacer.Visibility = Visibility.Collapsed;
            heightSpacer.Visibility = Visibility.Collapsed;
            break;
        default:
            break;
    }
}

请注意,上面的代码只是一个简单的实现。根据状态,间隔元素或隐藏或可见。这可以改变应用程序边缘的边距大小。在我这段代码的第一个版本中,我是这样简单调整界面的。我的最终版本会比这更复杂一些,但基本思想是一样的。

动态磁贴和徽章

动态磁贴和徽章是Metro应用程序的另外两个关键部分。它们不仅宣传您的应用程序,还为您提供了一种便捷的方式来显示应用程序中数据的信息。在我的应用程序中,我选择通过徽章更新来显示当前有多少任务到期。以下是我正在运行的磁贴截图


这段代码特别棘手,因为您必须加载和操作XML。幸运的是,我在微软的网站上找到了一些很棒的示例,帮助我正确地编写了代码。这是我使用的最终代码

private void UpdateBadge(int itemCount)
{
    try
    {
        if (itemCount > 99)
        {
            itemCount = 99;
        }
        if (itemCount < 1)
        {
            //Turns off the badge entirely
            BadgeUpdateManager.CreateBadgeUpdaterForApplication().Clear();
            return;
        }
        var badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
        var badgeElement = (XmlElement)badgeXml.SelectSingleNode("/badge");
        badgeElement.SetAttribute("value", itemCount.ToString());
        var badge = new BadgeNotification(badgeXml);
        BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badge);
    }
    catch (Exception)
    {
        //Turns off the badge when an unexpected error occurs
        BadgeUpdateManager.CreateBadgeUpdaterForApplication().Clear();
    }
}

请注意,我只使用了数字徽章 (1-99),而不是图像更新,因此我不处理这些类型的数据。在我的例子中,我只做了两个简单的检查。首先,我检查以确保数字不高于99 (允许的最高数字)。如果高于99,我将其设置为最大值99。接下来,我检查数字是否低于1。如果是,我将其视为重置,并完全清除磁贴上的徽章。

GPS传感器

要启用GPS传感器的使用,您需要做的第一件事是在您的应用程序清单文件中声明您将使用位置功能。以下是您应该设置此值的位置截图


如果您没有声明您随后尝试使用的功能,当代码达到调用未经授权功能的地步时,您的应用程序将抛出异常。当您声明该功能时,用户仍需要批准使用该功能。以下是运行中的应用程序中的样子


获取GPS坐标并不困难。您需要注意的一件事是所有Metro应用程序的通用标准,那就是需要使用Async/Await关键字。任何调用可能超过50ms的情况,您都需要使用线程。这是微软为Metro应用程序制定的另一个标准,以确保它们保持高性能。这意味着对可能存在或不存在(或需要在使用前获得批准)的设备的调用都应该使用Async/Await关键字。

在我的应用程序中,我不仅想捕获GPS坐标,我还想确定用户是否与一组已知坐标位于同一位置。我通过使用距离公式来做到这一点,该公式以公里为单位输出最终结果。然后我选择将100米(0.1公里)内的任何地方都视为同一位置。我这样做是因为您的营业地点可能很大,而且GPS的精度不精确。我认为两个不同的位置不会相距100米。如果发现需要,这可以进行调整。这是GPS捕获代码

async public void LoadLocation()
{
    try
    {
        // Get cancellation token
        _cts = new CancellationTokenSource();
        CancellationToken token = _cts.Token;


        // Carry out the operation
        Geoposition pos = await _geolocator.GetGeopositionAsync().AsTask(token);


        _latitude = pos.Coordinate.Latitude;
        _longitude = pos.Coordinate.Longitude;
        _accuracy = pos.Coordinate.Accuracy;


        LocationUpdated(this, new EventArgs());
    }
    catch (System.UnauthorizedAccessException)
    {
        _latitude = 0;
        _longitude = 0;
        _accuracy = 0;
    }
    catch (TaskCanceledException)
    {
        _latitude = 0;
        _longitude = 0;
        _accuracy = 0;
    }
    catch (Exception)
    {
        _latitude = 0;
        _longitude = 0;
        _accuracy = 0;
    }
    finally
    {
        _cts = null;
    }
}

请注意,这里有三个捕获异常块。我这样做是为了演示目的,因为在我的例子中它们都做同样的事情。您可以根据情况使用这些块来做不同的事情。例如,如果抛出`UnauthorizedAccessException`,您可以让用户知道如果他们不允许使用GPS传感器,他们就无法获得其好处(或者类似讽刺的话)。

您可能还从上面的代码中注意到,我正在引发一个`LocationUpdated`事件。这是一个我为这个实现创建的自定义事件。我监听这个事件,当它被触发时,我读取新的坐标信息并根据这些数据采取行动。

在处理GPS数据时,我创建了几个扩展方法来帮助我简化工作,还有一个`struct`。这个`struct`简单地保存了一个位置的经度和纬度。这使得传递坐标数据变得更容易。这就是那个`struct`

public struct Position
{
    public double Latitude;
    public double Longitude;
}

我创建的两个扩展方法专门用于识别两点之间的距离。第一个方法,名为`ToRadians`,将一个双精度浮点数转换为其弧度等效值。这是位置公式的重要组成部分。接下来是实际的`GetDistance`方法。这个方法是Position结构体的扩展方法,它接受另一个位置值。它使用一个常见的位置距离公式(不是我自己想出来的)。以下是这两个扩展方法

public static double GetDistance(this Position orig, Position dest)
{
    double R = 6378.1370D;
    double dLat = dest.Latitude - orig.Latitude;
    double dLon = dest.Longitude - orig.Longitude;


    dLat = dLat.ToRadians();
    dLon = dLon.ToRadians();


    double a = Math.Sin(dLat / 2) *
        Math.Sin(dLat / 2) +
        Math.Cos(orig.Latitude.ToRadians()) *
        Math.Cos(dest.Latitude.ToRadians()) *
        Math.Sin(dLon / 2) *
        Math.Sin(dLon / 2);


    double c = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)));
    double d = R * c;
    return d;
}


public static double ToRadians(this double value)
{
    return (Math.PI / 180) * value;
}

总结

所以,这就是如何构建一个Metro应用程序的基本知识,以及我如何构建我的应用程序的一些具体细节。正如我在文章开头提到的,我的目标是让这个应用程序的第一个版本在11月1日在应用商店上线。当应用程序上线时,它将具有上面列出的所有功能。然而,我不会满足于此。我已经开始着手列出应该成为应用程序第一次重大更新的附加功能。这些功能包括

  • Azure数据库支持(目前此应用程序仅使用SQLite)用于基于云的数据存储。
  • 基于环境光的切换主题(亮色与暗色主题)。
  • 目标报告 - 一个显示特定目标进度的报告屏幕。
  • 成就 - 鼓励您在任务进度中获得徽章。这些将显示在成就页面上,并在发生时以Toast通知的形式出现。

我还有一些想法,在拿到超级本硬件之前我无法开始着手。这些功能将在生活管理器应用程序的第二次重大更新中实现。它们包括

  • Windows Phone 8应用程序,支持通过云和无线网络在超级本和手机之间进行同步。
  • 超级本之间或超级本与手机之间的近场通信(NFC),用于将任务交给他人(或接收任务)。

结论

我希望您已经看到了我的应用程序的价值,也希望您学到了一些关于如何开发Windows 8 Metro应用程序的知识。请在下面告诉我您的想法。我总是很感谢建设性的反馈。对于那些想知道我的应用程序外观的人,我计划在应用程序发布之前与我的一位设计师朋友合作。希望我们能一起努力,提升应用程序的视觉效果。

历史

  • 2012年10月1日 - 初始版本
© . All rights reserved.