【问题标题】:How to create a responsive grid with squares?如何创建带有正方形的响应式网格?
【发布时间】:2014-08-17 09:22:58
【问题描述】:

我目前正在创建一个充满按钮的网格。现在我希望这个网格能够响应并且按钮是方形的。我想要创建的东西看起来像一个 iOS 或 Android 主屏幕(只是按钮)。

Grid grid = new Grid
        {

            HorizontalOptions = LayoutOptions.StartAndExpand,
            RowSpacing = 15,
            ColumnSpacing = 15,
            ColumnDefinitions = 
            {
                new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
                new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
                new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
            }
        };

        for (int i = 0; i < 12; i++)
        {
            MonitorButton button = new MonitorButton();
            grid.Children.Add(button, i % 3, i / 3);
        }

        this.Content = new ScrollView
        {
            Padding = new Thickness(15),
            Orientation = ScrollOrientation.Vertical,
            Content = grid
        };

所以我在这里创建了一个包含 3 列的网格,其中填充了 12 个 MonitorButtons(背景上有图像的按钮)。看起来不错..现在当我以横向模式打开屏幕时,屏幕上会充满矩形,因为列数仍然相同... 我该如何解决这个问题?我希望有某种动态的可能性(例如:itemscontrol @ XAML)。

【问题讨论】:

  • Xamarin.Forms 中当前没有 GridView。您将必须计算列宽(和间距)并动态添加列定义。这将使用modulus operator % 来完成

标签: mobile grid xamarin xamarin.forms


【解决方案1】:

当您旋转屏幕时,当前页面会自行调整大小,因此它会在您的页面中调用 OnSizeRequest 方法。如果覆盖该方法,则可以检测大小和方向。

YourPage.cs || YourPage.xaml.cs

protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
{
    if(widthConstraint>heightConstraint)
    {
        //landscape mode
        //set your grid row & columndefinitions
    }
    else
    {
        //portait mode
        //set your grid row & columndefinitions
    }
    return base.OnSizeRequest(widthConstraint, heightConstraint);
}

