第二十一章:变换(十一)

简介: 模拟时钟用于图形用户界面的经典示例程序之一是模拟时钟。 BoxView再一次为时钟之手进行救援。 必须根据当前时间的小时,分钟和秒旋转这些BoxView元素。让我们首先使用名为AnalogClockViewModel的类来处理旋转数学,该类包含在Xamarin.

模拟时钟
用于图形用户界面的经典示例程序之一是模拟时钟。 BoxView再一次为时钟之手进行救援。 必须根据当前时间的小时,分钟和秒旋转这些BoxView元素。
让我们首先使用名为AnalogClockViewModel的类来处理旋转数学,该类包含在Xamarin.FormsBook.Toolkit库中:

namespace Xamarin.FormsBook.Toolkit
{
    public class AnalogClockViewModel : ViewModelBase
    {
        double hourAngle, minuteAngle, secondAngle;
        public AnalogClockViewModel()
        {
            UpdateLoop();
        }
        async void UpdateLoop()
        {
            while (true)
            {
                DateTime dateTime = DateTime.Now;
                HourAngle = 30 * (dateTime.Hour % 12) + 0.5 * dateTime.Minute;
                MinuteAngle = 6 * dateTime.Minute + 0.1 * dateTime.Second;
                SecondAngle = 6 * dateTime.Second + 0.006 * dateTime.Millisecond;
                await Task.Delay(16);
            }
        }
        public double HourAngle
        {
            private set { SetProperty(ref hourAngle, value); }
            get { return hourAngle; }
        }
        public double MinuteAngle
        {
            private set { SetProperty(ref minuteAngle, value); }
            get { return minuteAngle; }
        }
        public double SecondAngle
        {
            private set { SetProperty(ref secondAngle, value); }
            get { return secondAngle; }
        }
    }
}

三个属性中的每一个在Task.Delay调用的循环中每秒更新60次。 当然,时针旋转角度不仅基于小时,而且基于DateTime值的Minute部分可用的小时部分。 类似地,分针的角度基于分钟和秒属性,秒针基于秒和毫秒属性。
ViewModel的这三个属性可以绑定到模拟时钟三个指针的Rotation属性。
如你所知,一些时钟具有平滑的滑动秒针,而其他时钟的秒针则以不连续的刻度移动。 AnalogClockViewModel类似乎强加了一个平滑的秒针,但如果你想要离散的刻度,你可以为此目的提供一个值转换器:

namespace Xamarin.FormsBook.Toolkit
{
    public class SecondTickConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
                              object parameter, CultureInfo culture)
        {
            return 6.0 * (int)((double)value / 6);
        }
        public object ConvertBack(object value, Type targetType, 
                                  object parameter, CultureInfo culture)
        {
            return (double)value;
        }
    }
}

如果你不知道它应该做什么,这个类的名称甚至是微小的代码都可能是模糊的:Convert方法转换double类型的角度,范围从0到360度,小数部分转换为离散角度值0 ,6,12,18,24等。 这些角度对应于秒针的离散位置。
MinimalBoxViewClock程序在其XAML文件中实例化三个BoxView元素,并将Rotation属性绑定到AnalogClockViewModel的三个属性:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="MinimalBoxViewClock.MinimalBoxViewClockPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
 
    <ContentPage.Resources>
        <ResourceDictionary>
            <toolkit:SecondTickConverter x:Key="secondTick" />
        </ResourceDictionary>
    </ContentPage.Resources>
    <AbsoluteLayout BackgroundColor="White"
                    SizeChanged="OnAbsoluteLayoutSizeChanged">
 
        <AbsoluteLayout.BindingContext>
            <toolkit:AnalogClockViewModel />
        </AbsoluteLayout.BindingContext>
 
        <BoxView x:Name="hourHand"
                 Color="Black"
                 Rotation="{Binding HourAngle}" />
 
        <BoxView x:Name="minuteHand"
                 Color="Black"
                 Rotation="{Binding MinuteAngle}" />
 
        <BoxView x:Name="secondHand"
                 Color="Black"
                 Rotation="{Binding SecondAngle, Converter={StaticResource secondTick}" />
    </AbsoluteLayout>
</ContentPage>

代码隐藏文件根据AbsoluteLayout的大小设置这些BoxView时钟指针的大小,并设置位置,以便所有指针在12:00位置从时钟中心指向:

