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:
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.Forms
Width
undHeight
Eigenschaften desSKCanvasView
Objekts. - Die
CanvasSize
-Eigenschaft desSKCanvasView
-Objekts. - Die
Size
Eigenschaft desSKImageInfo
Werts, der mit denWidth
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 DrawText
wird die Y-Koordinate um ein oder zwei Inkremente fontSpacing
erhöht.
Dies ist das Programm, das 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 "SKCanvasView
true
. 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:
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);