CollectionView. const number of images on screen. Xamarin Forms

Viewed 239

I have collection view with images. I achived const number(5) of images to view with relative layout. On my phone it looks fine. enter image description here But on other phone with different size of screen it has more images, than i need. enter image description here How to make const number of images on all screens?

This is my XAML:

<Grid VerticalOptions="Center" HorizontalOptions="FillAndExpand">
        <Grid.RowDefinitions>
            <RowDefinition Height="44"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="44"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="44"/>
        </Grid.ColumnDefinitions>
        <BoxView Color="Red" Grid.Row="0" Grid.Column="0"/>
        <BoxView Color="Red" Grid.Row="0" Grid.Column="2"/>
        <CollectionView Grid.Row="0" Grid.Column="1" x:Name="clv" ItemsSource="{Binding ImagesArray}" HorizontalOptions="FillAndExpand" VerticalOptions="Center">
            <CollectionView.ItemsLayout>
                <LinearItemsLayout Orientation="Horizontal" SnapPointsAlignment="Start" SnapPointsType="Mandatory" ItemSpacing="0"/>
            </CollectionView.ItemsLayout>
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <RelativeLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
                        <Image Source="{Binding ImageName}"
                           RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=0}"
                       RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.035, Constant=0}"/>
                    </RelativeLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </Grid>
1 Answers

CASE A: No scrolling - all items fit.

An easy way to evenly space items is using VerticalGrid:

<Grid ColumnSpacing="0" VerticalOptions="Center"
      RowDefinitions="44" ColumnDefinitions="44,*,44">
    <BoxView Color="Red" Grid.Row="0" Grid.Column="0"/>
    <BoxView Color="Red" Grid.Row="0" Grid.Column="2"/>
    <CollectionView Grid.Row="0" Grid.Column="1" x:Name="clv" ItemsSource="{Binding ImagesArray}" 
                    ItemsLayout="VerticalGrid, 5" >
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Grid ColumnDefinitions="*,Auto,*">
                    <BoxView Grid.Column="1" WidthRequest="40" Color="Blue" VerticalOptions="Center"/>
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</Grid>

"VerticalGrid, 5" means that there are 5 items in each row. These items will be evenly spaced across the width of the CollectionView.

NOTE: ColumnSpacing="0" in the outer grid, to maximize the space available to the middle column (the collection view).

In the ItemTemplate, I used a 3-column Grid to center the BoxView. That was needed because BoxView wants to fill the whole width of the grid cell. Might not need this for other elements - instead try HorizontalOptions=Center or HorizontalTextAlignment=Center (for Label).


CASE B: Horizontal scrolling - known item and border widths.

EvenlySpaceItemsScrolling.xaml:

<ContentPage.Content>
    <Grid ColumnSpacing="0" VerticalOptions="Center"
          RowDefinitions="44" ColumnDefinitions="44,*,44">
        <BoxView Color="Red" Grid.Row="0" Grid.Column="0"/>
        <BoxView Color="Red" Grid.Row="0" Grid.Column="2"/>
        <CollectionView Grid.Row="0" Grid.Column="1" x:Name="clv" ItemsSource="{Binding ImagesArray}">
            <CollectionView.ItemsLayout>
                <LinearItemsLayout x:Name="MyItemsLayout" Orientation="Horizontal" ItemSpacing="10"/>
            </CollectionView.ItemsLayout>
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <BoxView WidthRequest="30" HeightRequest="30" Color="Blue" VerticalOptions="Center"/>
                    </StackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </Grid>
</ContentPage.Content>

EvenlySpaceItemsScrolling.xaml.cs:

using System;
using System.Collections.ObjectModel;

using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestBugs
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class EvenlySpaceItemsScrolling : ContentPage
    {
        const int NItemsToShow = 5;
        const int ItemWidth = 30;

        public EvenlySpaceItemsScrolling()
        {
            InitializeComponent();

            BindingContext = this;
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();

            // If the amount of space to left and right of scrolling area is known,
            // we can handle it before the page is laid out.
            // See xaml: the width of left column is 44, right column is 44.
            const int SubtractThisWidth = 44 + 44;
            int collectionWidth = (int)ScreenLogicalWidth() - SubtractThisWidth;
            SetSpacingAndMargin(NItemsToShow, ItemWidth, collectionWidth);
        }


        private void SetSpacingAndMargin(int nItemsToShow, int itemWidth, int collectionWidth)
        {
            int widthPerItem = collectionWidth / nItemsToShow;
            int spacing = Math.Max(0, widthPerItem - itemWidth);
            int leftMargin = spacing / 2;

            this.MyItemsLayout.ItemSpacing = spacing;
            this.clv.Margin = new Thickness(leftMargin, 0, 0, 0);

            // --- Manually test values that work well at a specific screen width. ---
            //this.MyItemsLayout.ItemSpacing = 30;
            //this.clv.Margin = new Thickness(15, 0, 0, 0);
        }

        public static double ScreenLogicalWidth()
        {
            return DeviceDisplay.MainDisplayInfo.Width / DeviceDisplay.MainDisplayInfo.Density;
        }

        // For demo, this is only used to get a count of items.
        // Replace with your actual source.
        public ObservableCollection<Model> ImagesArray { get; set; } = new ObservableCollection<Model> {
            new Model(),
            new Model(),
            new Model(),
            new Model(),
            new Model(),
        };
    }
}

CASE C: Horizontal scrolling - measure collection width.

EvenlySpaceItemsScrolling.xaml - same as in CASE B.

EvenlySpaceItemsScrolling.xaml.cs - see CASE B, but REMOVE code in OnAppearing, and ADD this:

    protected override void LayoutChildren(double x, double y, double width, double height)
    {
        base.LayoutChildren(x, y, width, height);

        // Dynamically calculate based on collection width.
        int collectionWidth = (int)this.clv.Width;
        SetSpacingAndMargin(NItemsToShow, ItemWidth, collectionWidth);
    }
Related