Teilen über


Pixel und geräteunabhängige Einheiten

Entdecken Sie die Unterschiede zwischen SkiaSharp-Koordinaten und Xamarin.Forms Koordinaten

In diesem Artikel werden die Unterschiede im Koordinatensystem untersucht, das in SkiaSharp und Xamarin.Forms. Sie können Informationen abrufen, um zwischen den beiden Koordinatensystemen zu konvertieren, und grafiken zeichnen, die einen bestimmten Bereich ausfüllen:

Ein Oval, das den Bildschirm ausfüllt

Wenn Sie eine Weile programmieren Xamarin.Forms , haben Sie möglicherweise ein Gefühl für Xamarin.Forms Koordinaten und Größen. Die in den beiden vorherigen Artikeln gezeichneten Kreise scheinen ihnen vielleicht ein wenig klein zu sein.

Diese Kreise sind im Vergleich zu Xamarin.Forms Größen klein. Standardmäßig zeichnet SkiaSharp in Pixeleinheiten, während Xamarin.Forms Koordinaten und Größen auf einer geräteunabhängigen Einheit basieren, die von der zugrunde liegenden Plattform erstellt wird. (Weitere Informationen zum Xamarin.Forms Koordinatensystem finden Sie in Kapitel 5. Umgang mit Größen des Buchs Erstellen mobiler Apps mit Xamarin.Forms.)

Die Seite im Beispielprogramm mit dem Titel Surface Size verwendet die Textausgabe SkiaSharp, um die Größe der Anzeigeoberfläche aus drei verschiedenen Quellen anzuzeigen:

  • Die Normalen Xamarin.FormsWidth und Height Eigenschaften des SKCanvasView Objekts.
  • Die CanvasSize-Eigenschaft des SKCanvasView-Objekts.
  • Die Size Eigenschaft des SKImageInfo Werts, der mit den Width Height in den beiden vorherigen Seiten verwendeten Eigenschaften konsistent ist.

Die SurfaceSizePage Klasse zeigt, wie diese Werte angezeigt werden. Der Konstruktor speichert das SKCanvasView Objekt als Feld, sodass im PaintSurface Ereignishandler darauf zugegriffen werden kann:

SKCanvasView canvasView;

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

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

SKCanvas enthält sechs verschiedene DrawText Methoden, aber diese DrawText Methode ist die einfachste:

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

Sie geben die Textzeichenfolge, die X- und Y-Koordinaten an, an der der Text beginnen soll, und ein SKPaint Objekt. Die X-Koordinate gibt an, wo die linke Seite des Texts positioniert ist, aber achten Sie darauf: Die Y-Koordinate gibt die Position des Basisplans des Texts an. Wenn Sie jemals von Hand auf gezeilen Papier geschrieben haben, ist der Basisplan die Zeile, auf der sich die Zeichen befinden, und darunter untergeordnete Elemente (z. B. die buchstaben g, p, q und y) absteigend.

Mit SKPaint dem Objekt können Sie die Farbe des Texts, der Schriftfamilie und der Textgröße angeben. Standardmäßig hat die TextSize Eigenschaft den Wert 12, was zu winzigem Text auf hochauflösenden Geräten wie Smartphones führt. In den einfachsten Anwendungen benötigen Sie auch einige Informationen zur Größe des Texts, den Sie anzeigen. Die SKPaint Klasse definiert eine FontMetrics Eigenschaft und mehrere MeasureText Methoden, aber für weniger ausgefallene Anforderungen stellt die FontSpacing Eigenschaft einen empfohlenen Wert für den Abstand zwischen aufeinander folgenden Textzeilen bereit.

Der folgende PaintSurface Handler erstellt ein SKPaint Objekt für eine TextSize Von 40 Pixel, bei der es sich um die gewünschte vertikale Höhe des Texts vom oberen Rand der Aufsteigenden bis zum Ende der Absteigenden handelt. Der FontSpacing wert, den das SKPaint Objekt zurückgibt, ist etwas größer als das, etwa 47 Pixel.

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);
}

Die Methode beginnt die erste Textzeile mit einer X-Koordinate von 20 (für einen kleinen Rand links) und einer Y-Koordinate von fontSpacing, der etwas mehr ist als das, was erforderlich ist, um die volle Höhe der ersten Textzeile am oberen Rand der Anzeigeoberfläche anzuzeigen. Nach jedem Aufruf DrawTextwird die Y-Koordinate um ein oder zwei Inkremente fontSpacingerhöht.