public partial class MinimalBoxViewClockPage : ContentPage
{
    public MinimalBoxViewClockPage()
    {
        InitializeComponent();
    }
    void OnAbsoluteLayoutSizeChanged(object sender, EventArgs args)
    {
        AbsoluteLayout absoluteLayout = (AbsoluteLayout)sender;
        // Calculate a center and radius for the clock.
        Point center = new Point(absoluteLayout.Width / 2, absoluteLayout.Height / 2);
        double radius = Math.Min(absoluteLayout.Width, absoluteLayout.Height) / 2;
        // Position all hands pointing up from center.
        AbsoluteLayout.SetLayoutBounds(hourHand, 
            new Rectangle(center.X - radius * 0.05, 
                          center.Y - radius * 0.6, 
                          radius * 0.10, radius * 0.6));
        AbsoluteLayout.SetLayoutBounds(minuteHand,
            new Rectangle(center.X - radius * 0.025, 
                          center.Y - radius * 0.7, 
                          radius * 0.05, radius * 0.7));
        AbsoluteLayout.SetLayoutBounds(secondHand,
            new Rectangle(center.X - radius * 0.01, 
                          center.Y - radius * 0.9,
                          radius * 0.02, radius * 0.9));
        // Set the anchor to bottom center of BoxView.
        hourHand.AnchorY = 1;
        minuteHand.AnchorY = 1;
        secondHand.AnchorY = 1;
    }
}

例如,时针的长度为时钟半径的0.60,时钟半径的宽度为0.10。 这意味着时针左上角的水平位置必须设置为时钟中心左侧宽度的一半(半径的0.05倍)。 时针的垂直位置是指针在时钟中心上方的高度。 AnchorY的设置确保所有旋转都相对于每个时钟指针的中心底部:
2019_01_17_141658
当然,这个程序叫做MinimalBoxViewClock是有原因的。 它周围没有方便的刻度标记,因此有点难以辨别实际时间。 此外,钟针应该更恰当地重叠钟面的中心,使得它们至少看起来连接到旋转的销或管上。

目录
相关文章
|
5月前
|
算法 C语言
一文搞懂:一文教你快速搞懂速度曲线规划之S形曲线(超详细+图文+推导+附件代码)
一文搞懂:一文教你快速搞懂速度曲线规划之S形曲线(超详细+图文+推导+附件代码)
190 0
一文搞懂:一文教你快速搞懂速度曲线规划之S形曲线(超详细+图文+推导+附件代码)
算法入门小题目——点击消除
算法入门小题目——点击消除
|
Android开发
第二十一章:变换(十四)
3D-ish旋转 即使计算机屏幕是平面和二维的,也可以在这些屏幕上绘制视觉对象,使其具有第三维的外观。 在本章的前面,您看到了一些文本效果,它们提供了第三个维度的提示,而Xamarin.Forms支持两个额外的旋转,名为RotationX和RotationY,它们似乎也突破了屏幕固有的二维平面度。
1391 0
|
JavaScript Android开发 索引
第二十一章:变换(十二)
这两个问题都在非最小的BoxViewClock中得到解决。 XAML文件与MinimalBoxViewClock非常相似,但代码隐藏文件更为广泛。 它以名为HandParams的小结构开始,该结构定义每只手相对于半径的大小,但也包括偏移值。
1082 0
|
Android开发
第二十一章:变换(十三)
垂直滑块?某些观点是否可以轮换并仍然可以正常工作? 更具体地说,Xamarin.Forms的普通水平Slider元素可以旋转成垂直滑块吗?我们来试试吧。 VerticalSliders程序在StackLayout中包含三个滑块,StackLayout本身逆时针旋转90度: <ContentPage xmlns="http://xamarin.
815 0
|
Android开发 索引
第二十一章:变换(十)
样式通过将AnchorX值设置为0来结束,该值将旋转中心设置为每个Label的左边缘的垂直中心。 然后每个Label都会获得一个独特的旋转设置:显然,选择“ROTATE”字符串之前的空格,以便R的垂直条组合形成一个看起来几乎像圆的16边多边形。
1043 0
|
JavaScript Android开发 索引
第二十一章:变换(九)
旋转的文字效果轮换很有趣。 旋转动画时更有趣(正如您将在下一章中看到的那样),但即使使用静态图像也很有趣。本章和下一章中的几个旋转示例涉及将视觉元素排列在一个圆圈中,所以让我们首先尝试显示一个简单的圆圈。
877 0
|
Android开发
第二十一章:变换(八)
旋转变换 “旋转”属性旋转屏幕表面上的可视元素。 将“旋转”属性设置为以度为单位的角度(不是弧度)。 正角度顺时针旋转元素。 您可以将“旋转”设置为小于0或大于360的角度。实际旋转角度是旋转属性模数360的值。
840 0
|
Android开发 iOS开发
第二十一章:变换(七)
锚定规模当你尝试使用Scale属性时,你可能已经注意到视觉元素的任何扩展都是从元素的中心向外发生的,如果你将视觉元素缩小到任何东西,它也会向中心收缩。这是另一种思考方式:无论Scale属性的设置如何,视觉元素正中心的点都保持在同一位置。
966 0
|
Android开发 iOS开发 Windows
第二十一章:变换(六)
两个按钮的Clicked处理程序每个都启动一个独立的动画。 第一个Button的Clicked处理程序将其Scale属性从1设置为5并再次返回,而第二个Button的Clicked处理程序将其FontSize属性设置为1到5的比例因子,然后再返回。
965 0