随着微软发布 Surface Studio 在演示视频中非常抢眼的一个配件就是 Surface Dial,Dial 是Windows输入设备大家庭中的新成员我们把它归类为Windows Wheel 类型设备。今天为大家介绍一下如何配合这个神奇设备开发自己的应用。

Dial 是一个类似于滚轮的设备,可以协助用户更快的想计算机输入信息,但是Dial并不是一个专为精准输入所设计的设备,换句话说如果用户想点击屏幕上某一个点我们还是推荐用户使用手指触摸或者Surface Pan 或者鼠标完成这项工作,Dial更适合类似于画布的旋转,笔触的选择等快速的操作。

Surface Dial操作非常简单,只有三个操作事件 长按单击,以及 转动并且如果Surface Dial 与 Surface Studio 一起使用的话 Surface Dial 的菜单可以使用 Surface Dial的屏幕菜单,就是将 Dial 放在 Surface Studio 屏幕上随后通过对Surface Dial的操作,围绕Dial的屏幕周围会出现一个菜单。

Dial 自身与windows衔接紧密并且内置系统工具例如调整系统音量,大小缩小,以及撤销从做等功能。此外Dial 与 Windows Ink集成也是非常紧密。如果您的WUP应用已经使用了 InkCanvas 和 InkToolbar 那么 Dial就会自动和 InkToolbar中的内容相结合例如 调整标尺的角度和调整笔触的大小等功能。

然而对于我们开发者而言 Surface 也是为我们提供了API(这里分别有UWP 和 Win32版本,本文着重介绍UWP版本),其实开发起来也是非常简单。

首先关键点

RadialController 类,通过CreateForCurrentView()静态方法获取实例。

RadialControllerMenuItem 类,用来自定义菜单内容。当Dial长按时会弹出自定义菜单。

ButtonClicked 事件,用来捕捉Dial的点击事件。

RotationChanged 事件,用来捕捉Dial旋转事件。

ScreenContactStarted 事件,捕捉 Dial 放在了 Surface Studio 上事件,并且可以从回调参数中获取Dial在屏幕中摆放的位置。

ScreenContactContinued 事件,捕捉 Dial 放在了 Surface Studio 上并且移动了位置,并且可以从回调参数中获取Dial在屏幕中摆放的位置。

ScreenContactEnded 事件,捕捉 Dial 从 Surface Studio 上移开事件。

ControlLost 事件,捕捉操作的焦点离开。

照搬一个MSDN上的demo code比较直观的展示这几个事件的用法以及如何判断Dial在Surface Studio上的位置

Xaml代码

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel"
Orientation="Horizontal"
Grid.Row="0">
<TextBlock x:Name="Header"
Text="RadialController customization sample"
VerticalAlignment="Center"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
</StackPanel>
<Grid Grid.Row="1" x:Name="RootGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid x:Name="Grid0"
Grid.Row="0"
Grid.Column="0">
<StackPanel Orientation="Vertical"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<!-- Slider for rotational input -->
<Slider x:Name="RotationSlider0"
Width="300"
HorizontalAlignment="Left"/>
<!-- Switch for button input -->
<ToggleSwitch x:Name="ButtonToggle0"
HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
<Grid x:Name="Grid1"
Grid.Row="0"
Grid.Column="1">
<StackPanel Orientation="Vertical"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<!-- Slider for rotational input -->
<Slider x:Name="RotationSlider1"
Width="300"
HorizontalAlignment="Left"/>
<!-- Switch for button input -->
<ToggleSwitch x:Name="ButtonToggle1"
HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
<Grid x:Name="Grid2"
Grid.Row="1"
Grid.Column="0">
<StackPanel Orientation="Vertical"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<!-- Slider for rotational input -->
<Slider x:Name="RotationSlider2"
Width="300"
HorizontalAlignment="Left"/>
<!-- Switch for button input -->
<ToggleSwitch x:Name="ButtonToggle2"
HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
<Grid x:Name="Grid3"
Grid.Row="1"
Grid.Column="1">
<StackPanel Orientation="Vertical"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<!-- Slider for rotational input -->
<Slider x:Name="RotationSlider3"
Width="300"
HorizontalAlignment="Left"/>
<!-- Switch for button input -->
<ToggleSwitch x:Name="ButtonToggle3"
HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
</Grid>
</Grid>

