第二十六章:自定义布局(五)-阿里云开发者社区

开发者社区> wangccsy> 正文

第二十六章:自定义布局(五)

简介: 内视过程中本章到目前为止提供的大部分信息都是从包含派生自各种元素(如StackLayout,ScrollView和Label)的类的测试程序汇编而来,覆盖虚拟方法(如GetSizeRequest,OnSizeRequest,OnSizeAllocated和LayoutChildren) ,并使用System.Diagnostics命名空间中的Debug.WriteLine方法在Visual Studio或Xamarin Studio的“输出”窗口中显示信息。
+关注继续查看

内视过程中
本章到目前为止提供的大部分信息都是从包含派生自各种元素(如StackLayout,ScrollView和Label)的类的测试程序汇编而来,覆盖虚拟方法(如GetSizeRequest,OnSizeRequest,OnSizeAllocated和LayoutChildren) ,并使用System.Diagnostics命名空间中的Debug.WriteLine方法在Visual Studio或Xamarin Studio的“输出”窗口中显示信息。
探索过程中的一小部分 - 但使用手机本身显示此信息 - 显示在ExploreChildSizes示例中。
ExploreChildSizes使用MasterDetailPage在Master页面上显示一组单选按钮,在Detail部分上显示可视树。单选按钮使用第25章“页面变体”中提供的RadioButtonManager和RadioButtonItem类。这是带有单选按钮的主页面,用于在详细信息页面上为子视图选择Horizo​​ntalOptions和VerticalOptions属性:

<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                  xmlns:local="clr-namespace:ExploreChildSizes;assembly=ExploreChildSizes"
                  xmlns:toolkit=
                     "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
                  x:Class="ExploreChildSizes.ExploreChildSizesPage">
    <MasterDetailPage.Master>
        <ContentPage Title="swap">
            <ContentPage.Icon>
                <OnPlatform x:TypeArguments="FileImageSource"
                            WinPhone="Images/refresh.png" />
            </ContentPage.Icon>

            <ContentPage.Padding>
                <OnPlatform x:TypeArguments="Thickness"
                            iOS="0, 20, 0, 0" />
            </ContentPage.Padding>
            <ScrollView>
                <StackLayout Padding="20"
                             Spacing="20">
                    <StackLayout>
                        <StackLayout.BindingContext>
                            <toolkit:RadioButtonManager x:Name="vertRadios"
                                                        x:TypeArguments="LayoutOptions" />
                        </StackLayout.BindingContext>
                        <StackLayout HorizontalOptions="Start">
                            <Label Text="Child VerticalOptions"
                                   FontSize="Medium" />
                            <BoxView Color="Accent"
                                     HeightRequest="3" />
                        </StackLayout>
                        <local:RadioButton BindingContext="{Binding Items[0]}" />
                        <local:RadioButton BindingContext="{Binding Items[1]}" />
                        <local:RadioButton BindingContext="{Binding Items[2]}" />
                        <local:RadioButton BindingContext="{Binding Items[3]}" />
                        <local:RadioButton BindingContext="{Binding Items[4]}" />
                        <local:RadioButton BindingContext="{Binding Items[5]}" />
                        <local:RadioButton BindingContext="{Binding Items[6]}" />
                        <local:RadioButton BindingContext="{Binding Items[7]}" />
                    </StackLayout>
                    <StackLayout>
                        <StackLayout.BindingContext>
                            <toolkit:RadioButtonManager x:Name="horzRadios"
                                                        x:TypeArguments="LayoutOptions" />
                        </StackLayout.BindingContext>
                        <StackLayout HorizontalOptions="Start">
                            <Label Text="Child HorizontalOptions"
                                   FontSize="Medium" />
                            <BoxView Color="Accent"
                                     HeightRequest="3" />
                        </StackLayout>
                        <local:RadioButton BindingContext="{Binding Items[0]}" />
                        <local:RadioButton BindingContext="{Binding Items[1]}" />
                        <local:RadioButton BindingContext="{Binding Items[2]}" />
                        <local:RadioButton BindingContext="{Binding Items[3]}" />
                        <local:RadioButton BindingContext="{Binding Items[4]}" />
                        <local:RadioButton BindingContext="{Binding Items[5]}" />
                        <local:RadioButton BindingContext="{Binding Items[6]}" />
                        <local:RadioButton BindingContext="{Binding Items[7]}" />
                    </StackLayout>
                </StackLayout>
            </ScrollView>
        </ContentPage>
    </MasterDetailPage.Master>
    __