Dies ist das Programm, das ausgeführt wird:

Screenshots zeigen die Surface Size-App, die auf zwei mobilen Geräten ausgeführt wird.

Wie Sie sehen können, sind die CanvasSize Eigenschaft der SKCanvasView Werte und die Size Eigenschaft des SKImageInfo Werts konsistent, wenn die Pixelabmessungen gemeldet werden. Die Height Eigenschaften und Width Eigenschaften sind SKCanvasView Xamarin.Forms Eigenschaften und melden die Größe der Ansicht in den geräteunabhängigen Einheiten, die von der Plattform definiert werden.

Der iOS sieben Simulator auf der linken Seite verfügt über zwei Pixel pro geräteunabhängiger Einheit, und das Android Nexus 5 in der Mitte hat drei Pixel pro Einheit. Aus diesem Grund hat der zuvor gezeigte einfache Kreis unterschiedliche Größen auf verschiedenen Plattformen.

Wenn Sie lieber vollständig in geräteunabhängigen Einheiten arbeiten möchten, können Sie dies tun, indem Sie die IgnorePixelScaling Eigenschaft des Werts auf "SKCanvasViewtrue. Sie mögen die Ergebnisse jedoch möglicherweise nicht. SkiaSharp rendert die Grafiken auf einer kleineren Geräteoberfläche mit einer Pixelgröße, die der Größe der Ansicht in geräteunabhängigen Einheiten entspricht. (Beispielsweise würde SkiaSharp eine Anzeigeoberfläche von 360 x 512 Pixel auf dem Nexus 5 verwenden.) Anschließend wird das Bild in der Größe skaliert, was zu spürbaren Bitmap-Jaggies führt.

Um die gleiche Bildauflösung aufrechtzuerhalten, besteht eine bessere Lösung darin, ihre eigenen einfachen Funktionen zu schreiben, um zwischen den beiden Koordinatensystemen zu konvertieren.

Zusätzlich zur DrawCircle Methode werden auch zwei DrawOval Methoden definiert, SKCanvas die eine Ellipse zeichnen. Eine Ellipse wird durch zwei Radien und nicht durch einen einzelnen Radius definiert. Diese werden als Hauptradius und hilfsradius bezeichnet. Die DrawOval Methode zeichnet eine Ellipse mit den beiden Radien parallel zu den X- und Y-Achsen. (Wenn Sie eine Ellipse mit Achsen zeichnen müssen, die nicht parallel zu den X- und Y-Achsen sind, können Sie eine Drehungstransformation verwenden, wie im Artikel beschrieben.Der Transformationspfad "Drehen" oder "Grafikpfad", wie im Artikel "Drei Möglichkeiten zum Zeichnen eines Bogens" beschrieben. Diese Überladung der DrawOval Methode benennt die beiden Bogenmaßparameter rx und ry gibt an, dass sie parallel zu den X- und Y-Achsen sind:

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

Ist es möglich, eine Ellipse zu zeichnen, die die Anzeigeoberfläche ausfüllt? Die Ellipse-Füllungsseite veranschaulicht, wie das geht. Der PaintSurface Ereignishandler in der EllipseFillPage.xaml.cs-Klasse subtrahiert die Hälfte der Strichbreite von den xRadius WertenyRadius, um die gesamte Auslassungspunkte und die Gliederung innerhalb der Anzeigeoberfläche anzupassen:

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);
}

Hier wird folgendes ausgeführt:

Screenshots zeigen die Ellipse Fill-App, die auf zwei mobilen Geräten ausgeführt wird.

Die andere DrawOval Methode weist ein SKRect Argument auf, bei dem es sich um ein Rechteck handelt, das in Bezug auf die X- und Y-Koordinaten der oberen linken Ecke und der unteren rechten Ecke definiert ist. Die ovalen Füllungen dieses Rechtecks, was nahelegt, dass es möglich sein könnte, es auf der Ellipse Fill-Seite wie folgt zu verwenden:

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

Dadurch werden jedoch alle Ränder der Ellipse auf den vier Seiten abgeschnitten. Sie müssen alle SKRect Konstruktorargumente entsprechend anpassen strokeWidth , um diese Arbeit richtig zu gestalten:

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