Mobiele apps programmeren met Xamarin – Layout – AbsoluteLayout en RelativeLayout

print

Inhoud


Wat vooraf ging


AbsoluteLayout

AbsoluteLayout wordt gebruikt om de elementen op een specifieke plaats te “verankeren”. Dit kan zowel via vaste waarden als via proportionele waarden. Deze waarden slaan zowel op de positie als op het formaat (hoogte en breedte) van het element.

AbsoluteLayout komt met 2 belangrijke attached properties (deze properties worden dus toegevoegd aan de elementen):

AbsoluteLayout.LayoutBounds

De propertie AbsoluteLayout.LayoutBounds is een attached property en bepaald de positionering en de hoogte en breedte van het element waar het aan gekoppeld (attached) is. AbsoluteLayout.LayoutBounds komt met 4 parameters in respectievelijke volgorde:

  • X – de x (horizontale) positie
  • Y – de y (verticale) positie
  • Width – de breedte van het element
  • Height – de hoogte van het element

De waarden die worden meegegeven kunnen absoluut (vast) zijn.

Een voorbeeld in XAML van absolute plaatsing:

<Label Text="Test" AbsoluteLayout.LayoutBounds="115,150,100,100" />

Hetzelfde in C#:

var label = new Label { Text = "Test" };
AbsoluteLayout.SetLayoutBounds(label, new Rectangle(115,150,100,100));

De waarden die worden meegegeven kunnen proportioneel zijn met een getal tussen 0 en 1.

Een voorbeeld in XAML van proportionele plaatsing:

<Label Text="I'm bottom center on every device." AbsoluteLayout.LayoutBounds=".5,1,.5,.1" AbsoluteLayout.LayoutFlags="All" />
  • (horizontaal) = .5 (gecentreerd (0 is links en 1 rechts))
  • Y (verticaal) = 1 (onderaan (0 zou bovenaan zijn))
  • Width = .5 (de breedte is de helft van het scherm)
  • Height = .1 (de hoogte is 1/10 van het scherm)

Hetzelfde in C#:

var label = new Label { Text = "I'm bottom center on every device." };
AbsoluteLayout.SetLayoutBounds(label, new Rectangle(.5,1,.5,.1));
AbsoluteLayout.SetLayoutFlags(label, AbsoluteLayoutFlags.All);

Let op, omdat alle waarden proportioneel zijn moet u de volgende code toevoegen AbsoluteLayout.LayoutFlags="All". Wilt u de tekst zelf ook nog centreren voeg dan nog een HorizontalOptions="Center" toe.

Onderstaande code werkt dit volledig uit met de tekst PCVO Dender en Schelde.

<?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:local="clr-namespace:App2"
             x:Class="App2.MainPage">
    <AbsoluteLayout>
        <Label Text="PCVO Dender en Schelde" AbsoluteLayout.LayoutBounds=".5,1,.5,.1" 
               AbsoluteLayout.LayoutFlags="All" FontSize="Large" HorizontalOptions="Center" ></Label>
    </AbsoluteLayout>
</ContentPage>

AbsoluteLayout.LayoutFlags

Wellicht hebt u in bovenstaand voorbeeld de attached property AbsoluteLayout.LayoutFlags opgemerkt. Wat doet deze?

Wel, hoe weet de computer dat die 1 proportioneel bedoeld is en niet gewoon verwijst naar een plaatsing of een hoogte en breedte van 1 punt?

De computer kan dit niet weten tenzij we hem zeggen hoe hij de waarden moet interpreteren en daarvoor dient de AbsoluteLayout.LayoutFlags.

De AbsoluteLayout.LayoutFlags heeft volgende waarden:

  • None – alle waarden worden als absoluut beschouwd. Dit is de standaardwaarde en mag dus weggelaten worden indien de waarden allemaal absoluut zijn.
  • All – alle waarden zijn proportioneel.
  • WidthProportional – de breedte is proportioneel, alle andere waarden zijn absoluut.
  • HeightProportional – de hoogte is proportioneel, alle andere waarden zijn absoluut.
  • XProportional – de horizontale waarde (X) is proportioneel, alle andere waarden zijn absoluut.
  • YProportional – de verticale waarde (Y) is proportioneel, alle andere waarden zijn absoluut.
  • PositionProportional – de positionele waarden (X en Y) zijn proportioneel, de hoogte en de breedte zijn absoluut.
  • SizeProportional – de hoogte en de breedte zijn proportioneel, de positionele (X en Y) waarden zijn absoluut.

