像素與裝置獨立單位
探索 SkiaSharp 座標與 Xamarin.Forms 座標之間的差異
本文探討 SkiaSharp 和 Xamarin.Forms中使用的座標系統差異。 您可以取得兩個座標系統之間轉換的資訊,也可以繪製填滿特定區域的圖形:
如果您已經進行 Xamarin.Forms 過一段時間的程序設計,您可能會覺得 Xamarin.Forms 座標和大小。 前兩篇文章中繪製的圓圈可能有點小。
與大小相比Xamarin.Forms,這些圓形很小。 根據預設,SkiaSharp 會以像素為單位繪製,而 Xamarin.Forms 基礎平臺所建立的裝置獨立單位上的基底座標和大小。 (如需座標系統的詳細資訊Xamarin.Forms,請參閱第五章。處理使用 建立行動應用程式Xamarin.Forms之書籍的大小。
標題 為 Surface Size 之範例程式中的頁面會使用 SkiaSharp 文字輸出來顯示來自三個不同來源的顯示介面大小:
- 物件的一般 Xamarin.Forms
Width
和Height
屬性SKCanvasView
。 - 對象的
CanvasSize
屬性SKCanvasView
。 - 值的
Size
屬性SKImageInfo
,與前兩頁所使用的 和Height
屬性一致Width
。
類別 SurfaceSizePage
示範如何顯示這些值。 建構函式會將 SKCanvasView
物件儲存為欄位,因此可以在事件處理程式中 PaintSurface
存取:
SKCanvasView canvasView;
public SurfaceSizePage()
{
Title = "Surface Size";
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
SKCanvas
包含六種不同的 DrawText
方法,但此方法 DrawText
是最簡單的方法:
public void DrawText (String text, Single x, Single y, SKPaint paint)
您可以指定文字字串、要開始文字的 X 和 Y 座標,以及 SKPaint
物件。 X 座標會指定文字左邊的位置,但注意:Y 座標會指定文字基準的位置。 如果您曾經用手寫在折線紙上,則基準是字元所在的行,以及下方的下階字元(例如字母 g、p、q 和 y 上的遞減。
物件 SKPaint
可讓您指定文字、字型系列和文字大小的色彩。 根據預設, TextSize
屬性的 值為12,這會導致手機等高解析度裝置上的小文字。 除了最簡單的應用程式之外,您還需要一些有關所顯示文字大小的資訊。 類別 SKPaint
會 FontMetrics
定義屬性和數 MeasureText
種方法,但對於較不花哨的需求,屬性 FontSpacing
會為連續文字行間距提供建議值。
下列 PaintSurface
處理程式會 SKPaint
為 40 像素的 TextSize
建立 物件,這是從遞增器頂端到下階的文字所需垂直高度。 FontSpacing
對象傳回的值SKPaint
稍大一點,約 47 圖元。
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);
}
方法會以 X 座標 20(左邊為一點邊界)和 的 Y 座標 fontSpacing
開始第一行文字,這比顯示介面頂端第一行文字的完整高度所需多一點。 每次呼叫 DrawText
之後,Y 座標就會增加一或兩個遞增 fontSpacing
。
以下是程式執行情況:
如您所見, CanvasSize
和 值的 屬性 SKCanvasView
Size
SKImageInfo
在報告圖元維度時是一致的。 的 Height
和 Width
屬性是Xamarin.Forms屬性SKCanvasView
,並報告平臺所定義之裝置獨立單位中的檢視大小。
左邊的 iOS 七個模擬器每個裝置獨立單位有兩個圖元,而中央的 Android Nexus 5 每單位有三個圖元。 這就是為什麼先前顯示的簡單圓形在不同平臺上有不同的大小。
如果您想要完全在裝置獨立單位中工作,您可以將 的 SKCanvasView
屬性設定IgnorePixelScaling
為 true
來執行此動作。 不過,您可能不喜歡結果。 SkiaSharp 會在較小的裝置表面上呈現圖形,其圖元大小等於裝置獨立單位的檢視大小。 (例如,SkiaSharp 會在 Nexus 5 上使用 360 x 512 像素的顯示表面。然後,它會相應增加該影像的大小,導致明顯的位圖鋸齒狀。
為了維持相同的影像解析度,更好的解決方案是撰寫您自己的簡單函式,以在兩個座標系統之間轉換。
除了 DrawCircle
方法之外, SKCanvas
也會定義繪製橢圓形的兩 DrawOval
種方法。 橢圓形是由兩個弧度而不是單一半徑所定義。 這些稱為 主要半徑 和 次要半徑。 方法 DrawOval
會繪製橢圓形,其中兩個弧度與 X 和 Y 軸平行。 (如果您需要使用與 X 和 Y 軸不平行的軸繪製橢圓形,您可以使用旋轉轉換,如本文所述旋轉轉換或圖形路徑,如繪製Arc的三種方式一文所述。 方法的 DrawOval
這個多載會命名兩個 radii 參數 rx
,並 ry
指出它們與 X 和 Y 軸平行:
public void DrawOval (Single cx, Single cy, Single rx, Single ry, SKPaint paint)
是否可以繪製填滿顯示表面的橢圓形? 省 略號填滿 頁面會示範如何。 EllipseFillPage.xaml.cs 類別中的事件處理程式會從 和 yRadius
值減去筆劃寬度xRadius
的一半,以符合顯示介面內的整個橢圓及其外框:PaintSurface
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);
}
這裡正在執行:
另 DrawOval
一個方法有自 SKRect
變數,這是以左上角和右下角的 X 和 Y 座標來定義的矩形。 橢圓形會填滿該矩形,這表示可能會在橢圓形填滿頁面中使用它,如下所示:
SKRect rect = new SKRect(0, 0, info.Width, info.Height);
canvas.DrawOval(rect, paint);
不過,這會截斷四側橢圓形外框的所有邊緣。 您必須根據 strokeWidth
來調整所有SKRect
建構函式自變數,讓此工作正確:
SKRect rect = new SKRect(strokeWidth / 2,
strokeWidth / 2,
info.Width - strokeWidth / 2,
info.Height - strokeWidth / 2);
canvas.DrawOval(rect, paint);