</MasterDetailPage>

这个页面在Xamarin.FormsBook.Toolkit库中使用了一个名为RadioButtonManager的类,您可以在闲暇时阅读它。 它允许成为与所选按钮关联的项目的绑定源。 RadioButton类使用Accent颜色和Bold属性来指示所选项:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ExploreChildSizes.RadioButton">
    <Label Text="{Binding Name}"
           FontSize="Medium">
        <Label.GestureRecognizers>
            <TapGestureRecognizer Command="{Binding Command}"
                                  CommandParameter="{Binding Value}"/>
        </Label.GestureRecognizers>
        <Label.Triggers>
            <DataTrigger TargetType="Label"
                         Binding="{Binding IsSelected}"
                         Value="True">
                <Setter Property="TextColor" Value="Accent" />
                <Setter Property="FontAttributes" Value="Bold" />
            </DataTrigger>
        </Label.Triggers>
    </Label>
</ContentView>

这是三个平台上的Master页面。 在所有三个屏幕的右侧,您可以看到一个详细信息页面的切片,其中包含StackLayout的黄色背景:
2019_05_15_095023
详细信息页面(如下所示)被网格划分为两行相等的高度。 顶行是一个简单的可视树,由StackLayout和Label以及BoxView组成。 但是,此可视树中的类实际上是从StackLayout,Label和BoxView派生的,并且称为OpenStackLayout,OpenLabel和OpenBoxView。 请注意,OpenLabel和OpenBoxView的VerticalOptions和HorizontalOptions属性绑定到Master页面上的两个RadioButtonManager对象:

<MasterDetailPage __ >
    __
    <MasterDetailPage.Detail>
        <ContentPage> 
            <ContentPage.Padding>
                <OnPlatform x:TypeArguments="Thickness"
                            iOS="0, 20, 0, 0" />
            </ContentPage.Padding>
            <Grid>
                <local:OpenStackLayout x:Name="openStackLayout"
                                       Grid.Row="0"
                                       BackgroundColor="Yellow"
                                       Padding="15">
                    <local:OpenLabel
                        x:Name="openLabel"
                        Text="This is a label with text sufficiently long enough to wrap"
                        FontSize="Large"
                        BackgroundColor="Gray"
                        VerticalOptions="{Binding Source={x:Reference vertRadios},
                                                  Path=SelectedValue}"
                        HorizontalOptions="{Binding Source={x:Reference horzRadios},
                                                    Path=SelectedValue}" />
                    <local:OpenBoxView
                        x:Name="openBoxView"
                        Color="Pink"
                        VerticalOptions="{Binding Source={x:Reference vertRadios},
                                                  Path=SelectedValue}"
                        HorizontalOptions="{Binding Source={x:Reference horzRadios},
                                                    Path=SelectedValue}" />
                </local:OpenStackLayout>
                __
            </Grid>
        </ContentPage>
    </MasterDetailPage.Detail>
</MasterDetailPage>

此上下文中的Open前缀表示这些类定义公共属性,这些属性显示GetSizeRequest调用的参数和返回值,以及(在OpenStackLayout的情况下)LayoutChildren的参数。 所有这些属性都由只读可绑定属性支持,以便它们可以作为数据绑定的源。 此外,Bounds属性镜像在名为ElementBounds的属性中,也由只读可绑定属性支持:
这是OpenLabel类。 另外两个是相似的:

