热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WP7应用开发笔记(4)圆形滑动控件实现

控件功能分析圆形控件能识别顺时针、逆时针滑动的手势,并能识别滑动速度。系统提供的相关事件OnManipulationStarted滑动开始手按下OnManipulat

控件功能分析

圆形控件 能识别顺时针、逆时针滑动的手势,并能识别滑动速度。

 

系统提供的相关事件

OnManipulationStarted滑动开始手按下
OnManipulationDelta滑动中手按住并移动
OnManipulationCompleted滑动完成手放开

这3个事件是实现滑动的必要事件,因为EventArgs提供了手的XY坐标已经移动速度,

不过遗憾要识别顺时针需要自己实现。

 

识别顺时针滑动

识别了顺时针,反之就能识别逆时针,但如何识别顺时针滑动呢,

其实这个问题困扰了我不少时间,首先看看默认的坐标轴结构图:

用红色刷子表示手势,貌似没有找到突破口,光从X和Y的变化没有什么参考的地方。

 

必须要换一下思路,将坐标轴移动到圆心,方向不变(Y向下和数学的有点区别)

同样用红色刷子表示手势,光从X和Y的变化貌似也没有什么参考的地方,但是在不同区间却又一定的规律。

我们来看看顺时针滑动的X和Y的变化规律

区间1X 增大Y 减少
区间2X 增大Y 增大
区间3X 减少Y 减少
区间4X 减少Y 增大

恩,这样就基本可以识别方向了,但是写起来有点麻烦,有没有更好的方法呢。

这时我丢了N年的几何突然捡回了,我一直纠结X和Y,不是还有角度这个概念么,

一直坐冷板凳的Math出场了里面有个Atan函数可以求出反三角函数。

角度公式如下:var angle = Math.Atan(x / y) * 180.0 / Math.PI;

接下来通过区间就可以确定角度的方向,这些大问题解决了。突然感慨数学的重要。

当然还有一些细节问题可以参考代码的实现

 

控件代码实现

XAML:

View Code

<UserControl
xmlns&#61;"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
&#61;"http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d
&#61;"http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc
&#61;"http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local
&#61;"clr-namespace:VirtualKeyboard.Controls"
mc:Ignorable
&#61;"d"
x:Class
&#61;"VirtualKeyboard.Controls.LoopControl"
d:DesignWidth
&#61;"340" d:DesignHeight&#61;"340">

<Canvas>
<Ellipse x:Name&#61;"bigEllipse" Height&#61;"340" Width&#61;"340" Canvas.Left&#61;"0" Canvas.Top&#61;"0" StrokeThickness&#61;"4" Stroke&#61;"White" Fill&#61;"#FF7A7A7A" />
<local:RoundButton x:Name&#61;"centerButton" Height&#61;"150" Width&#61;"150" Canvas.Left&#61;"95" Canvas.Top&#61;"95" ImageSource&#61;"/VirtualKeyboard.Controls;component/Icons/word.ok.png" Background&#61;"#FF1D1D1D"/>
Canvas>
UserControl>

 为了方便中间加入了一个圆形的OK按钮&#xff0c;这个按钮的实现将在后面的篇幅中实现&#xff0c;如果有兴趣练习可以直接去掉local:RoundButton

C#

View Code

