zoukankan      html  css  js  c++  java
  • WP7应用开发笔记(4) 圆形滑动控件实现

    控件功能分析

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

    系统提供的相关事件

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

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

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

    识别顺时针滑动

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

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

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

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

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

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

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

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

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

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

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

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

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

    控件代码实现

    XAML:

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

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

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

    C#

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

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

    public event RoutedEventHandler CenterClick
    {
    add { centerButton.Click += value; }
    remove { centerButton.Click -= value; }
    }

    public event EventHandler<LoopGlideEventArg> LoopGlide;

    public void OnLoopGlide(LoopGlideEventArg e)
    {
    EventHandler<LoopGlideEventArg> handler = LoopGlide;
    if (handler != null) handler(this, e);
    }

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


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

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

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

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

    /// <summary>
    /// 求角度
    /// </summary>
    /// <param name="point"></param>
    /// <returns></returns>
    private Angle ToAngle(Point point)
    {
    var xPoint = ToXPoint(point);
    var x = xPoint.X;
    var y = xPoint.Y;
    var angle = Math.Atan(x / y) * 180.0 / Math.PI;
    if (!(y < 0.0))
    {
    angle += 180.0;
    }
    else if (x > 0.0)
    {
    angle += 360.0;
    }
    return new Angle(angle);
    }

    /// <summary>
    /// 变化坐标
    /// </summary>
    /// <param name="point"></param>
    /// <returns></returns>
    private Point ToXPoint(Point point)
    {
    var x = point.X - BigRadius;
    var y = point.Y - BigRadius;
    return new Point(x, y);
    }

    private bool IsInCenter(Point point)
    {
    var xPoint = ToXPoint(point);
    var radius = Math.Sqrt(xPoint.X * xPoint.X + xPoint.Y * xPoint.Y);
    return radius < SmailRadius;
    }

    #region Private Struct

    /// <summary>
    /// 角度
    /// </summary>
    private struct Angle : IEquatable<Angle>
    {
    private readonly double value;
    private const double Epsilon = 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 = value;
    }

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

    public static AngleDistance operator -(Angle angle1, Angle angle2)
    {
    double angleDistance = angle1.value - angle2.value;
    if (angleDistance > 180.0)
    {
    angleDistance -= 360;
    }
    else if (angleDistance < -180.0)
    {
    angleDistance += 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) < Epsilon;
    }
    }

    /// <summary>
    /// 夹角
    /// </summary>
    private struct AngleDistance
    {
    private readonly double angleDistance;

    public AngleDistance(double angleDistance)
    {
    this.angleDistance = angleDistance;
    }

    public double Distance
    {
    get { return angleDistance; }
    }

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

    /// <summary>
    /// 顺时针
    /// </summary>
    public bool Clockwise
    {
    get { return angleDistance > 0; }
    }
    }
    #endregion


    }


     

  • 相关阅读:
    牛客网 2018年东北农业大学春季校赛 L题 wyh的天鹅
    牛客网 2018年东北农业大学春季校赛 I题 wyh的物品
    记cccc天梯赛第三届决赛
    noi.openjudge 1.13.15
    Java连接Oracle数据库的三种连接方式
    SSH框架总结(框架分析+环境搭建+实例源码下载)
    深入浅出MyBatis:JDBC和MyBatis介绍
    每个 Java 开发者都应该知道的 5 个注解
    高效Web开发的10个jQuery代码片段
    编写优秀jQuery插件的10个技巧
  • 原文地址:https://www.cnblogs.com/kiminozo/p/2329373.html
Copyright © 2011-2022 走看看