Unidades independentes de dispositivo e pixels
Explore as diferenças entre coordenadas e Xamarin.Forms coordenadas do SkiaSharp
Este artigo explora as diferenças no sistema de coordenadas usado no SkiaSharp e Xamarin.Formsno . Você pode obter informações para converter entre os dois sistemas de coordenadas e também desenhar gráficos que preencham uma área específica:
Se você já está programando há Xamarin.Forms algum tempo, pode ter uma ideia de Xamarin.Forms coordenadas e tamanhos. Os círculos desenhados nos dois artigos anteriores podem parecer um pouco pequenos para você.
Esses círculos são pequenos em comparação com Xamarin.Forms os tamanhos. Por padrão, o SkiaSharp desenha em unidades de pixels enquanto Xamarin.Forms baseia coordenadas e tamanhos em uma unidade independente do dispositivo estabelecida pela plataforma subjacente. (Mais informações sobre o sistema de Xamarin.Forms coordenadas podem ser encontradas no Capítulo 5. Lidando com tamanhos do livro Criando aplicativos móveis com Xamarin.Forms.)
A página no programa de amostra intitulado Tamanho da superfície usa a saída de texto do SkiaSharp para mostrar o tamanho da superfície de exibição de três fontes diferentes:
- A normal Xamarin.Forms
Width
eHeight
as propriedades doSKCanvasView
objeto. - A propriedade
CanvasSize
do objetoSKCanvasView
. - A
Size
propriedade doSKImageInfo
valor, que é consistente com asWidth
propriedades eHeight
usadas nas duas páginas anteriores.
A SurfaceSizePage
classe mostra como exibir esses valores. O construtor salva o SKCanvasView
objeto como um campo, para que ele possa ser acessado no manipulador de PaintSurface
eventos:
SKCanvasView canvasView;
public SurfaceSizePage()
{
Title = "Surface Size";
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
SKCanvas
Inclui seis métodos diferentes DrawText
, mas este DrawText
método é o mais simples:
public void DrawText (String text, Single x, Single y, SKPaint paint)
Você especifica a cadeia de caracteres de texto, as coordenadas X e Y onde o texto deve começar e um SKPaint
objeto. A coordenada X especifica onde o lado esquerdo do texto está posicionado, mas cuidado: A coordenada Y especifica a posição da linha de base do texto. Se você já escreveu à mão em papel pautado, a linha de base é a linha na qual os caracteres ficam e abaixo da qual os descendentes (como os das letras g, p, q e y) descem.
O SKPaint
objeto permite especificar a cor do texto, a família de fontes e o tamanho do texto. Por padrão, a propriedade tem um valor de 12, o TextSize
que resulta em texto minúsculo em dispositivos de alta resolução, como telefones. Em qualquer coisa, exceto nos aplicativos mais simples, você também precisará de algumas informações sobre o tamanho do texto que está exibindo. A SKPaint
classe define uma FontMetrics
propriedade e vários MeasureText
métodos, mas para necessidades menos sofisticadas, a FontSpacing
propriedade fornece um valor recomendado para espaçar linhas sucessivas de texto.
O manipulador a seguir PaintSurface
cria um SKPaint
objeto para um TextSize
de 40 pixels, que é a altura vertical desejada do texto da parte superior dos ascendentes até a parte inferior dos descendentes. O FontSpacing
valor que o objeto retorna é um pouco maior que isso SKPaint
, cerca de 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);
}
O método começa a primeira linha de texto com uma coordenada X de 20 (para uma pequena margem à esquerda) e uma coordenada Y de , que é um pouco mais do fontSpacing
que o necessário para exibir a altura total da primeira linha de texto na parte superior da superfície de exibição. Após cada chamada para DrawText
, a coordenada Y é aumentada em um ou dois incrementos de fontSpacing
.
Este é o programa em execução:
Como você pode ver, a CanvasSize
propriedade do e a SKCanvasView
Size
propriedade do valor são consistentes no relatório das dimensões em SKImageInfo
pixels. As Height
propriedades e Width
das SKCanvasView
propriedades are Xamarin.Forms e relatam o tamanho da exibição nas unidades independentes de dispositivo definidas pela plataforma.
O simulador iOS sete à esquerda tem dois pixels por unidade independente de dispositivo e o Android Nexus 5 no centro tem três pixels por unidade. É por isso que o círculo simples mostrado anteriormente tem tamanhos diferentes em plataformas diferentes.
Se preferir trabalhar inteiramente em unidades independentes de dispositivo, você pode fazer isso definindo a IgnorePixelScaling
propriedade do SKCanvasView
.true
No entanto, você pode não gostar dos resultados. O SkiaSharp renderiza os gráficos em uma superfície de dispositivo menor, com um tamanho de pixel igual ao tamanho da exibição em unidades independentes do dispositivo. (Por exemplo, o SkiaSharp usaria uma superfície de exibição de 360 x 512 pixels no Nexus 5.) Em seguida, ele aumenta o tamanho da imagem, resultando em imperfeições de bitmap perceptíveis.
Para manter a mesma resolução de imagem, uma solução melhor é escrever suas próprias funções simples para converter entre os dois sistemas de coordenadas.
Além do DrawCircle
método, SKCanvas
também define dois DrawOval
métodos que desenham uma elipse. Uma elipse é definida por dois raios em vez de um único raio. Estes são conhecidos como raio maior e raio menor. O DrawOval
método desenha uma elipse com os dois raios paralelos aos eixos X e Y. (Se você precisar desenhar uma elipse com eixos que não são paralelos aos eixos X e Y, poderá usar uma transformação de rotação conforme discutido no artigo A Transformação de rotação ou um caminho gráfico, conforme discutido no artigo Três maneiras de desenhar um arco). Essa sobrecarga do método nomeia os dois parâmetros rx
de DrawOval
raios e ry
indica que eles são paralelos aos eixos X e Y:
public void DrawOval (Single cx, Single cy, Single rx, Single ry, SKPaint paint)
É possível desenhar uma elipse que preencha a superfície da tela? A página Preenchimento de elipse demonstra como. O PaintSurface
manipulador de eventos na classe EllipseFillPage.xaml.cs subtrai metade da largura do traço dos xRadius
valores e yRadius
para ajustar toda a elipse e seu contorno dentro da superfície de exibição:
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);
}
Aqui está rodando:
O outro DrawOval
método tem um SKRect
argumento, que é um retângulo definido em termos das coordenadas X e Y de seu canto superior esquerdo e canto inferior direito. O oval preenche esse retângulo, o que sugere que pode ser possível usá-lo na página Preenchimento de elipse assim:
SKRect rect = new SKRect(0, 0, info.Width, info.Height);
canvas.DrawOval(rect, paint);
No entanto, isso trunca todas as bordas do contorno da elipse nos quatro lados. Você precisa ajustar todos os argumentos do SKRect
construtor com base no strokeWidth
para que isso funcione corretamente:
SKRect rect = new SKRect(strokeWidth / 2,
strokeWidth / 2,
info.Width - strokeWidth / 2,
info.Height - strokeWidth / 2);
canvas.DrawOval(rect, paint);