code-behind 代码

Slider ActiveSlider;
ToggleSwitch ActiveSwitch;
Grid ActiveGrid; public MainPage()
{
... myController.ScreenContactStarted +=
MyController_ScreenContactStarted;
myController.ScreenContactContinued +=
MyController_ScreenContactContinued;
myController.ScreenContactEnded +=
MyController_ScreenContactEnded;
myController.ControlLost += MyController_ControlLost; //Set initial grid for Surface Dial input.
ActiveGrid = Grid0;
ActiveSlider = RotationSlider0;
ActiveSwitch = ButtonToggle0;
} private void MyController_ScreenContactStarted(RadialController sender,
RadialControllerScreenContactStartedEventArgs args)
{
//find grid at contact location, update visuals, selection
ActivateGridAtLocation(args.Contact.Position);
} private void MyController_ScreenContactContinued(RadialController sender,
RadialControllerScreenContactContinuedEventArgs args)
{
//if a new grid is under contact location, update visuals, selection
if (!VisualTreeHelper.FindElementsInHostCoordinates(
args.Contact.Position, RootGrid).Contains(ActiveGrid))
{
ActiveGrid.Background = new
SolidColorBrush(Windows.UI.Colors.White);
ActivateGridAtLocation(args.Contact.Position);
}
} private void MyController_ScreenContactEnded(RadialController sender, object args)
{
//return grid color to normal when contact leaves screen
ActiveGrid.Background = new
SolidColorBrush(Windows.UI.Colors.White);
} private void MyController_ControlLost(RadialController sender, object args)
{
//return grid color to normal when focus lost
ActiveGrid.Background = new
SolidColorBrush(Windows.UI.Colors.White);
} private void ActivateGridAtLocation(Point Location)
{
var elementsAtContactLocation =
VisualTreeHelper.FindElementsInHostCoordinates(Location,
RootGrid); foreach (UIElement element in elementsAtContactLocation)
{
if (element as Grid == Grid0)
{
ActiveSlider = RotationSlider0;
ActiveSwitch = ButtonToggle0;
ActiveGrid = Grid0;
ActiveGrid.Background = new SolidColorBrush(
Windows.UI.Colors.LightGoldenrodYellow);
return;
}
else if (element as Grid == Grid1)
{
ActiveSlider = RotationSlider1;
ActiveSwitch = ButtonToggle1;
ActiveGrid = Grid1;
ActiveGrid.Background = new SolidColorBrush(
Windows.UI.Colors.LightGoldenrodYellow);
return;
}
else if (element as Grid == Grid2)
{
ActiveSlider = RotationSlider2;
ActiveSwitch = ButtonToggle2;
ActiveGrid = Grid2;
ActiveGrid.Background = new SolidColorBrush(
Windows.UI.Colors.LightGoldenrodYellow);
return;
}
else if (element as Grid == Grid3)
{
ActiveSlider = RotationSlider3;
ActiveSwitch = ButtonToggle3;
ActiveGrid = Grid3;
ActiveGrid.Background = new SolidColorBrush(
Windows.UI.Colors.LightGoldenrodYellow);
return;
}
}
}

当然还有一个关键点如何创建自定义的菜单 使用Controller.Menu.Items.Add()方法添加和使用 Remove()方法删除自定义菜单。

