第十七章:掌握网格(七)

简介:

响应方向变化
应用程序页面的布局通常与特定的外形和宽高比紧密相关。有时,应用程序将要求仅在纵向或横向模式下使用它。但是,当手机改变方向时,应用程序通常会尝试在屏幕上移动。
网格可以帮助应用程序适应方向更改。可以在XAML中定义网格,对纵向和横向模式都有一定的限制,然后一些代码可以在页面的SizeChanged处理程序中进行适当的调整。
如果您可以将应用程序的整个布局划分为两个大区域,当手机以纵向模式定向或水平定向为横向模式时,此作业最简单。将这些区域中的每一个放在网格的单独单元格中。当手机处于纵向模式时,网格有两行,当它处于横向模式时,它有两列。在下图中,第一个区域始终位于顶部或左侧。第二个区域可以是纵向模式的第二行,也可以是横向模式的第二列:
2018_10_07_151504
为了使事情变得相当简单,您需要在XAML中定义具有两行和两列的网格,但在纵向模式下,第二列的宽度为零,而在横向模式下,第二行的高度为零。
GridRgbSliders程序演示了这种技术。它类似于第15章“交互式界面”中的RgbSliders程序,除了布局使用Grid和StackLayout的组合,而Label元素通过使用带有值的数据绑定来显示Slider元素的当前值转换器和值转换器参数。 (稍后会详细介绍。)基于三个Slider元素设置BoxView的Color属性仍然需要代码,因为Color结构的R,G和B属性不受可绑定属性的支持,并且这些属性不能单独更改无论如何,因为他们没有公共集访问器。 (但是,在下一章中,在MVVM上,您将看到一种在代码隐藏文件中消除此逻辑的方法。)
正如您在下面的清单中所看到的,名为mainGrid的Grid确实有两行和两列。但是,它已初始化为纵向模式,因此第二列的宽度为零。 Grid的顶行包含BoxView,使用“*”(星号)设置尽可能大,而底行包含StackLayout和所有交互式控件。这是自动高度:

<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="GridRgbSliders.GridRgbSlidersPage"
             SizeChanged="OnPageSizeChanged">
 
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <ContentPage.Resources>
        <ResourceDictionary>
            <toolkit:DoubleToIntConverter x:Key="doubleToInt" />
            <Style TargetType="Label">
                <Setter Property="HorizontalTextAlignment" Value="Center" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    <Grid x:Name="mainGrid">
        <!-- Initialized for portrait mode. -->
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="0" />
        </Grid.ColumnDefinitions>
        <BoxView x:Name="boxView"
                 Grid.Row="0" Grid.Column="0" />
        <StackLayout x:Name="controlPanelStack"
                     Grid.Row="1" Grid.Column="0"
                     Padding="10, 5">
 
            <StackLayout VerticalOptions="CenterAndExpand">
                <Slider x:Name="redSlider"
                        ValueChanged="OnSliderValueChanged" />
                <Label Text="{Binding Source={x:Reference redSlider},
                                      Path=Value,
                                      Converter={StaticResource doubleToInt},
                                      ConverterParameter=255,
                                      StringFormat='Red = {0:X2}'}" />
            </StackLayout>
            <StackLayout VerticalOptions="CenterAndExpand">
                <Slider x:Name="greenSlider"
                        ValueChanged="OnSliderValueChanged" />
                <Label Text="{Binding Source={x:Reference greenSlider},
                                      Path=Value,
                                      Converter={StaticResource doubleToInt},
                                      ConverterParameter=255,
                                      StringFormat='Green = {0:X2}'}" />
            </StackLayout>
 
            <StackLayout VerticalOptions="CenterAndExpand">
                <Slider x:Name="blueSlider"
                        ValueChanged="OnSliderValueChanged" />
                <Label Text="{Binding Source={x:Reference blueSlider},
                                      Path=Value,
                                      Converter={StaticResource doubleToInt},
                                      ConverterParameter=255,
                                      StringFormat='Blue = {0:X2}'}" />
            </StackLayout>
        </StackLayout>
    </Grid>
</ContentPage>

这是纵向视图:
2018_10_07_152401
XAML文件中的布局以两种方式为横向模式准备。首先,Grid已经有了第二列。这意味着要切换到横向模式,代码隐藏文件需要将第二行的高度更改为零,将第二列的宽度更改为非零值。
其次,包含所有Slider和Label元素的StackLayout可以从代码访问,因为它有一个名称,特别是controlPanelStack。然后,代码隐藏文件可以对此StackLayout进行Grid.SetRow和Grid.SetColumn调用,以将其从第1行和第0列移动到第0行和第1列。
在纵向模式下,BoxView的高度为“”(星号),StackLayout的高度为“自动”。这是否意味着StackLayout的宽度在横向模式下应该是Auto?这不是明智之举,因为它会缩小Slider元素的宽度。横向模式的一个更好的解决方案是给BoxView和StackLayout宽度为“”(星号),将屏幕分成两半。
这是代码隐藏文件,显示负责在纵向和横向模式之间切换的页面上的SizeChanged处理程序,以及设置BoxView颜色的Slider元素的ValueChanged处理程序:

