简体   繁体   English

Xamarin.Forms 动态布局取决于屏幕方向或大小

[英]Xamarin.Forms dynamic layout depending on screen orientation or size

Did Xamarin.Forms already contain a control/layout which orders it's content depending on the screen orientation or size? Xamarin.Forms 是否已经包含一个控件/布局,它根据屏幕方向或大小对其内容进行排序?

What I want: Two stacklayouts which are ordered horizontal, if the screen got enough space.我想要的:如果屏幕有足够的空间,两个堆栈布局是水平排列的。 When the Screen changes, so that the screen got not enough horizontal-space, the two stacklayouts should be ordered vertical.当屏幕发生变化时,屏幕没有足够的水平空间,两个堆栈布局应该垂直排列。

I don't want to do it in code behind.我不想在后面的代码中这样做。

I search for an solution which only uses the xaml.我搜索仅使用 xaml 的解决方案。

I guess you can't achieve this using ONLY XAML.我猜你不能只使用 XAML 来实现这一点。 Certainly, you will need some c# code.当然,您将需要一些 c# 代码。 The XAML on Xamarin.Forms is designed to be responsive, and you often define the view properties in a relative mode (instead of absolute). Xamarin.Forms 上的 XAML 设计为响应式的,您通常以相对模式(而不是绝对模式)定义视图属性。

You can see an example of the behavior you want at this topic where we can see a screen changing the orientation of the StackLayout according to the device orientation (you can use it as your guideline to write your own layout component)您可以在本主题中看到您想要的行为示例,我们可以看到屏幕根据设备方向更改 StackLayout 的方向(您可以将其用作编写自己的布局组件的指南)

The screen on portrait mode:纵向模式下的屏幕: 纵向模式下的屏幕

The screen on landscape mode:横向模式下的屏幕: 横向模式下的屏幕

That is accomplished with the following XAML:这是通过以下 XAML 完成的:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.StackLayoutPageXaml"
Title="Stack Photo Editor - XAML">
    <ContentPage.Content>
        <StackLayout Spacing="10" Padding="5" Orientation="Vertical"
        x:Name="outerStack"> <!-- can change orientation to make responsive -->
            <ScrollView>
                <StackLayout Spacing="5" HorizontalOptions="FillAndExpand"
                    WidthRequest="1000">
                    <StackLayout Orientation="Horizontal">
                        <Label Text="Name: " WidthRequest="75"
                            HorizontalOptions="Start" />
                        <Entry Text="deer.jpg"
                            HorizontalOptions="FillAndExpand" />
                    </StackLayout>
                    <StackLayout Orientation="Horizontal">
                        <Label Text="Date: " WidthRequest="75"
                            HorizontalOptions="Start" />
                        <Entry Text="07/05/2015"
                            HorizontalOptions="FillAndExpand" />
                    </StackLayout>
                    <StackLayout Orientation="Horizontal">
                        <Label Text="Tags:" WidthRequest="75"
                            HorizontalOptions="Start" />
                        <Entry Text="deer, tiger"
                            HorizontalOptions="FillAndExpand" />
                    </StackLayout>
                    <StackLayout Orientation="Horizontal">
                        <Button Text="Save" HorizontalOptions="FillAndExpand" />
                    </StackLayout>
                </StackLayout>
            </ScrollView>
            <Image  Source="deer.jpg" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Some C# is used to change the orientation of outerStack based on the orientation of the device:一些 C# 用于根据设备的方向更改 outerStack 的方向:

protected override void OnSizeAllocated (double width, double height){
    base.OnSizeAllocated (width, height);
    if (width != this.width || height != this.height) {
        this.width = width;
        this.height = height;
        if (width > height) {
            outerStack.Orientation = StackOrientation.Horizontal;
        } else {
            outerStack.Orientation = StackOrientation.Vertical;
        }
    }
}

I hope it help you.我希望它对你有帮助。

As far as I know this is not possible.据我所知,这是不可能的。 I did basically exactly what you want 'manually'.我基本上做了你想要的“手动”。 It's not too hard, though.不过,这并不太难。 First of all, you'll have to wrap your stack layouts in another StackLayout首先,您必须将堆栈布局包装在另一个StackLayout

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App.Views.TestPage">
    <ContentPage.Content>
        <StackLayout x:Name="OuterStackLayout">
            <StackLayout>
                <!-- Inner stack layout 1 -->
            </StackLayout>
            <StackLayout>
                <!-- Inner stack layout 2 -->
            </StackLayout>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Next, you'll have to override OnSizeAllocated and set the outer OuterStackLayout.Orientation according to your screen orientation接下来,您必须覆盖OnSizeAllocated并根据您的屏幕方向设置外部OuterStackLayout.Orientation

protected override void OnSizeAllocated(double width, double height)
{
    base.OnSizeAllocated(width, height);

    if (SizeHasChanged(width, height)) // elided, just compare width, height with the stored values
    {
        StoreSize(width, height); // store in private members

        if (IsLandscape)
        {
            this.OuterStackLayout.Orientation = StackOrientation.Horizontal;
        }
        else
        {
            this.OuterStackLayout.Orientation = StackOrientation.Vertical;
        }
    }
}

public bool IsLandscape => _width > _height;

Maybe you'll have to fiddle around with the horizontal options of the inner StackLayout sa bit - or other layout parameters, but basically this should do.也许您将不得不摆弄内部StackLayout的水平选项或其他布局参数,但基本上应该这样做。

