Xamarin.Forms:基于设备大小的显示框架

时间:2018-06-28 19:58:31

标签: c# xamarin.forms

我正在开发一个控件,该控件可以动态地从List中生成一个网格,它应该始终显示2个项目。

correct presentation

现在我遇到了问题,在小型设备(例如iPhone 4s)上,每行仅显示一项。

wrong presentation

XAML代码

    <ContentPage.Content>
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height="140"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="10" />
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="10" />
        </Grid.ColumnDefinitions>
        <Image Source="background.png" Grid.ColumnSpan="3" Grid.RowSpan="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Aspect="Fill" Margin="0,-10,0,0"/>
        <Image Source="logo.png" Grid.Column="1" Grid.Row="0" HorizontalOptions="Center" HeightRequest="140"/>
        <ScrollView Orientation="Vertical" Grid.Column="1" Grid.Row="1">
            <view:WrapView Command="{Binding ModuleClickedCommand}" ItemsSource="{Binding TileItems}" Orientation="Vertical">
                <view:WrapView.ItemTemplate>
                    <DataTemplate>
                        <Frame Opacity="0.7" CornerRadius="5" BackgroundColor="#dcdcdc" Margin="20,15,0,0" WidthRequest="130" HeightRequest="130" Padding="6,6,6,6">
                            <Frame.Effects>
                                <effects:TileClickRoutingEffect />
                            </Frame.Effects>
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="*" />
                                    <RowDefinition Height="Auto" />
                                </Grid.RowDefinitions>
                                <Image Grid.Row="0" Source="{Binding ImgSource}"/>
                                <Label Grid.Row="1" HorizontalOptions="CenterAndExpand" FontSize="12" LineBreakMode="TailTruncation" Text="{Binding Title}" XAlign="Center"/>
                            </Grid>
                        </Frame>
                    </DataTemplate>
                </view:WrapView.ItemTemplate>
            </view:WrapView>
        </ScrollView>
    </Grid>
</ContentPage.Content>

控件中的高度/宽度计算

 /// <summary>
    /// Called during the measure pass of a layout cycle to get the desired size of an element.
    /// </summary>
    /// <param name="widthConstraint">The available width for the element to use.</param>
    /// <param name="heightConstraint">The available height for the element to use.</param>
    protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
    {
        if (WidthRequest > 0)
            widthConstraint = Math.Min(widthConstraint, WidthRequest);
        if (HeightRequest > 0)
            heightConstraint = Math.Min(heightConstraint, HeightRequest);

        double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
        double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);

        return Orientation == StackOrientation.Horizontal
            ? DoVerticalMeasure(internalWidth, internalHeight)
                : DoHorizontalMeasure(internalWidth, internalHeight);

    }

    private SizeRequest DoVerticalMeasure(double widthConstraint, double heightConstraint)
    {
        int columnCount = 1;

        double width = 0;
        double height = 0;
        double minWidth = 0;
        double minHeight = 0;
        double heightUsed = 0;

        foreach (var item in Children)
        {
            var size = item.GetSizeRequest(widthConstraint, heightConstraint);
            width = Math.Max(width, size.Request.Width);

            var newHeight = height + size.Request.Height + Spacing;
            if (newHeight > heightConstraint)
            {
                columnCount++;
                heightUsed = Math.Max(height, heightUsed);
                height = size.Request.Height;
            }
            else
                height = newHeight;

            minHeight = Math.Max(minHeight, size.Minimum.Height);
            minWidth = Math.Max(minWidth, size.Minimum.Width);
        }

        if (columnCount > 1)
        {
            height = Math.Max(height, heightUsed);
            width *= columnCount;  // take max width
        }

        return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
    }

    /// <summary>
    /// Does the horizontal measure.
    /// </summary>
    /// <returns>The horizontal measure.</returns>
    /// <param name="widthConstraint">Width constraint.</param>
    /// <param name="heightConstraint">Height constraint.</param>
    private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
    {
        int rowCount = 1;

        double width = 0;
        double height = 0;
        double minWidth = 0;
        double minHeight = 0;
        double widthUsed = 0;

        foreach (var item in Children)
        {
            var size = item.GetSizeRequest(widthConstraint, heightConstraint);
            // Wenn width*2 nicht mehr in widthContraint passt, dann muss die breite händisch durch /2.5 gesetzt werden. Solle dies nicht der fall sein: ganz normal weitermachen
            //TODO size.Request.Width evtl anpassen

            height = Math.Max(height, size.Request.Height);

            var newWidth = width + size.Request.Width + Spacing;
            if (newWidth > widthConstraint)
            {
                rowCount++;
                widthUsed = Math.Max(width, widthUsed);
                width = size.Request.Width;
            }
            else
                width = newWidth;

            minHeight = Math.Max(minHeight, size.Minimum.Height);
            minWidth = Math.Max(minWidth, size.Minimum.Width);
        }

        if (rowCount > 1)
        {
            width = Math.Max(width, widthUsed);
            height = (height + Spacing) * rowCount - Spacing;
        }

        return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
    }

    /// <summary>
    /// Positions and sizes the children of a Layout.
    /// </summary>
    /// <param name="x">A value representing the x coordinate of the child region bounding box.</param>
    /// <param name="y">A value representing the y coordinate of the child region bounding box.</param>
    /// <param name="width">A value representing the width of the child region bounding box.</param>
    /// <param name="height">A value representing the height of the child region bounding box.</param>
    protected override void LayoutChildren(double x, double y, double width, double height)
    {
        if (Orientation == StackOrientation.Horizontal)
        {
            double colWidth = 0;
            double yPos = y, xPos = x;

            foreach (var child in Children.Where(c => c.IsVisible))
            {
                var request = child.GetSizeRequest(width, height);

                double childWidth = request.Request.Width;
                double childHeight = request.Request.Height;
                colWidth = Math.Max(colWidth, childWidth);

                if (yPos + childHeight > height)
                {
                    yPos = y;
                    xPos += colWidth + Spacing;
                    colWidth = 0;
                }

                var region = new Rectangle(xPos, yPos, childWidth, childHeight);
                LayoutChildIntoBoundingRegion(child, region);
                yPos += region.Height + Spacing;
            }
        }
        else
        {
            double rowHeight = 0;
            double yPos = y, xPos = x;

            foreach (var child in Children.Where(c => c.IsVisible))
            {
                var request = child.GetSizeRequest(width, height);

                double childWidth = request.Request.Width;
                double childHeight = request.Request.Height;
                rowHeight = Math.Max(rowHeight, childHeight);

                if (xPos + childWidth > width)
                {
                    xPos = x;
                    yPos += rowHeight + Spacing;
                    rowHeight = 0;
                }

                var region = new Rectangle(xPos, yPos, childWidth, childHeight);
                LayoutChildIntoBoundingRegion(child, region);
                xPos += region.Width + Spacing;
            }

        }
    }

我现在的问题是,是否有可能根据设备的分辨率计算一帧的大小?您有解决该问题的想法吗?您是否必须处理类似的问题? 感谢您的帮助。

0 个答案:

没有答案