public partial class GridRgbSlidersPage : ContentPage
{
    public GridRgbSlidersPage()
    {
        // Ensure link to Toolkit library.
        new Xamarin.FormsBook.Toolkit.DoubleToIntConverter();
        InitializeComponent();
    }
    void OnPageSizeChanged(object sender, EventArgs args)
    {
        // Portrait mode.
        if (Width < Height)
        {
            mainGrid.RowDefinitions[1].Height = GridLength.Auto;
            mainGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Absolute);
            Grid.SetRow(controlPanelStack, 1);
            Grid.SetColumn(controlPanelStack, 0);
        }
        // Landscape mode.
        else
        {
            mainGrid.RowDefinitions[1].Height = new GridLength(0, GridUnitType.Absolute);
            mainGrid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
            Grid.SetRow(controlPanelStack, 0);
            Grid.SetColumn(controlPanelStack, 1);
        }
    }
    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        boxView.Color = new Color(redSlider.Value, greenSlider.Value, blueSlider.Value);
    }
}

这是横向展示的横向布局:
2018_10_07_153450
请注意,特别是在iOS和Android显示器上,每对Slider和Label元素如何组合在一起。这是第三种方式,即XAML文件准备好适应横向模式。每对Slider和Label元素都嵌套在一个嵌套的StackLayout中。这将给出CenterAndExpand的VerticalOptions设置以执行此间距。
稍微考虑安排BoxView和控制面板:在纵向模式下,操纵Slider元素的手指不会遮挡BoxView中的结果,而在横向模式下,惯用右手的用户的手指不会模糊BoxView也是。 (当然,左撇子用户可能会坚持使用程序选项来交换位置!)
屏幕截图显示了以十六进制显示的Slider值。这是通过数据绑定完成的,这通常是个问题。 Slider的Value属性是double类型,如果您尝试使用“X2”格式化十六进制的double,则会引发异常。类型转换器(例如,名为DoubleToIntConverter)必须将源double转换为int以进行字符串格式化。但是,Slider元素的设置范围为0到1,而格式为十六进制的整数值的范围必须介于0到255之间。
解决方案是使用Binding的ConverterParameter属性。设置为此属性的任何内容都作为第三个参数传递给值转换器中的Convert和ConvertBack方法。这是Xamarin.FormsBook.Toolkit库中的DoubleToIntConverter类:

namespace Xamarin.FormsBook.Toolkit
{
    public class DoubleToIntConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, 
 object parameter, CultureInfo culture)
        {
            string strParam = parameter as string;
            double multiplier = 1;
            if (!String.IsNullOrEmpty(strParam))
            {
                Double.TryParse(strParam, out multiplier);
            }
            return (int)Math.Round((double)value * multiplier);
        }
        public object ConvertBack(object value, Type targetType, 
 object parameter, CultureInfo culture)
        {
            string strParam = parameter as string;
            double divider = 1;
            if (!String.IsNullOrEmpty(strParam))
            {
                Double.TryParse(strParam, out divider);
            }
            return (int)value / divider;
        }
    }
} 

Convert和ConvertBack方法假定参数参数是一个字符串,如果是,则尝试将其转换为double。 然后将该值乘以转换的double值,然后将产品转换为int。
值转换器,转换器参数和字符串格式的组合将从Slider到0到1的值转换为0到255范围内的整数,然后将这些值格式化为两个十六进制数字:

<Label Text="{Binding Source={x:Reference redSlider},
                      Path=Value,
                      Converter={StaticResource doubleToInt},
                      ConverterParameter=255,
                      StringFormat='Red = {0:X2}'}" />

当然,如果您在代码中定义Binding,则可能将ConverterParameter属性设置为255的数值而不是字符串“255”,并且DoubleToIntConverter中的逻辑将失败。 简单的数值转换器通常比完全防弹更简单。
如果没有代码隐藏文件中的Slider事件处理程序,可以完全实现像GridRgbSliders这样的程序吗? 代码肯定仍然是必需的,但其中一些将被移离用户界面逻辑。 这是下一章探讨的Model-View-ViewModel架构的主要目标。

目录
相关文章
|
8月前
|
开发者
所有消除游戏背后都有一张看不见的网格
所有消除游戏背后都有一张看不见的网格
90 0
|
算法
网格算法和穷举法
网格算法和穷举法
168 0
技术分享 | ANSYS高级几何处理与网格应用技巧
Space Claim、排油烟机几何处理、流体域抽取;ANSYS Meshing、实际案例网格划分
技术分享 | ANSYS高级几何处理与网格应用技巧
|
存储 监控 架构师
【数据网格架构】什么是数据网格——以及如何不将其网格化
【数据网格架构】什么是数据网格——以及如何不将其网格化
【数据网格架构】什么是数据网格——以及如何不将其网格化
|
开发者
所有消除游戏背后那张看不见的网格
观察一下上方的这一系列各种各样的消除游戏的图片,它们都有着这样的一个共同点,就是都是按照行列进行布局,有 7 行 7 列,有 10 行 10 列的。这样的行列布局是不是特别的像一个“网格”?这就是我们今天要讲的,所有消除游戏背后都有的那张看不见的“网格”。
117 0
|
存储 SQL 架构师
数据网格简史
数据网格简史
167 0
数据网格简史
|
数据库管理
UCSF DOCK6.8对接教程之蛋白分子表面和受体空间准备
UCSF DOCK6.8对接教程之蛋白分子表面和受体空间准备
432 0
UCSF DOCK6.8对接教程之蛋白分子表面和受体空间准备
|
Android开发 iOS开发
|
JavaScript Android开发