class OpenLabel : Label
{
    static readonly BindablePropertyKey ConstraintKey =
        BindableProperty.CreateReadOnly(
            "Constraint",
            typeof(Size),
            typeof(OpenLabel),
            new Size());
    public static readonly BindableProperty ConstraintProperty =
        ConstraintKey.BindableProperty;
    static readonly BindablePropertyKey SizeRequestKey =
        BindableProperty.CreateReadOnly(
        "SizeRequest",
        typeof(SizeRequest),
        typeof(OpenLabel),
        new SizeRequest());
    public static readonly BindableProperty SizeRequestProperty =
        SizeRequestKey.BindableProperty;
    static readonly BindablePropertyKey ElementBoundsKey =
        BindableProperty.CreateReadOnly(
            "ElementBounds",
            typeof(Rectangle),
            typeof(OpenLabel),
            new Rectangle());
    public static readonly BindableProperty ElementBoundsProperty =
        ElementBoundsKey.BindableProperty;
    public OpenLabel()
    {
        SizeChanged += (sender, args) =>
        {
            ElementBounds = Bounds;
        };
    }
    public Size Constraint
    {
        private set { SetValue(ConstraintKey, value); }
        get { return (Size)GetValue(ConstraintProperty); }
    }
    public SizeRequest SizeRequest
    {
        private set { SetValue(SizeRequestKey, value); }
        get { return (SizeRequest)GetValue(SizeRequestProperty); }
    }
    public Rectangle ElementBounds
    {
        private set { SetValue(ElementBoundsKey, value); }
        get { return (Rectangle)GetValue(ElementBoundsProperty); }
    }
    public override SizeRequest GetSizeRequest(double widthConstraint, double heightConstraint)
    {
        Constraint = new Size(widthConstraint, heightConstraint);
        SizeRequest sizeRequest = base.GetSizeRequest(widthConstraint, heightConstraint);
        SizeRequest = sizeRequest;
        return sizeRequest;
    }
}

详细信息页面上网格的下半部分包含一个可滚动的StackLayout,其中包含数据绑定以显示这些属性:

<MasterDetailPage __ >
    __
    <MasterDetailPage.Detail>
        <ContentPage>
            <ContentPage.Padding>
                <OnPlatform x:TypeArguments="Thickness"
                            iOS="0, 20, 0, 0" />
            </ContentPage.Padding>
            <Grid>
                __
                <ScrollView Grid.Row="1"
                            Padding="10, 0">
                    <StackLayout>
                        <StackLayout.Resources>
                            <ResourceDictionary>
                                <Style TargetType="Label">
                                    <Setter Property="FontSize" Value="Small" />
                                </Style>
                            </ResourceDictionary>
                        </StackLayout.Resources>
                        <StackLayout
                            BindingContext="{Binding Source={x:Reference openStackLayout}">
                            <Label Text="StackLayout:"
                                   FontAttributes="Bold" />
                            <Label Text="{Binding Path=Constraint,
                                                  StringFormat='Constraint = {0}'}" />
                            <Label Text="{Binding Path=SizeRequest.Request,
                                                  StringFormat='Request = {0}'}" />
                            <Label Text="{Binding Path=SizeRequest.Minimum,
                                                  StringFormat='Minimum = {0}'}" />

                            <Label Text="{Binding Path=ElementBounds,
                                                  StringFormat='Bounds = {0}'}" />
                            <Label Text="{Binding Path=LayoutBounds,
                                                  StringFormat='Layout = {0}'}" />
                        </StackLayout>
                        <StackLayout BindingContext="{Binding Source={x:Reference openLabel}">
                            <Label Text="Label:"
                                   FontAttributes="Bold" />
                            <Label Text="{Binding Path=Constraint,
                                                  StringFormat='Constraint = {0}'}" />
                            <Label Text="{Binding Path=SizeRequest.Request,

                                                  StringFormat='Request = {0}'}" />
                            <Label Text="{Binding Path=SizeRequest.Minimum,
                                                  StringFormat='Minimum = {0}'}" />
                            <Label Text="{Binding Path=ElementBounds,
                                                  StringFormat='Bounds = {0}'}" />
                        </StackLayout>
                        <StackLayout BindingContext="{Binding Source={x:Reference openBoxView}">
                            <Label Text="BoxView:"
                                   FontAttributes="Bold" />
                            <Label Text="{Binding Path=Constraint,
                                                  StringFormat='Constraint = {0}'}" />
                            <Label Text="{Binding Path=SizeRequest.Request,
                                                  StringFormat='Request = {0}'}" />
                            <Label Text="{Binding Path=SizeRequest.Minimum,
                                                  StringFormat='Minimum = {0}'}" />
                            <Label Text="{Binding Path=ElementBounds,
                                                  StringFormat='Bounds = {0}'}" />
                        </StackLayout>
                    </StackLayout>
                </ScrollView>
            </Grid>
        </ContentPage>
    </MasterDetailPage.Detail>