Option #1 'Orientation States' - Portrait & Landscape.选项 #1 '方向状态' - 纵向和横向。 Use 'OrientationStates' inside visual state manager like so:在视觉状态管理器中使用“OrientationStates”,如下所示:

Inside .XAML File内部 .XAML 文件

<Grid x:Name="myGrid"
        Margin="10,30,10,10">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="OrientationStates">

            <!-- Row & Column Definitions of Grid - [2 Rows, 1 Col = Portrait] & [1 Row, 2 Cols = Landscape] -->
            <VisualState x:Name="Portrait">
                <VisualState.Setters>
                    <Setter Property="Grid.RowDefinitions"
                            Value="*,*" />
                    <Setter Property="Grid.ColumnDefinitions"
                            Value="*" />
                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Landscape">
                <VisualState.Setters>
                    <Setter Property="Grid.RowDefinitions"
                            Value="*" />
                    <Setter Property="Grid.ColumnDefinitions"
                            Value="*,*" />
                </VisualState.Setters>
            </VisualState>

        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <!-- Change position of stack layouts in grid, to match above stated orientation requirement -->
    <StackLayout x:Name="firstStackLayout">
        <Entry Placeholder="Enter first words" />
        <Button Text="Nothing Happens" />

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>

                <VisualState x:Name="Portrait">
                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                    </VisualState.Setters>
                </VisualState>

                <VisualState x:Name="Landscape">
                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                    </VisualState.Setters>
                </VisualState>

            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </StackLayout>

    <StackLayout x:Name="secondStackLayout">
        <Entry Placeholder="Enter last words" />
        <Button Text="Still Nothing Happens" />

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>

                <VisualState x:Name="Portrait">
                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="1" />
                        <Setter Property="Grid.Column" Value="0" />
                    </VisualState.Setters>
                </VisualState>

                <VisualState x:Name="Landscape">
                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="1" />
                    </VisualState.Setters>
                </VisualState>

            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </StackLayout>
</Grid>

With an added 'OnSizeAllocated()' function in CodeBehind like so:在 CodeBehind 中添加了“OnSizeAllocated()”函数,如下所示:

Inside .XAML.CS File内部 .XAML.CS 文件

public partial class RegisterPage : ContentPage
{
    public RegisterPage()
    {
        InitializeComponent();
    }

    protected override void OnSizeAllocated(double width, double height)
    {
        base.OnSizeAllocated(width, height);

        var state = (width > height) ? "Landscape" : "Portrait";

        // Call the 'Portrait' & 'Landscape' States in .XAML File
        VisualStateManager.GoToState(myGrid, state);
        VisualStateManager.GoToState(firstStackLayout, state);
        VisualStateManager.GoToState(secondStackLayout, state);
    }
}

[OR] [或者]

Option #2 The magic bullet - Your ONLY .XAML File solution to that problem.选项 #2 灵丹妙药 - 您唯一的 .XAML 文件解决方案

'Orientation Triggers' - Portrait & Landscape. “方向触发器” - 纵向和横向。

Use 'OrientationStateTriggers' inside 'State Triggers' in Visual State like so:在 Visual State 的“状态触发器”中使用“OrientationStateTriggers”,如下所示:

Inside .XAML File内部 .XAML 文件

<Grid Margin="10,30,10,10">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>

            <!-- Row & Column Definitions of Grid - [2 Rows, 1 Col = Portrait] & [1 Row, 2 Cols = Landscape] -->
            <VisualState x:Name="gridPortrait">

                <VisualState.StateTriggers>
                    <OrientationStateTrigger Orientation="Portrait" />
                </VisualState.StateTriggers>

                <VisualState.Setters>
                    <Setter Property="Grid.RowDefinitions"
                            Value="*,*" />
                    <Setter Property="Grid.ColumnDefinitions" Value="*" />
                </VisualState.Setters>

            </VisualState>

            <VisualState x:Name="gridLandscape">

                <VisualState.StateTriggers>
                    <OrientationStateTrigger Orientation="Landscape" />
                </VisualState.StateTriggers>

                <VisualState.Setters>
                    <Setter Property="Grid.RowDefinitions"
                            Value="*" />
                    <Setter Property="Grid.ColumnDefinitions"
                            Value="*,*" />
                </VisualState.Setters>

            </VisualState>

        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <!-- Change position of stack layouts in grid, to match above stated orientation requirement -->
    <StackLayout>
        <Entry Placeholder="Enter first words" />
        <Button Text="Nothing Happens" />

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>

                <VisualState x:Name="myPortraitState">

                    <VisualState.StateTriggers>
                        <OrientationStateTrigger Orientation="Portrait" />
                    </VisualState.StateTriggers>

                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                    </VisualState.Setters>

                </VisualState>

                <VisualState x:Name="myLandscapeState">

                    <VisualState.StateTriggers>
                        <OrientationStateTrigger Orientation="Landscape" />
                    </VisualState.StateTriggers>

                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                    </VisualState.Setters>

                </VisualState>

            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </StackLayout>

    <StackLayout>
        <Entry Placeholder="Enter last words" />
        <Button Text="Still Nothing Happens" />

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="myPortraitState">
                    <VisualState.StateTriggers>
                        <OrientationStateTrigger Orientation="Portrait" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="1" />
                        <Setter Property="Grid.Column" Value="0" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="myLandscapeState">
                    <VisualState.StateTriggers>
                        <OrientationStateTrigger Orientation="Landscape" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="1" />
                    </VisualState.Setters>
                </VisualState>

            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </StackLayout>
</Grid>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM