Partager via


Unités indépendantes de l’appareil et pixels

Explorer les différences entre les coordonnées et Xamarin.Forms les coordonnées SkiaSharp

Cet article explore les différences dans le système de coordonnées utilisé dans SkiaSharp et Xamarin.Forms. Vous pouvez obtenir des informations à convertir entre les deux systèmes de coordonnées et dessiner des graphiques qui remplissent une zone particulière :

Ovale qui remplit l’écran

Si vous avez été en Xamarin.Forms programmation depuis un certain temps, vous pouvez avoir une impression de Xamarin.Forms coordonnées et de tailles. Les cercles dessinés dans les deux articles précédents peuvent sembler un peu petits pour vous.

Ces cercles sont petits par rapport aux Xamarin.Forms tailles. Par défaut, SkiaSharp dessine en unités de pixels tandis que Xamarin.Forms les coordonnées et tailles de base sur une unité indépendante de l’appareil établie par la plateforme sous-jacente. (Pour plus d’informations sur le système de coordonnées, consultez le Xamarin.Forms chapitre 5. Gestion des tailles du livre Création d’applications mobiles avec Xamarin.Forms.)

La page de l’exemple de programme intitulé Surface Size utilise la sortie de texte SkiaSharp pour afficher la taille de l’aire d’affichage à partir de trois sources différentes :

  • Xamarin.FormsWidth Normal et Height propriétés de l’objetSKCanvasView.
  • Propriété CanvasSize de l'objet SKCanvasView.
  • Propriété Size de la SKImageInfo valeur, qui est cohérente avec les Width Height propriétés utilisées dans les deux pages précédentes.

La SurfaceSizePage classe montre comment afficher ces valeurs. Le constructeur enregistre l’objet SKCanvasView en tant que champ, afin qu’il soit accessible dans le PaintSurface gestionnaire d’événements :

SKCanvasView canvasView;

public SurfaceSizePage()
{
    Title = "Surface Size";

    canvasView = new SKCanvasView();
    canvasView.PaintSurface += OnCanvasViewPaintSurface;
    Content = canvasView;
}

SKCanvas comprend six méthodes différentes DrawText , mais cette DrawText méthode est la plus simple :

public void DrawText (String text, Single x, Single y, SKPaint paint)

Vous spécifiez la chaîne de texte, les coordonnées X et Y où le texte doit commencer et un SKPaint objet. La coordonnée X spécifie où le côté gauche du texte est positionné, mais attention : la coordonnée Y spécifie la position de la ligne de base du texte. Si vous avez déjà écrit à la main sur du papier aligné, la ligne de base est la ligne sur laquelle les caractères s’assoient et sous lesquels descendent les décroissants (tels que ceux des lettres g, p, q et y) descendent.

L’objet SKPaint vous permet de spécifier la couleur du texte, la famille de polices et la taille du texte. Par défaut, la TextSize propriété a la valeur 12, ce qui entraîne un texte minuscule sur des appareils haute résolution tels que les téléphones. Dans les applications les plus simples, vous aurez également besoin d’informations sur la taille du texte que vous affichez. La SKPaint classe définit une FontMetrics propriété et plusieurs MeasureText méthodes, mais pour des besoins moins sophistiqués, la FontSpacing propriété fournit une valeur recommandée pour l’espacement des lignes successives de texte.

Le gestionnaire suivant PaintSurface crée un SKPaint objet pour un TextSize objet de 40 pixels, qui correspond à la hauteur verticale souhaitée du texte du haut des croissants au bas des décroissants. La FontSpacing valeur retournée par l’objet SKPaint est un peu plus grande que celle-ci, soit environ 47 pixels.

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    SKPaint paint = new SKPaint
    {
        Color = SKColors.Black,
        TextSize = 40
    };

    float fontSpacing = paint.FontSpacing;
    float x = 20;               // left margin
    float y = fontSpacing;      // first baseline
    float indent = 100;

    canvas.DrawText("SKCanvasView Height and Width:", x, y, paint);
    y += fontSpacing;
    canvas.DrawText(String.Format("{0:F2} x {1:F2}",
                                  canvasView.Width, canvasView.Height),
                    x + indent, y, paint);
    y += fontSpacing * 2;
    canvas.DrawText("SKCanvasView CanvasSize:", x, y, paint);
    y += fontSpacing;
    canvas.DrawText(canvasView.CanvasSize.ToString(), x + indent, y, paint);
    y += fontSpacing * 2;
    canvas.DrawText("SKImageInfo Size:", x, y, paint);
    y += fontSpacing;
    canvas.DrawText(info.Size.ToString(), x + indent, y, paint);
}