Voorbeeld

  • Start een nieuw Xamarin-project en geef het een passende naam of voeg een pagina toe aan het vorige Layout-project.

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="LayoutOpties.Page5">
    <AbsoluteLayout>
        <Label Text="Gecentreerd op iPhone 4 maar niet op andere devices" AbsoluteLayout.LayoutBounds="115,150,100,100" LineBreakMode="WordWrap" />
        <Label Text="Op ieder toestel onderaan en gecentreerd." AbsoluteLayout.LayoutBounds=".5,1,.5,.1" AbsoluteLayout.LayoutFlags="All" LineBreakMode="WordWrap" />
        <BoxView Color="Blue" AbsoluteLayout.LayoutBounds=".5,0,100,25" AbsoluteLayout.LayoutFlags="PositionProportional" />
        <BoxView Color="Red" AbsoluteLayout.LayoutBounds="0,.5,25,100" AbsoluteLayout.LayoutFlags="PositionProportional" />
        <BoxView Color="Olive" AbsoluteLayout.LayoutBounds="1,.5, 25, 100" AbsoluteLayout.LayoutFlags="PositionProportional" />
    </AbsoluteLayout>

</ContentPage>

C#

using Xamarin.Forms;

namespace LayoutOpties
{
    public class AbsoluteLayoutC : ContentPage
    {
        public AbsoluteLayoutC()
        {
            var layout = new AbsoluteLayout();

            var centerLabel = new Label { Text = "Gecentreerd op iPhone 4 maar niet op andere devices", LineBreakMode = LineBreakMode.WordWrap, FontSize = 20 };
            AbsoluteLayout.SetLayoutBounds(centerLabel, new Rectangle(115, 159, 100, 100));

            var bottomLabel = new Label { Text = "Op ieder toestel onderaan en gecentreerd.", LineBreakMode = LineBreakMode.WordWrap };
            AbsoluteLayout.SetLayoutBounds(bottomLabel, new Rectangle(.5, 1, .5, .1));
            AbsoluteLayout.SetLayoutFlags(bottomLabel, AbsoluteLayoutFlags.All);

            var topBox = new BoxView { Color = Color.Blue };
            AbsoluteLayout.SetLayoutBounds(topBox, new Rectangle(.5, 0, 100, 25));
            AbsoluteLayout.SetLayoutFlags(topBox, AbsoluteLayoutFlags.PositionProportional);

            var leftBox = new BoxView { Color = Color.Red };
            AbsoluteLayout.SetLayoutBounds(leftBox, new Rectangle(0, .5, 25, 100));
            AbsoluteLayout.SetLayoutFlags(leftBox, AbsoluteLayoutFlags.PositionProportional);

            var rightBox = new BoxView { Color = Color.Olive };
            AbsoluteLayout.SetLayoutBounds(rightBox, new Rectangle(1, .5, 25, 100));
            AbsoluteLayout.SetLayoutFlags(rightBox, AbsoluteLayoutFlags.PositionProportional);

            layout.Children.Add(bottomLabel);
            layout.Children.Add(centerLabel);
            layout.Children.Add(rightBox);
            layout.Children.Add(leftBox);
            layout.Children.Add(topBox);

            Content = layout;
        }
    }
}

Voorbeeld – Schaakbord

Omdat een schaakbord het best dynamische wordt opgebouwd gaan we dit via programmeercode ontwerpen.

  • Start een nieuw Xamarin-project en geef het een passende naam (bv. schaakbord) of voeg een pagina (geen XAML-pagina) toe aan het bestaande layout-project.
using Xamarin.Forms;

namespace LayoutOpties
{
    public class Schaakbord : ContentPage
    {
        public Schaakbord()
        {
            const double squareSize = 35;

            AbsoluteLayout absoluteLayout = new AbsoluteLayout
            {
                BackgroundColor = Color.FromRgb(240, 220, 130),
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.Center
            };

            for (int row = 0; row < 8; row++)
            {
                for (int col = 0; col < 8; col++)
                {
                    // Sla telkens een vierkantje over.
                    if (((row ^ col) & 1) == 0)
                        continue;

                    BoxView boxView = new BoxView
                    {
                        Color = Color.FromRgb(0, 64, 0)
                    };
                    Rectangle rect = new Rectangle(col * squareSize,
                                                   row * squareSize,
                                                   squareSize, squareSize);

                    absoluteLayout.Children.Add(boxView, rect);
                }
            }
            this.Content = absoluteLayout;

        }
    }
}

Complex voorbeeld

We hernemen hier het voorbeeld dat we reeds aangemaakt hebben via een StackLayout.

