免责声明:本文章由fengyun1989创作,采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
到这里,我们已经拥有一个不错的程序,可以数据的增删改,可以看到当前的课表内容等。接下来,我们要实现Snapped模式,用磁贴来实现数据的显示,还有徽章(badge)的运用。
什么是Snapped模式呢,就是程序的分屏显示,Windows 8 Snap 要求至少 1366 * 768 的显示分辨率才能使用,其实程序本身就能够Snapped,只是显示效果不尽然如我们所愿。我们的目标就是实现按照我们的意愿的snapped模式。
那么怎么在模拟器启动snapped模式呢,使用手指模式,然后再模拟器屏幕上方,向中间划,就能看到程序变成缩略图,然后就向左或向右滑动就能进入snapped模式了。如下图:
如果不能看到,那么请修改模拟器的分辨率,点击右侧工具栏的小电脑按钮,选择1366 * 768以上的分辨率。
看上图的显示效果,看起来不太友好,虽然基本信息也出来了。那么我们可以用代码捕捉到程序的改变,然后进行布局调整。
在MainPage的构造函数里面注册这么一个事件:
Window.Current.SizeChanged += Current_SizeChanged;
这个事件可以在程序显示大小改变的时候触发。另外,给MainPage的Grid设置一个Name的属性:
<Grid x:Name="GridLayout" Background="{StaticResource AppBackgroundColor}">
然后修改Current_SizeChanged方法如下:
void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e) { var currentViewState = Windows.UI.ViewManagement.ApplicationView.Value; if (currentViewState == ApplicationViewState.Snapped) { GridLayout.ColumnDefinitions[0].Width = new GridLength(e.Size.Width * 0.6); WeekdayListTitle.FontSize = 24; ItemDetailTitle.FontSize = 24; } else { GridLayout.ColumnDefinitions[0].Width = new GridLength(e.Size.Width / 2); WeekdayListTitle.FontSize = 56; ItemDetailTitle.FontSize = 56; } }
上面改变的时候就改变视图。Windows.UI.ViewManagement.ApplicationView有一个TryUnsnap方法,可以使snapped模式恢复为正常的全屏视图。
现在,我们的视图变成好看多了吧。当然,可以在XAML里面定义好VisualStateManager,在后台代码稍加控制state,也能达到效果。
这里提供微软的例子地址:http://code.msdn.microsoft.com/windowsapps/Snap-Sample-2dc21ee3
磁贴和徽章(Badge)
静态磁贴需要的是三种规模的图片。30*30,150*150,310*150三种像素的图片。如果大家没有这种大小的图片,请到这里下载:http://dl.dbank.com/c0ak8gdsuc 我把图片都放在了Assets文件夹,现在打开Package.appxmanifest,修改如下:
这样,如上图所示修改,编译运行就能看到结果了。在模拟器怎么实现宽磁贴变为小磁贴呢,按紧磁贴,稍微向下拖动,就能看到会有Appbar弹出,就能改变磁贴了。
在Package.appxmanifest里面有很多关于程序的设置,大家可以看下。
动态磁贴
磁贴的动态更新都要基于模板,模板都是一些微软定义好的枚举类型。详请看http://msdn.microsoft.com/en-us/library/windows/apps/hh761491.aspx。
模板系统是基于XML片段,比如下面这段模板:
<tile> <visual lang="en-US"> <binding template="TileSquareText03"> <text id="1">Text Field 1</text> <text id="2">Text Field 2</text> <text id="3">Text Field 3</text> <text id="4">Text Field 4</text> </binding> </visual> </tile>
只有4行,我们就利用填充Xml的方式并传递给系统来更新磁贴。为了从所以数据中获取我们需要更新到磁贴的数据,我写了这么些方法添加到MainPage。算法简陋,勿见怪。
ScheduleItem GetNowSchedule() { DateTime date = DateTime.Now; string weekday = date.DayOfWeek.ToString(); string localWeekday = GetLocalWeekday(weekday); var weekdayList = viewModel.WeekdayList; int index = 0; for (int i = 0; i < weekdayList.Count; i++) { if (weekdayList[i].Weekday == weekday || weekdayList[i].Weekday == localWeekday) { index = i; break; } } List<DateTime> scheduleStartTime = new List<DateTime>(); for (int i = 0; i < weekdayList[index].ScheduleList.Count; i++) { ScheduleItem item = weekdayList[index].ScheduleList[i]; string[] times = item.StartTime.Split(':'); int hour = Convert.ToInt32(times[0]); int minute = Convert.ToInt32(times[1]); DateTime tempDate = new DateTime(date.Year, date.Month, date.Day, hour, minute, 0); scheduleStartTime.Add(tempDate); } TimeSpan MinDate; int scheduleIndex = 0; for (int i = 0; i < scheduleStartTime.Count; i++) { if ((scheduleStartTime[i] - date).Hours >= 0) { if (scheduleIndex == 0) { MinDate = scheduleStartTime[i] - date; scheduleIndex = i; } else { if (MinDate > (scheduleStartTime[i] - date)) { scheduleIndex = i; MinDate = scheduleStartTime[i] - date; } } } } return weekdayList[index].ScheduleList[scheduleIndex]; } string GetLocalWeekday(string weekday) { string returnValue = String.Empty; ; switch (weekday) { case "Moonday": returnValue = "星期一"; break; case "Tuesday": returnValue = "星期二"; break; case "Wednesday": returnValue = "星期三"; break; case "Thursday": returnValue = "星期四"; break; case "Friday": returnValue = "星期五"; break; case "Saturday": returnValue = "星期六"; break; case "Sunday": returnValue = "星期日"; break; default: break; } return returnValue; }
上面做的是取出下一个即将需要上的课。判断方法有点麻烦,算法不太好。主要是重复遍历。先获取当天在weekdayList的index。然后再通过时间来判断哪个是最接近当前时间的。算法有点问题,如果过了当天上课的时间,不能显示下一天的第一节课的时间。这个作为示例程序就这样吧。大家可以通过改变系统的时间来达到测试的目的。
既然能够获取数据了,下面进行磁贴更新,添加这么一个方法到MainPage:
void UpdateTile() { ScheduleItem showScheduleItem = GetNowSchedule(); XmlDocument tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareText03); XmlNodeList textNodes = tileXml.GetElementsByTagName("text"); textNodes[0].InnerText = showScheduleItem.LessonName; textNodes[1].InnerText = showScheduleItem.ClassRoom; textNodes[2].InnerText = showScheduleItem.StartTime + "-" + showScheduleItem.EndTime; for (int i = 0; i < 5; i++) { TileUpdateManager.CreateTileUpdaterForApplication().Update(new TileNotification(tileXml)); } }
上面的代码,先获取模板,然后对模板进行填充,最后一个for循环通知系统更新磁贴,循环5次是为了保证磁贴更新能够正确处理。所以5次操作。
现在在构造函数添加这么一行:
UpdateTile();
那么,这次要在本地计算机上调试,鉴于数据的重复性,我设置时间到了星期一的中午,因为星期一的三个课程都不一样。
可能很难立即获得更新后瓷砖。你可以从以下获得帮助:
我这次就是清理工程后重新生成才行。不过,一旦获取更新,以后就不会再次消失。
但是现在宽磁贴的更新好没有做。现在在磁贴上右键--放大,就会发现什么数据都没有。
更新宽磁贴
要更新两个瓷砖尺寸(方的和宽的),你需要将两个XML模板来创建一个单一的片段,其中包含两个更新。宽模板相对于普通的模板有几个额外的字段,
<tile> <visual lang="en-US"> <binding template="TileSquareText03"> <text id="1">Apples</text> <text id="2">Hotdogs</text> <text id="3">Soda</text> <text id="4"></text> </binding> <binding template="TileWideBlockAndText01"> <text id="1">Apples (Whole Foods)</text> <text id="2">Hotdogs (Costco)</text> <text id="3">Soda (Costco)</text> <text id="4"></text> <text id="5">2</text> <text id="6">Stores</text> </binding> </visual> </tile>
没有组合模版的API。我采用的方法是使用XML处理来支持分开填充模板,然后在过程的最后将它们组合,注释掉原来的UpdateTile方法,然后添加新的UpdateTile方法
void UpdateTile() { ScheduleItem showScheduleItem = GetNowSchedule(); XmlDocument narrowTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareText03); XmlDocument wideTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideBlockAndText01); XmlNodeList narrowTextNodes = narrowTileXml.GetElementsByTagName("text"); XmlNodeList wideTextNodes = wideTileXml.GetElementsByTagName("text"); narrowTextNodes[0].InnerText = showScheduleItem.LessonName; narrowTextNodes[1].InnerText = showScheduleItem.ClassRoom; narrowTextNodes[2].InnerText = showScheduleItem.StartTime + "-" + showScheduleItem.EndTime; wideTextNodes[0].InnerText = "科目:" + showScheduleItem.LessonName; wideTextNodes[1].InnerText = "教室:" + showScheduleItem.ClassRoom; wideTextNodes[2].InnerText = "时间:" + showScheduleItem.StartTime + "-" + showScheduleItem.EndTime; var wideBingElement = wideTileXml.GetElementsByTagName("binding")[0]; var importedNode = narrowTileXml.ImportNode(wideBingElement, true); narrowTileXml.GetElementsByTagName("visual")[0].AppendChild(importedNode); for (int i = 0; i < 5; i++) { TileUpdateManager.CreateTileUpdaterForApplication().Update(new TileNotification(narrowTileXml)); } }
ImportNode的方法创建一个我的宽绑定元素的一个新的拷贝在我的方的文档的环境中。ImportNode方法的参数是我想要导入的元素和一个bool值,代表是否我想要导入子节点。然后使用AppendChild元素将它插入到narrowTileXml里面,从而能构成和上面Xml模板一样的数据类型。只要小心组合,什么样的模板都能巧妙组合到一块。
最后,我们同样的5次通知更新。现在,宽磁贴也能获取到更新了。
关于磁贴动态更新就到这里了。
推荐两篇关于磁贴好博文:http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/04/20/10295998.aspx
http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/04/24/10297319.aspx
更新徽章(Badge)
什么是Badge呢。看下图:
这里红色方块处就是徽章。徽章可以是数字(1-99之间)或者是图片(图片好像是微软定义好的)。图片定义:http://msdn.microsoft.com/en-us/library/windows/apps/hh761458.aspx
下面我们就来创建这么一个方法添加到MainPage。
private void UpdateBadge() { BadgeTemplateType templateType = BadgeTemplateType.BadgeGlyph; XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(templateType); ((XmlElement)badgeXml.GetElementsByTagName("badge")[0]).SetAttribute("value", "attention"); for (int i = 0; i < 5; i++) { BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(new BadgeNotification(badgeXml)); } }
更新徽章也是需要模版的,微软就提供了两个模版,一个是BadgeGlyph,一个是BadgeNumber,和更新Tile一样,获取模版的内容,然后设置value这个属性,值为attention时是显示一个叹号,其他图片值请看:http://msdn.microsoft.com/en-us/library/windows/apps/hh761458.aspx。如果是BadgeNumber模板,那么”attention“就换为"3",就显示3这个数字了。同样的,我们也重复5次通知更新。
现在,在MainPage的构造函数的最后添加:
UpdateBadge();
编译运行就能看到叹号了。
本节就到这里了,继续学习:<win8>(五)实例讲解win8(XAML+C#)开发--------课程表:Appbar,FilePicker,启动页面(动画)
微软官方示例:App tiles and badges sample
本次工程下载:http://dl.dbank.com/c0pnham1f1