La méthode commence la première ligne de texte avec une coordonnée X de 20 (pour une petite marge à gauche) et une coordonnée Y de fontSpacing, qui est un peu plus que ce qui est nécessaire pour afficher la hauteur complète du premier trait de texte en haut de la surface d’affichage. Après chaque appel à DrawText, la coordonnée Y est augmentée par un ou deux incréments de fontSpacing.

Voici le programme en cours d’exécution :

Captures d’écran montrant l’application Surface Size s’exécutant sur deux appareils mobiles.

Comme vous pouvez le voir, la CanvasSize propriété et SKCanvasView la Size propriété de la SKImageInfo valeur sont cohérentes dans la création de rapports sur les dimensions de pixels. Les Height propriétés et Width les propriétés sont Xamarin.Forms SKCanvasView des propriétés et signalent la taille de la vue dans les unités indépendantes de l’appareil définies par la plateforme.

Le simulateur iOS sept sur la gauche a deux pixels par unité indépendante de l’appareil, et Android Nexus 5 dans le centre a trois pixels par unité. C’est pourquoi le cercle simple présenté précédemment a des tailles différentes sur différentes plateformes.

Si vous préférez travailler entièrement dans des unités indépendantes de l’appareil, vous pouvez le faire en définissant la IgnorePixelScaling propriété sur trueSKCanvasView . Toutefois, vous n’aurez peut-être pas l’impression des résultats. SkiaSharp restitue les graphiques sur une surface d’appareil plus petite, avec une taille de pixel égale à la taille de la vue dans les unités indépendantes de l’appareil. (Par exemple, SkiaSharp utilise une surface d’affichage de 360 x 512 pixels sur le Nexus 5.) Il effectue ensuite un scale-up de cette image en taille, ce qui entraîne des jaggies bitmap notables.

Pour maintenir la même résolution d’images, une meilleure solution consiste à écrire vos propres fonctions simples pour effectuer une conversion entre les deux systèmes de coordonnées.

En plus de la DrawCircle méthode, SKCanvas définit également deux DrawOval méthodes qui dessinent un ellipse. Un ellipse est défini par deux rayons plutôt qu’un rayon unique. Il s’agit du rayon principal et du rayon mineur. La DrawOval méthode dessine un ellipse avec les deux rayons parallèles aux axes X et Y. (Si vous devez dessiner un ellipse avec des axes qui ne sont pas parallèles aux axes X et Y, vous pouvez utiliser une transformation de rotation comme indiqué dans l’article La transformation de rotation ou un chemin graphique, comme indiqué dans l’article Trois façons de dessiner un arc. Cette surcharge de la DrawOval méthode nomme les deux paramètres rx radii et ry indique qu’ils sont parallèles aux axes X et Y :

public void DrawOval (Single cx, Single cy, Single rx, Single ry, SKPaint paint)

Est-il possible de dessiner un ellipse qui remplit la surface d’affichage ? La page Ellipse Fill montre comment procéder. Le PaintSurface gestionnaire d’événements de la classe EllipseFillPage.xaml.cs soustrait la moitié de la largeur du trait à partir des valeurs et yRadius à l’ensemble xRadius des points de suspension et de son contour dans la surface d’affichage :

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    float strokeWidth = 50;
    float xRadius = (info.Width - strokeWidth) / 2;
    float yRadius = (info.Height - strokeWidth) / 2;

    SKPaint paint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Blue,
        StrokeWidth = strokeWidth
    };
    canvas.DrawOval(info.Width / 2, info.Height / 2, xRadius, yRadius, paint);
}

Ici, il est en cours d’exécution :

Captures d’écran montrant l’application Ellipse Fill s’exécutant sur deux appareils mobiles.

L’autre DrawOval méthode a un SKRect argument, qui est un rectangle défini en termes de coordonnées X et Y de son angle supérieur gauche et du coin inférieur droit. L’ovale remplit ce rectangle, ce qui suggère qu’il peut être possible de l’utiliser dans la page Ellipse Fill comme suit :

SKRect rect = new SKRect(0, 0, info.Width, info.Height);
canvas.DrawOval(rect, paint);

Toutefois, cela tronque tous les bords du contour de l’ellipse sur les quatre côtés. Vous devez ajuster tous les arguments du SKRect constructeur en fonction de ce strokeWidth qui suit :

SKRect rect = new SKRect(strokeWidth / 2,
                         strokeWidth / 2,
                         info.Width - strokeWidth / 2,
                         info.Height - strokeWidth / 2);
canvas.DrawOval(rect, paint);