<?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="LayoutOpties.ComplexAbsoluteLayout">
    <ScrollView>
        <AbsoluteLayout BackgroundColor="Maroon">
            <BoxView BackgroundColor="Gray" AbsoluteLayout.LayoutBounds="0,0,1,20" AbsoluteLayout.LayoutFlags="XProportional,YProportional,WidthProportional" />
            <Button BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds=".5,55,70,70" AbsoluteLayout.LayoutFlags="XProportional" BorderRadius="35" />
            <Button BackgroundColor="Red" AbsoluteLayout.LayoutBounds=".5,60,60,60" AbsoluteLayout.LayoutFlags="XProportional" BorderRadius="30" />
            <Label Text="User Name" FontAttributes="Bold" FontSize="26" TextColor="Black" HorizontalTextAlignment="Center" AbsoluteLayout.LayoutBounds=".5,140,1,40" AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
            <Entry Text="Bio + Hashtags" TextColor="White" BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds=".5,180,1,40" AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
            <AbsoluteLayout BackgroundColor="White" AbsoluteLayout.LayoutBounds="0, 220, 1, 50" AbsoluteLayout.LayoutFlags="XProportional,WidthProportional">
                <AbsoluteLayout AbsoluteLayout.LayoutBounds="0,0,.5,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional">
                    <Button BackgroundColor="Black" BorderRadius="20" AbsoluteLayout.LayoutBounds="5,.5,40,40" AbsoluteLayout.LayoutFlags="YProportional" />
                    <Label Text="Accent Color" TextColor="Black" AbsoluteLayout.LayoutBounds="50,.55,1,25" AbsoluteLayout.LayoutFlags="YProportional,WidthProportional" />
                </AbsoluteLayout>
                <AbsoluteLayout AbsoluteLayout.LayoutBounds="1,0,.5,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,XProportional">
                    <Button BackgroundColor="Maroon" BorderRadius="20" AbsoluteLayout.LayoutBounds="5,.5,40,40" AbsoluteLayout.LayoutFlags="YProportional" />
                    <Label Text="Primary Color" TextColor="Black" AbsoluteLayout.LayoutBounds="50,.55,1,25" AbsoluteLayout.LayoutFlags="YProportional,WidthProportional" />
                </AbsoluteLayout>
            </AbsoluteLayout>
            <AbsoluteLayout AbsoluteLayout.LayoutBounds="0,270,1,50" AbsoluteLayout.LayoutFlags="WidthProportional" Padding="5,0,0,0">
                <Label Text="Age:" TextColor="White" AbsoluteLayout.LayoutBounds="0,25,.25,50" AbsoluteLayout.LayoutFlags="WidthProportional" />
                <Entry Text="35" TextColor="White" BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds="1,10,.75,50" AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
            </AbsoluteLayout>
            <AbsoluteLayout AbsoluteLayout.LayoutBounds="0,320,1,50" AbsoluteLayout.LayoutFlags="WidthProportional" Padding="5,0,0,0">
                <Label Text="Interests:" TextColor="White" AbsoluteLayout.LayoutBounds="0,25,.25,50" AbsoluteLayout.LayoutFlags="WidthProportional" />
                <Entry Text="Xamarin.Forms" TextColor="White" BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds="1,10,.75,50" AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
            </AbsoluteLayout>
            <AbsoluteLayout AbsoluteLayout.LayoutBounds="0,370,1,50" AbsoluteLayout.LayoutFlags="WidthProportional" Padding="5,0,0,0">
                <Label Text="Ask me about:" TextColor="White" AbsoluteLayout.LayoutBounds="0,25,.25,50" AbsoluteLayout.LayoutFlags="WidthProportional" />
                <Entry Text="Xamarin, C#, .NET, Mono" TextColor="White" BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds="1,10,.75,50" AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
            </AbsoluteLayout>
        </AbsoluteLayout>
    </ScrollView>
</ContentPage>

RelativeLayout

Een RelativeLayout bouwt de layout relatief ten opzichte van de het parent-element of een ander element. De RelativeLayout kent 4 attached properties:

  • XConstraint – de X positie
  • YConstraint – de Y positie
  • WidthConstraint – de breedte van het element
  • HeightConstraint – de hoogte van het element

De waarden worden toegekend via een ConstraintExpression. Deze ConstraintExpression heeft de volgende eigenschappen:

  • Type – duidt aan of de constraint relatief is aan de “ouder” (RelativeToParent) of aan een andere View (RelativeToView)
  • ElementName – indien de constraint relatief is aan een andere View komt hier de naam van deze View
  • Property – de eigenschap waar de constraint relatief aan is
  • Factor – een “multiplier” die van toepassing is op de waarde van de Property
  • Constant – een constante die toegevoegd wordt aan de waarde van de Property

Een paar voorbeelden zal dit hopelijk wat duidelijker maken.

Voorbeeld

Rode box

De rode box heeft volgende eigenschappen:

XConstraint – horizontaal – de box begint gewoon tegen de kantlijn en hoeft dus niet geduid te worden.
YConstraint – verticaal – de verticale positie begint op een factor 15% van de totale hoogte relatief ten opzichte van het “ouder”-element (de pagina). Dit is de reden dat er bovenaan een witte boord is.

RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.15, Constant=0}"

