共用方式為


像素與裝置獨立單位

探索 SkiaSharp 座標與 Xamarin.Forms 座標之間的差異

本文探討 SkiaSharp 和 Xamarin.Forms中使用的座標系統差異。 您可以取得兩個座標系統之間轉換的資訊,也可以繪製填滿特定區域的圖形:

填滿螢幕的橢圓形

如果您已經進行 Xamarin.Forms 過一段時間的程序設計,您可能會覺得 Xamarin.Forms 座標和大小。 前兩篇文章中繪製的圓圈可能有點小。

與大小相比Xamarin.Forms,這些圓形很小。 根據預設,SkiaSharp 會以像素為單位繪製,而 Xamarin.Forms 基礎平臺所建立的裝置獨立單位上的基底座標和大小。 (如需座標系統的詳細資訊Xamarin.Forms,請參閱第五章。處理使用 建立行動應用程式Xamarin.Forms之書籍的大小

標題 為 Surface Size 之範例程式中的頁面會使用 SkiaSharp 文字輸出來顯示來自三個不同來源的顯示介面大小:

  • 物件的一般 Xamarin.FormsWidthHeight 屬性 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,這會導致手機等高解析度裝置上的小文字。 除了最簡單的應用程式之外,您還需要一些有關所顯示文字大小的資訊。 類別 SKPaintFontMetrics 定義屬性和數 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

以下是程式執行情況:

螢幕快照顯示兩個行動裝置上執行的 Surface Size 應用程式。

如您所見, CanvasSize 和 值的 屬性 SKCanvasView Size SKImageInfo 在報告圖元維度時是一致的。 的 HeightWidth 屬性是Xamarin.Forms屬性SKCanvasView,並報告平臺所定義之裝置獨立單位中的檢視大小。

左邊的 iOS 七個模擬器每個裝置獨立單位有兩個圖元,而中央的 Android Nexus 5 每單位有三個圖元。 這就是為什麼先前顯示的簡單圓形在不同平臺上有不同的大小。

如果您想要完全在裝置獨立單位中工作,您可以將 的 SKCanvasView 屬性設定IgnorePixelScalingtrue來執行此動作。 不過,您可能不喜歡結果。 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);
}

這裡正在執行:

螢幕快照顯示兩個行動裝置上執行的Ellipse Fill應用程式。

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