今天我们在WP上来实现一个“闪光灯”,WP7上是不能控制手机自带的硬件闪光灯的,但是我们可以通过将手机屏幕设置成全白色来模拟闪光灯的效果。在这个练习里我们将实现若干种不同颜色的闪光灯,以及SOS莫斯代码闪光灯和频闪闪光灯(开关切换非常快)。
1 用户界面设计
我们预备采用ApplicationBar来放置三个图标:分别是普通闪光灯、SOS摩斯码闪光灯和频闪闪光灯,用来控制闪光灯模式;在菜单中选择闪光颜色。
1.1 ApplicationBar的图标
在应用程序项目中添加一个文件夹Images用来存放将要添加的图标文件,打开源程序中被注释起来的那段ApplicationBar的代码,准备添加图标文件。右击Images文件夹选择Add-Existing Items,在我的电脑上,自带图标存放路径是:C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Icons\dark,选择合适的图标加入,并将其生成属性改为Content。
修改ApplicationBar的代码:
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" Opacity="0.5">
<shell:ApplicationBarIconButton IconUri="/Images/appbar.basecircle.rest.png" Text="Solid" Click="SolidButton_Click"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar.overflowdots.png" Text="SOS" Click="SosButton_Click"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.camera.rest.png" Text="Strobe" Click="StrobeButton_Click"/>
1.2 ApplicationBar的菜单
修改ApplicationBar的菜单项,使用不同的颜色:
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="red"/>
<shell:ApplicationBarMenuItem Text="orange"/>
<shell:ApplicationBarMenuItem Text="yellow"/>
<shell:ApplicationBarMenuItem Text="green"/>
<shell:ApplicationBarMenuItem Text="cyan"/>
<shell:ApplicationBarMenuItem Text="purple"/>
<shell:ApplicationBarMenuItem Text="gray"/>
<shell:ApplicationBarMenuItem Text="white"/>
</shell:ApplicationBar.MenuItems>
不在每个菜单内部定义各自的Click事件是每个菜单所做的工作基本相同,所以考虑将所有的事件处理方法放到同一个方法中,通过单击的菜单项的文本信息来转化成相应的颜色,当然这里也可以指定Click方法相同,但是我们决定在代码中实现。
1.3 主界面
因为这个应用程序并不需要额外的界面,所以将整个界面简化成只有一个Grid即可:
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="White">
</Grid>
到这里的效果是这样的:
注意到最上边沿的状态栏仍然影响整体的感觉,我们可以把状态栏禁用掉,同时把朝向更改一下:
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
现在的效果好多了:
2 业务代码实现
2.1 响应菜单事件
为每个菜单的Click事件绑定相同的方法,通过检查菜单内容转化成相应的颜色,并改变当前闪光颜色:
2.1.1 在初始化方法中加入代码为每个菜单绑定事件:
public MainPage()
{
InitializeComponent();
foreach(ApplicationBarMenuItem menuItem in this.ApplicationBar.MenuItems)
menuItem.Click += MenuItem_Click;
}
并同时引入命名空间:
using Microsoft.Phone.Shell;
准备写事件处理方法:
void MenuItem_Click(object sender, EventArgs e)
{
}
2.1.2 根据点击的菜单的内容获取颜色:
string chosenColor = (sender as IApplicationBarMenuItem).Text;
Color c = (Color)typeof(Colors).GetProperty(chosenColor, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase).GetValue(null, null);
消息处理方法的第一个参数sender是一个object类型,表示触发该事件方法的对象,因此可以转化成任何由object派生的类型,IApplicationBarMenuItem是菜单项接口类型,菜单项类实现了这个接口,这个接口有属性Text表示菜单项的内容,使用一个string对象接收文本;下一步就是由文本获取颜色了,这里使用的是反射的方法,反射就是 运行时检查和处理程序元素 的功能。typeof(Colors)得到的是一个Type对象,应用Type.GetProperty(string, Bindingflags)方法使用指定的绑定约束搜索指定属性,返回获取到的符合属性约束的值,将该值转化为Color类对象即得到实际的颜色值。
BindingFlags.Public:表示可以在搜索中包含公共属性
BindingFlags.Static:表示期望得到返回值,或使用BindingFlags.Instance
BindingFlags.IgnoreCase:表示忽略搜索文本的大小写,使得可以由"red"搜索到内置的颜色类型"Red"。
2.1.3 设置颜色
设置颜色需要系统维护一个画刷(SolidColorBrush),每次获取新的颜色时,给这个画刷赋值并填充背景。
private SolidColorBrush onBrush;
设置颜色:
this.onBrush = new SolidColorBrush(c);
this.LayoutRoot.Background = onBrush;
效果如下:
2.2 相应图标按钮事件
首先,我们需要提供一个标识当前工作状态的枚举类型:
enum FlashMode
{
Solid,
Sos,
Strobe
}
FlashMode mode = FlashMode.Solid;
2.2.1 处理事件处理方法
private void SolidButton_Click(object sender, EventArgs e)
{
if (mode == FlashMode.Solid)
return;
else
{
//Solid Handle
}
}
private void SosButton_Click(object sender, EventArgs e)
{
if (mode == FlashMode.Sos)
return;
else
{
//SOS Handle
}
}
private void StrobeButton_Click(object sender, EventArgs e)
{
if (mode == FlashMode.Strobe)
return;
else
{
//Strobe Handle
}
}
我们的想法是,点击Solid图标时,恢复正常模式,静态颜色背景;点击SOS图标时,按。。。—— —— —— 。。。的方式闪光;点击Strobe图标时,快速闪光。下面分别实现:
2.2.2 SOS模式
this.mode = FlashMode.Sos;
this.sosStep = 0;
this.sosTimer.Start();
开启一个定时器,周期性地输出闪光。
int sosStep;
DispatcherTimer sosTimer = new DispatcherTimer();
初始化代码中:
this.sosStep = 0;
this.sosTimer.Interval = TimeSpan.Zero;
this.sosTimer.Tick += sosTimer_Tick;
定时器代码:
void sosTimer_Tick(object sender, EventArgs e)
{
switch (this.sosStep)
{
case 1:
case 3:
case 5:
case 13:
case 15:
case 17:
this.LayoutRoot.Background = this.onBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(0.3);
break;
case 7:
case 9:
case 11:
this.LayoutRoot.Background = this.onBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(1.0);
break;
case 18:
this.LayoutRoot.Background = this.onBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(1.5);
break;
default:
this.LayoutRoot.Background = this.offBrush;
this.sosTimer.Interval = TimeSpan.FromSeconds(.3);
break;
}
this.sosStep = (this.sosStep + 1) % 19;
}
2.2.2 Strobe模式
同样地,需要使用一个定时器来控制闪光,具体代码不再详细给出
2.2.3 Solid模式
关闭所有定时器,模式设置为Solid。
2.2.4 别的问题
(1)目前还存在的问题是初始化的时候无法记住上一次的选择,这需要第一天练习里的独立存储设置来实现;
(2)动态更换图标的问题,在点击图标后,需要动态地改变显示的图标,可以这样实现:
(sender as IApplicationBarIconButton).IconUri = new Uri("Images/other.png", UriKind.Relative);
(3)使用反射是一个比较耗时的操作。
3 总结
本练习中学习的主要知识点有:
(1)ApplicationBar的操作,动态处理内容的方法(重新指定Uri的值)。
(2)DispatcherTimer使用定时器的方法,相关联的有Interval变量,Tick事件和Start、Stop方法,以及TimeSpan的用法。
(3)SolidColorBrush的用法,Color与Colors的区别(后者是一个预定义颜色的集合),
(4)处理事件相应方法中的sender参数的方法以及简单的反射方法
(5)没有在例子中出现的,MessageBox的用法:
MessageBox.Show("Oh Lod!", "Warning!", MessageBoxButton.OKCancel);
效果是这样的:
如果我们需要改写按钮上的文字或者重写按钮事件,可以这样实现:
引入命名空间:
using Microsoft.Xna.Framework.GamerServices;
这里需要在项目引用中添加Microsoft.Xna.Framework.GamerServices才能识别
然后利用这两个函数实现自定义操作:
调用消息框出现:
Guide.BeginShowMessageBox("Warning!", "Oh My Lod!", new string[] { "Left", "Right" }, 0, MessageBoxIcon.Warning, new AsyncCallback(OnMessageBoxClosed), null);
关闭消息框回调方法:
void OnMessageBoxClosed(IAsyncResult result)
{
int? buttonIndex = Guide.EndShowMessageBox(result);
if (buttonIndex == 1) //确定
//处理确定事件
return;
else if (buttonIndex == 2) //取消
//处理取消事件
return;
else //按了硬件的返回键
return;
}