WidthConstraint – de breedte van de rode box is even breed als de breedte van het “ouder”-element (hier de volledige pagina).

RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}"

HeightConstraint – de hoogte is een factor 80% van de hoogte van het “ouder”-element (de volledige pagina). Dit is de reden dat u onderaan nog een witte boord hebt.

        
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.8, Constant=0}" />

De volledige rode box in XAML:

        
<BoxView Color="Red" x:Name="redBox" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.15, Constant=0}" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=0}" RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.8, Constant=0}" />

Dezelfde code in C#:

        
layout.Children.Add (redBox, 
        Constraint.RelativeToParent ((parent) => {
        return parent.X;
    }), Constraint.RelativeToParent ((parent) => {
        return parent.Y * .15;
    }), Constraint.RelativeToParent((parent) => {
        return parent.Width;
    }), Constraint.RelativeToParent((parent) => {
        return parent.Height * .8;
    }));

Merk op dat ook al heeft XAML geen code voor de X-positie, in C# moeten steeds de 4 eigenschappen in die volgorde aanwezig zijn: x-positie, y-positie, breedte, hoogte. De code gebruikt een Lambda-expressie en geeft een berekening terug.

Blauwe box

De blauwe box heeft volgende eigenschappen:

XConstraint – horizontaal – de blauwe box zal ten opzichte van de rode box horizontaal 20 opschuiven (Constant=20).

RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView, ElementName=redBox, Property=X, Factor=1, Constant=20}"

YConstraint – verticaal – de blauwe box zal ten opzichte van de rode box verticaal 20 opschuiven (Constant=20).

RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=redBox, Property=Y, Factor=1, Constant=20}"

WidthConstraint – de blauwe box is half zo breed (factor=.5) dan de breedte van de “ouder” (de pagina).

RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.5, Constant=0}"

HeightConstraint – de blauwe box is half zo breed (factor=.5) dan de breedte van de “ouder” (de pagina).

        
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.5, Constant=0}" />

De volledige blauwe box in XAML:

        
<BoxView Color="Blue" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView, ElementName=redBox, Property=X, Factor=1, Constant=20}" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=redBox, Property=Y, Factor=1, Constant=20}" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=.5, Constant=0}" RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.5, Constant=0}" />

Dezelfde code in C#:

        
layout.Children.Add (blueBox, 
        Constraint.RelativeToView (redBox, (Parent, sibling) => {
        return sibling.X + 20;
    }), Constraint.RelativeToView (redBox, (parent, sibling) => {
        return sibling.Y + 20;
    }), Constraint.RelativeToParent((parent) => {
        return parent.Width * .5;
    }), Constraint.RelativeToParent((parent) => {
        return parent.Height * .5;
    }));

Voorbeeld 2

        
<?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="LayoutOpties.RelativeLayoutVoorbeeld2">

    <RelativeLayout>

        <!-- Upper left -->
        <BoxView Color="Red" RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}" RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}" />
        <!-- Upper right -->
        <BoxView Color="Green" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=-40}" RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}" />
        <!-- Lower left -->
        <BoxView Color="Blue" RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=-40}" />
        <!-- Lower right -->
        <BoxView Color="Yellow" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1, Constant=-40}" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1, Constant=-40}" />

        <!-- Centered and 1/3 width and height of parent -->
        <BoxView x:Name="oneThird" Color="Red" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.33}" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.33}" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.33}" RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.33}" />

        <!-- 1/3 width and height of previous -->
        <BoxView Color="Blue" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView, ElementName=oneThird, Property=X}" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=oneThird, Property=Y}" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToView, ElementName=oneThird, Property=Width, Factor=0.33}" RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToView, ElementName=oneThird, Property=Height, Factor=0.33}" />
    </RelativeLayout>

</ContentPage>

Design guides

Onderstaande design guides sluiten aan bij wat besproken is in deze post. Ik raad aan deze zeker eens te bekijken.

Android

iOS

UWP


Hoor het eens van iemand anders

We maken nog eens een uitstapje naar de Xamarin University waar ze het deze keer hebben over Responsive Layout. Responsive layout, het automatisch aanpassen van de pagina aan het formaat en de oriëntatie van de device is standaard ingebouwd in Xamarin en heeft slechts een paar kleine “tweaks” nodig. Deze komen aan bod in onderstaande video.


Behandelde Basiscompetenties uit de module ICT Programmeren – Specifieke ontwikkelomgeving: complexe functionaliteiten

  • IC BC246 – kan complexe ontwerpen in een specifieke ontwikkelomgeving maken
  • IC BC251 – kan een ontwerp in een specifieke ontwikkelomgeving verfijnen
  • IC BC253 – kan broncode in een specifieke ontwikkelomgeving optimaliseren
  • IC BC257 – heeft aandacht voor de gebruiksvriendelijkheid van de toepassing

Geef een reactie

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.