public partial class LoopControl
{
#region Const
const double BigRadius &#61; 200.0;
const double SmailRadius &#61; 70.0;
private readonly TimeSpan deltaSpan &#61; new TimeSpan(0, 0, 0, 0, 200);
#endregion

#region Private
private DateTime startTime;
private Angle startAngle;
#endregion

public event RoutedEventHandler CenterClick
{
add { centerButton.Click &#43;&#61; value; }
remove { centerButton.Click -&#61; value; }
}

public event EventHandler LoopGlide;

public void OnLoopGlide(LoopGlideEventArg e)
{
EventHandler handler &#61; LoopGlide;
if (handler !&#61; null) handler(this, e);
}

public LoopControl()
{
// 为初始化变量所必需
InitializeComponent();
}


protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
{
startTime &#61; DateTime.Now;
startAngle &#61; ToAngle(e.ManipulationOrigin);
base.OnManipulationStarted(e);
}

protected override void OnManipulationDelta(ManipulationDeltaEventArgs e)
{
if (IsInCenter(e.ManipulationOrigin)) return;

var now &#61; DateTime.Now;
if (now - startTime > deltaSpan)
{
GetDirection(e.ManipulationOrigin);
startTime &#61; now;
startAngle &#61; ToAngle(e.ManipulationOrigin);
}
base.OnManipulationDelta(e);
}

private void GetDirection(Point point)
{
var angle &#61; ToAngle(point);
var angleDistance &#61; startAngle - angle;
OnLoopGlide(new LoopGlideEventArg(angleDistance.Distance));
}

///


/// 求角度
///

///
///
private Angle ToAngle(Point point)
{
var xPoint &#61; ToXPoint(point);
var x &#61; xPoint.X;
var y &#61; xPoint.Y;
var angle &#61; Math.Atan(x / y) * 180.0 / Math.PI;
if (!(y <0.0))
{
angle &#43;&#61; 180.0;
}
else if (x > 0.0)
{
angle &#43;&#61; 360.0;
}
return new Angle(angle);
}

///
/// 变化坐标
///

///
///
private Point ToXPoint(Point point)
{
var x &#61; point.X - BigRadius;
var y &#61; point.Y - BigRadius;
return new Point(x, y);
}

private bool IsInCenter(Point point)
{
var xPoint &#61; ToXPoint(point);
var radius &#61; Math.Sqrt(xPoint.X * xPoint.X &#43; xPoint.Y * xPoint.Y);
return radius }

#region Private Struct

///
/// 角度
///

private struct Angle : IEquatable
{
private readonly double value;
private const double Epsilon &#61; 0.1;

public double Value
{
get { return value; }
}

public Angle(double value)
{
if (value > 360.0 || value <0)
{
throw new ArgumentOutOfRangeException("value", "value must between 0 and 360");
}
this.value &#61; value;
}

public override string ToString()
{
return string.Format("{0:N2}", value);
}

public static AngleDistance operator -(Angle angle1, Angle angle2)
{
double angleDistance &#61; angle1.value - angle2.value;
if (angleDistance > 180.0)
{
angleDistance -&#61; 360;
}
else if (angleDistance <-180.0)
{
angleDistance &#43;&#61; 360;
}
return new AngleDistance(angleDistance);
}

public override bool Equals(object obj)
{
if (obj is Angle)
{
return Equals((Angle)obj);
}
return false;
}

public override int GetHashCode()
{
return 360 ^ value.GetHashCode();
}

public bool Equals(Angle other)
{
return Math.Abs(value - other.value) }
}

///
/// 夹角
///

private struct AngleDistance
{
private readonly double angleDistance;

public AngleDistance(double angleDistance)
{
this.angleDistance &#61; angleDistance;
}

public double Distance
{
get { return angleDistance; }
}

public override string ToString()
{
return string.Format("{0:N2}", angleDistance);
}

///
/// 顺时针
///

public bool Clockwise
{
get { return angleDistance > 0; }
}
}
#endregion


}


 

转:https://www.cnblogs.com/kiminozo/archive/2012/01/25/2329373.html



推荐阅读
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 深入理解CSS中的margin属性及其应用场景
    本文主要介绍了CSS中的margin属性及其应用场景,包括垂直外边距合并、padding的使用时机、行内替换元素与费替换元素的区别、margin的基线、盒子的物理大小、显示大小、逻辑大小等知识点。通过深入理解这些概念,读者可以更好地掌握margin的用法和原理。同时,文中提供了一些相关的文档和规范供读者参考。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
author-avatar
泡沫茱_617
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有