注意:这里Surface Dial 菜单可以容纳7个选项,如果超过7个那么Dial 需要有浮动控件配合选择,这样做会影响用户体验是不推荐的。

        private void CreateMenuItems()
{
menuItems = new List<RadialControllerMenuItem>
{
RadialControllerMenuItem.CreateFromKnownIcon("Item0", RadialControllerMenuKnownIcon.InkColor),
RadialControllerMenuItem.CreateFromKnownIcon("Item1", RadialControllerMenuKnownIcon.NextPreviousTrack),
RadialControllerMenuItem.CreateFromKnownIcon("Item2", RadialControllerMenuKnownIcon.Volume),
RadialControllerMenuItem.CreateFromIcon("Item3", RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item3.png"))),
RadialControllerMenuItem.CreateFromIcon("Item4", RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item4.png"))),
RadialControllerMenuItem.CreateFromIcon("Item5", RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item5.png")))
};
sliders = new List<Slider> { Slider0, Slider1, Slider2, Slider3, Slider4, Slider5 };
toggles = new List<ToggleSwitch> { Toggle0, Toggle1, Toggle2, Toggle3, Toggle4, Toggle5 }; for (int i = ; i < menuItems.Count; ++i)
{
RadialControllerMenuItem radialControllerItem = menuItems[i];
int index = i; radialControllerItem.Invoked += (sender, args) => { OnItemInvoked(index); };
}
} private void OnItemInvoked(int selectedItemIndex)
{
activeItemIndex = selectedItemIndex;
} private void AddItem(object sender, RoutedEventArgs e)
{
RadialControllerMenuItem radialControllerMenuItem = GetRadialControllerMenuItemFromSender(sender); if (!Controller.Menu.Items.Contains(radialControllerMenuItem))
{
Controller.Menu.Items.Add(radialControllerMenuItem);
}
} private void RemoveItem(object sender, RoutedEventArgs e)
{
RadialControllerMenuItem radialControllerMenuItem = GetRadialControllerMenuItemFromSender(sender); if (Controller.Menu.Items.Contains(radialControllerMenuItem))
{
Controller.Menu.Items.Remove(radialControllerMenuItem);
}
} private void SelectItem(object sender, RoutedEventArgs e)
{
RadialControllerMenuItem radialControllerMenuItem = GetRadialControllerMenuItemFromSender(sender); if (Controller.Menu.Items.Contains(radialControllerMenuItem))
{
Controller.Menu.SelectMenuItem(radialControllerMenuItem);
PrintSelectedItem();
}
}

菜单选项推荐使用64x64像素的PNG透明图像即可,但是同时支持到44x44的最小像素值。

注意:黑色边框是为了在高对比度模式下也可以让我们的图标也可以清晰可见。

最后附上微软的示例代码:RadialControlle

希望上的总结可以帮助到大家, 同时欢迎大家在这里和我沟通交流或者在新浪微博上 @王博_Nick

Surface Dial 与 Windows Wheel UWP应用开发的更多相关文章

  1. 面向初学者的 Windows 10 UWP 应用开发

    眼看 Windows 10 for Mobile 正式版也快要推送了,就先挖个坑吧,原文视频链接为:Windows 10 development for absolute beginners,以下博客 ...

  2. UWP 手绘视频创作工具技术分享系列 - Ink & Surface Dial

    本篇作为技术分享系列的第四篇,详细讲一下手绘视频中 Surface Pen 和 Surface Dial 的使用场景. 先放一张微软官方商城的图,Surface 的使用中结合了 Surface Pen ...

  3. UwpDesktop!WPF也能开发Surface Dial

    原文:UwpDesktop!WPF也能开发Surface Dial 前段时间巨硬发布了一款新的输入设备Surface Dial,配合Surface Studio使用简直炫酷到没朋友. 本人由于公司业务 ...

  4. UWP应用开发系列视频教程简介 - Built for Windows 10

    万分感谢Fdyo同学给我们带来的有中文字幕的系列教程! http://zhuanlan.zhihu.com/MSFaith/20364660 下面是这系列video教程中的一个截图作为示例,有代码,有 ...

  5. Windows 10 UWP开发:如何去掉ListView默认的选中效果

    原文:Windows 10 UWP开发:如何去掉ListView默认的选中效果 开发UWP的时候,很多人会碰到一个问题,就是ListView在被数据绑定之后经常有个默认选中的效果,就像这样: 而且它不 ...

  6. 如何为Surface Dial设备开发自定义交互功能

    随着Surface Studio的发布,微软发布了与之相配套的外设硬件Surface Dial,用户可以将Surface Dail吸附在Surface Studio的屏幕上面,用旋转和点击的实体操作来 ...

  7. Mobilize.Net Silverlight bridge to Windows 10 UWP

    Windows UWP 既 Windows 10 Universal Windows platform,这个微软基于Windows NT内核的个运行时(Runtime)平台,此平台横跨所有的 Wind ...

  8. 自动启动 Windows 10 UWP 应用

    原文: https://docs.microsoft.com/zh-cn/windows/uwp/xbox-apps/automate-launching-uwp-apps 简介 开发人员有多种选项可 ...

  9. 操作系统-Windows:UWP(Universal Windows Platform)

    ylbtech-操作系统-Windows:UWP(Universal Windows Platform) 1.返回顶部 1. UWP即Windows 10中的Universal Windows Pla ...

随机推荐

  1. iOS8学习笔记-构建多视图应用程序

    严格来说,凡是UIView的子类都可以称为视图,实际上IOS的图形界面就是视图包含视图的方案,绝大多数的控件都是UIView的子类,但是苹果公司定义的视图术语,通常是指具有相应视图控制器的uiview ...

  2. display属性及inline-block值(可用来布局)

    display:block就是将元素显示为块级元素. block元素的特点是: 总是在新行上开始: 高度,行高以及顶和底边距都可控制: 宽度缺省是它的容器的100%,除非设定一个宽度 <div& ...

  3. Yii -- framework 目录结构说明

    base 底层的类库文件 caching 所有缓存方法 cli 项目生成脚本 collecions 用PHP语言构造传统OO语言的数据存储单元.如队列,栈,哈希等等 console yii控制台 db ...

  4. displayport

    去年买了一张ATI的显卡,但是很坑爹的是显卡后面没有VGA接口,除了一个HDMI,另外两个接口不认识,后来认识了DVI,用DVI连接显示器,剩下一个接口是什么一直没管,前两天电脑清灰,感兴趣了,去查了 ...

  5. linux学习小记:如何查看linux服务器的cpu数量,内核数,和cpu线程数

    查看物理cpu个数 grep 'physical id' /proc/cpuinfo | sort -u | wc -l 查看每个cpu核心数量 grep 'core id' /proc/cpuinf ...

  6. javah编译class文件找不到android.app.Activity的类文件

    在android工程的根目录使用javah生成jni 头文件时候,报找不到android.app.Activity的类文件错误. 无法访问android.app.Activity是说明没有引入andr ...

  7. MySQL5.6-Tomcat7环境变量的配置

    一.MySQL环境变量配置(zip安装):系统-高级系统设置--环境变量--path添加D:\Mysql\bin 找到mysql解压目录下的my-default.ini文件修改 basedir = D ...

  8. iOS 选择框 单选框

    UIButton *checkbox = [UIButton buttonWithType:UIButtonTypeCustom]; CGRect checkboxRect = CGRectMake( ...

  9. JAVA控制台输入输出方法总结

    java的控制台输入输出有很多方法,此文分别对其进行介绍. 1.控制台的输入 关于控制台的输入主要介绍三种方法,第一种方法使用BufferedReader获得控制台输入的数据,此方法是传统的输入方法, ...

  10. 2.8. 创建 NSManagedObject 的子类 (Core Data 应用程序实践指南)

    现在根据模型来创建NSManagedObject的子类.如果模型改变了,那就就重新生成这些文件.所以,不要在生成的文件里自定义方法,因为重新生成之后,这些修改就丢失了.假如确实需要重新生成自定义的方法 ...