</MasterDetailPage>

然后,您可以在Label和BoxView上设置VerticalOptions和HorizontalOptions的各种组合,并查看它们如何影响参数并从GetSizeRequest方法返回值以及Layout方法的参数(这些都反映在Bounds属性中):
2019_05_15_100104
Label和BoxView上的VerticalOptions设置无效,除非Expands标志为true。 Horizo​​ntalOptions设置将项目定位在左侧,中间或右侧。
您可能会注意到一些奇怪之处:首先,OpenStackLayout不会调用其GetSizeRequest方法。这就是为什么屏幕下半部分的前三项全部为零。此GetSizeRequest调用通常来自Grid,它是其父级。但是,Grid的大小基于屏幕的大小,Grid包含两行大小相等的行。 OpenStackLayout将其VerticalOptions和Horizo​​ntalOptions属性设置为LayoutOptions.Fill,因此它的大小将基于Grid而不是其内容。
如果您想进一步调查此行为,则需要在“详细信息”页面上的标记中更改OpenStackLayout的VerticalOptions或Horizo​​ntalOptions属性。在这种情况下,Grid将调用OpenStackLayout和OpenStackLayout的GetSizeRequest方法,然后对Label和BoxView进行GetSizeRequest调用,因为它需要知道OpenStackLayout大小来定位它。
OpenLabel和OpenBoxView都使用Double.PositiveInfinity的高度约束来调用其GetSizeRequest方法,但Label显示了平台之间的一些不一致。
在各种Windows平台上,从显示的值中可以看出,Label的约束宽度不等于StackLayout的布局宽度。但是进一步的探索揭示了GetSizeRequest方法不止一次被调用 - 第一次使用布局宽度,然后使用所请求的Label宽度。
Android Label将宽度约束作为其请求的宽度返回,这意味着Label上的HorizontalOptions设置对其水平位置没有影响。 当文本只占一行时,Android实现中的这种差异就会消失。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
计算机基础2 | 学习笔记
快速学习计算机基础2。
11 0
Vue 仿钉钉流程图(流程节点绘制 vue+Ant【如果用其他UI库需要替换几个组件】 附 demo)
# [这里是git地址](https://gitee.com/xiaoyaoluntian/imitating-dingding-flow-chart/tree/comdemo/)
6 0
Vue 集成腾讯地图基础api Demo集合(基础地图引入与展示模块,地址逆解析,3D/2D切换 ,位置服务,mark标记)
Vue 集成腾讯地图基础api Demo集合(基础地图引入与展示模块,地址逆解析,3D/2D切换 ,位置服务,mark标记)
7 0
计算机基础3 | 学习笔记
快速学习计算机基础3。
10 0
数据类型-数值和字符串 | 学习笔记
快速学习数据类型-数值和字符串。
5 0
作用域及作用域链 | 学习笔记
快速学习作用域及作用域链。
5 0
代码注释 | 学习笔记
快速学习代码注释。
9 0
求数组元素的和 | 学习笔记
快速学习求数组元素的和。
6 0
函数的返回值 | 学习笔记
快速学习函数的返回值。
6 0
函数当作参数2 | 学习笔记
快速学习函数当作参数2。
6 0
+关注
wangccsy
前一个帐号wangccsy@126.com不知道怎么的就成了企业帐号,改不成个人。所以重新注册了一个个人帐号。老程序员。精通JAVA,C#,数据库,对软件开发过程和流程熟悉。考取系统分析师,项目管理师和系统架构设计师等软件资格考试认证。愿意和大家一起前进。
984
文章
391
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载