【讨论】:

    【解决方案2】:

    我认为这个自定义的(自定义网格控制)网格会更好用,它有命令,所以你可以识别哪个项目被点击,因此我认为你的 UI 上可能不需要这么多按钮。 您可以在网格单元格中添加任何您想要的控件(在 Square 中,就像您添加了按钮一样)

    GridView.Xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <Grid xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Test.Controls.GridView">
    
    </Grid>
    

    GridView.Xaml.cs

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Input;
    using Test.Views;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace Test.Controls
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class GridView : Grid
        {
            public GridView()
            {
                InitializeComponent();
                for (var i = 0; i < MaxColumns; i++)
                    ColumnDefinitions.Add(new ColumnDefinition());
            }
    
            public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create<GridView, object>(p => p.CommandParameter, null);
            public static readonly BindableProperty CommandProperty = BindableProperty.Create<GridView, ICommand>(p => p.Command, null);
            public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create<GridView, IEnumerable<object>>(p => p.ItemsSource, null, BindingMode.OneWay, null, (bindable, oldValue, newValue) => { ((GridView)bindable).BuildTiles(newValue); });
    
            private int _maxColumns = 2;
            private float _tileHeight = 0;
    
            public Type ItemTemplate { get; set; } = typeof(SubControlsView);
    
            public int MaxColumns
            {
                get { return _maxColumns; }
                set { _maxColumns = value; }
            }
    
            public float TileHeight
            {
                get { return _tileHeight; }
                set { _tileHeight = value; }
            }
    
            public object CommandParameter
            {
                get { return GetValue(CommandParameterProperty); }
                set { SetValue(CommandParameterProperty, value); }
            }
    
            public ICommand Command
            {
                get { return (ICommand)GetValue(CommandProperty); }
                set { SetValue(CommandProperty, value); }
            }
    
            public IEnumerable<object> ItemsSource
            {
                get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
                set { SetValue(ItemsSourceProperty, value); }
            }
    
            public void BuildTiles(IEnumerable<object> tiles)
            {
                try
                {
                    if (tiles == null || tiles.Count() == 0)
                        Children?.Clear();
    
                    // Wipe out the previous row definitions if they're there.
                    RowDefinitions?.Clear();
    
                    var enumerable = tiles as IList ?? tiles.ToList();
                    var numberOfRows = Math.Ceiling(enumerable.Count / (float)MaxColumns);
    
                    for (var i = 0; i < numberOfRows; i++)
                        RowDefinitions?.Add(new RowDefinition { Height = TileHeight });
    
                    for (var index = 0; index < enumerable.Count; index++)
                    {
                        var column = index % MaxColumns;
                        var row = (int)Math.Floor(index / (float)MaxColumns);
    
                        var tile = BuildTile(enumerable[index]);
    
                        Children?.Add(tile, column, row);
                    }
                }
                catch
                { // can throw exceptions if binding upon disposal
                }
            }
    
            private Layout BuildTile(object item1)
            {
                var buildTile = (Layout)Activator.CreateInstance(ItemTemplate, item1);
                buildTile.InputTransparent = false;
    
                var tapGestureRecognizer = new TapGestureRecognizer
                {
                    Command = Command,
                    CommandParameter = item1,
                    NumberOfTapsRequired = 1
                };
    
                buildTile?.GestureRecognizers.Add(tapGestureRecognizer);
    
    
                return buildTile;
            }
        }
    }
    

    您可以在此处创建内部 UI(视图)。即每个方格的内部视图。在这个例子中,我只添加了一个标签。

    SubControlsView.Xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <Grid xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Test.Views.SubControlsView" BackgroundColor="CornflowerBlue">
        <StackLayout VerticalOptions="CenterAndExpand">
            <!--You can add any controls which you want--> 
            <!--<Label Text="{Binding id}" HorizontalOptions="CenterAndExpand" TextColor="White" />-->
    
        </StackLayout>
    </Grid>
    

    SubControlsView.Xaml.cs

    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace Test.Views
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class SubControlsView : Grid
        {
            public SubControlsView()
            {
                InitializeComponent();
            }
    
            public SubControlsView(object item)
            {
                InitializeComponent();
                BindingContext = item;
            }
        }
    }
    

    然后在任何你想要的地方使用这个控件。即在任何内容页面上。这将是您的实际视图(仪表板)

    SubCarriers.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"
                 xmlns:control="clr-namespace:Test.Controls"
                x:Class="Test.Views.SubCarriers" Title="Details Dashboard">
        <ContentPage.Content>
            <ScrollView Margin="5">
                <control:GridView HorizontalOptions="FillAndExpand"
                            Grid.Row="1"
                            VerticalOptions="FillAndExpand"
                            RowSpacing="5"
                            ColumnSpacing="5"
                            MaxColumns="2"
                            ItemsSource="{Binding SubCarriersCnt}"
                            CommandParameter="{Binding SubCarriersCnt}"
                            Command="{Binding ClickCommand}"
                            IsClippedToBounds="False">
                    <control:GridView.TileHeight>
                        <OnPlatform x:TypeArguments="x:Single"
                          iOS="60"
                          Android="90"
                          WinPhone="90" />
                    </control:GridView.TileHeight>
                </control:GridView>
            </ScrollView>
        </ContentPage.Content>
    </ContentPage>
    

    然后将您的 ViewModel 绑定到您的视图。并在 YourViewModel 中实现 ClickCommand

    SubCarriers.Xaml.cs

    using System;
    using Test.ViewModels;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    using CommonUtility;
    
    namespace Test.Views
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class SubCarriers : ContentPage
        {
            private YourViewModel viewModel;
    
            public SubCarriers()
            {
                InitializeComponent();
                this.BindingContext = new SubCarriersViewModel();
                viewModel = (YourViewModel)this.BindingContext;
            }
    
        }
    }
    

    【讨论】:

    • 嗨,我已经实现了一切,除了 ClickCommand 之外它都可以工作。我已经尝试了几件事,但无法获得点击操作。如果可以,请分享代码。
    猜你喜欢
    • 2016-09-03
    • 1970-01-01
    • 2013-05-20
    • 1970-01-01
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    • 2018-04-26
    • 2015-06-01
    相关资源
    最近更新 更多