I need to be able to do some direct drawing in Xamarin Forms. Is there a control equivelent to a canvas? I saw somewhere that maybe the AbsoluteLayout was, but I need something that I can, for example, execute a DrawString or DrawArc on. Does such a thing exist, or do you have to create a custom control?
Try with SkiaSharp, it basically allows you to do more or less the same than you can do in the native canvas of Android.
To use it:
Add the NuGet package SkiaSharp.Views.Forms to your projects.
Add the Skia namespace to your page or layout:
xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
Add the Skia canvas view, defining the name of the callback method that will be used to draw on it from the code behind:
<skia:SKCanvasView
x:Name="CanvasView"
PaintSurface="OnCanvasViewPaintSurface"/>
Draw whatever you want:
private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
var info = args.Info;
var surface = args.Surface;
var canvas = surface.Canvas;
canvas.Clear();
// In this example, we will draw a circle in the middle of the canvas
var paint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = Color.Red.ToSKColor(), // Alternatively: SKColors.Red
};
canvas.DrawCircle(info.Width / 2, info.Height / 2, 100, paint);
}
Related
I am using processing 3, and trying to implement an interactive map via gicentre GeoMap library. I have got the U.S. map shown and the hovering feature work i.e. highlight the hovering state. I am wondering is there any way I can zoom into the state in this GeoMap library. Maybe a mouseClick or a mouseMove event to trigger this function. I am not sure how to redraw the map to make it zoom into the selected state. Here is my starting code:
import org.gicentre.geomap.*;
GeoMap geoMap;
int id = -1;
void setup()
{
size(800, 400);
geoMap = new GeoMap(this); // Create the geoMap object.
geoMap.readFile("usContinental"); // Read shapefile.
}
void draw()
{
background(202, 226, 245); // Ocean colour
stroke(0,40); // Boundary colour
fill(206,173,146); // Land colour
//if (id == -1) {
geoMap.draw(); // Draw the entire map.
//} else {
// geoMap.draw(id);
//}
// Find the country at mouse position and draw in different color.
id = geoMap.getID(mouseX, mouseY);
if (id != -1)
{
fill(180, 120, 120); // Highlighted land colour.
geoMap.draw(id);
}
}
Any idea? Thanks!
Questions like these are best answered by looking at the docs for the library. Here are the docs for the giCentre geoMap library.
According to that, this library basically just shows shape files, without any fancy logic for zooming the map. You could implement this yourself using the scale() function or the camera functions. But you might be best off just finding a library that supports changing the zoom out of the box.
I'm new with Xamarin. I'm actually trying to set the background image of a view and stretch it.
The image is a 2048x1536 pixels png.
nfloat vpHeight = View.Bounds.Height;
nfloat vpWidth = View.Bounds.Width;
Console.WriteLine(vpWidth);
Console.WriteLine(vpHeight);
The above code will return me 1024x768 (it's a landscape position).
var img = UIImage.FromFile("pages/p1.png");
UIImageView imgView = new UIImageView(img);
imgView.ContentMode = UIViewContentMode.ScaleAspectFit;
var prevPage = new UIView()
{
Frame = new CoreGraphics.CGRect(0, 0, vpWidth, vpHeight)
};
prevPage.Add(imgView);
this is the code where I set the background, but the result is just the half of the image in x and y just like the image bellow:
So, how to make the image to adjust to the width and height of the view ?
ty !!
I would create an UIImageView as a background like so:
var img = UIImage.FromFile("pages/p1.png");
UIImageView imgView = new UIImageView(img);
imgView.ContentMode = UIViewContentMode.ScaleAspectFit;
then add this to whatever view you are using.
ContentMode can be used like so:
Update
I would add it to the prevPage like so:
var prevPage = new UIView()
{
Frame = new CoreGraphics.CGRect(0, 0, vpWidth, vpHeight)
};
var img = UIImage.FromFile("pages/p1.png");
UIImageView imgView = new UIImageView(new CGRect(0,0,vpWidth,vpHeight));
imgView.Image = img;
imgView.ContentMode = UIViewContentMode.ScaleAspectFit; // or ScaleAspectFill
prevPage.Add(imgView);
Also its worth noting that using the View.Bounds to position the view is bit clunky. I would take a look into Autolayout as you will encounter problems on different devices and orientations. These are some good tutorials on Autolayout they might be native code but you are looking for the relationships of the views rather than the code.
Raywenderlich tutorial
Other Tutorial
Any probs with autolayout just ask another question.
I would recommend you stay away from FromPatternImage unless you are really using a pattern.
For the lowest memory consumption and best UI performance, this is what I do:
1st) Resize your image using an image context to match the size of the view:
UIGraphics.BeginImageContext(View.Frame.Size);
UIImage.FromBundle("bg.jpg").Draw(View.Bounds);
var bgImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
2nd) Display the resized image in a UIImageView and send it to the lowest Z-order:
var uiImageView = new UIImageView(View.Frame);
uiImageView.Image = bgImage;
View.AddSubview(uiImageView);
View.SendSubviewToBack(uiImageView);
I'm just playing around with Xamarin Forms and SkiaSharp. I've create a simple Xamarin forms ContentView with a SkiaCanvas. My PaintSurface is as follows to draw a simple green circle:
private void canvas_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
var view = (SKCanvasView)sender;
var canvas = e.Surface.Canvas;
using(var paint = new SKPaint())
{
paint.Color = Colours.XamarinGreen;
paint.Style = SKPaintStyle.Fill;
paint.StrokeWidth = 1;
paint.IsAntialias = true;
canvas.DrawCircle((float)view.Width / 2.0f, (float)view.Height / 2.0f, (float)view.Height / 2.0f, paint);
}
}
In my test app, when I resize the window by dragging the corner of the window the circle flickers. I was wondering is there an easy way to enable double buffering when drawing graphics with SkiaSharp in a Xamarin.Forms app?
I am working on an app in which images are flying on the Screen.
I need to implement:
Hold onto any of the flying images on Tap
Drag the image to certain position of the user's choice by letting the user hold it.
Here is another easy way to do dragging.
Just draw your image (Texture2d) with respect to a Rectangle instead of Vector2.
Your image variables should look like this
Texture2d image;
Rectangle imageRect;
Draw your image with respect to "imageRect" in Draw() method.
spriteBatch.Draw(image,imageRect,Color.White);
Now in Update() method handle your image with single touch input.
//Move your image with your logic
TouchCollection touchLocations = TouchPanel.GetState();
foreach(TouchLocation touchLocation in touchLocations)
{
Rectangle touchRect = new Rectangle
(touchLocation.Position.X,touchLocation.Position.Y,10,10);
if(touchLocation.State == TouchLocationState.Moved
&& imageRect.Intersects(touchRect))
{
imageRect.X = touchRect.X;
imageRect.Y = touchRect.Y;
}
//you can bring more beauty by bringing centre point
//of imageRect instead of initial point by adding width
//and height to X and Y respectively and divide it by 2
There's a drag-and-drag example in XNA here: http://geekswithblogs.net/mikebmcl/archive/2011/03/27/drag-and-drop-in-a-windows-xna-game.aspx
When you load your image in, you'll need a BoundingBox or Rectangle Object to control where it is.
So, in the XNA app on your phone, you should have a couple of objects declared for your texture.
Texture2D texture;
BoundingBox bBox;
Vector2 position;
bool selected;
Then after you load your image content, keep your bounding box updated with the position of your image.
bBox.Min = new Vector3(position, 1.0f);
bBox.Max = new Vector3(position.X + texture.Width, position.Y + texture.Height, 0f);
Then also in your update method, you should have a touch collection initialized to handle input from the screen, get the positions of the touch collection, loop through them and see if they intersect your boundingbox.
foreach (Vector2 pos in touchPositions)
{
BoundingBox bb = new BoundingBox();
bb.Min = new Vector3(pos, 1.0f);
bb.Max = new Vector3(pos, 0f);
if (bb.Intersects(bBox)
{
if (selected)
{
//do something
}
else
{
selected = true;
}
}
}
From there, you have whether your object is selected or not. Then just use the gestures events to determine what you want to do with your texture object.
I implemented a zoom in and out function on a canvas element.
it works by scaling the canvas, translating it, and then redraw the whole scene again.
the problem is that it takes a lot of time to redraw everything because i got a lot of things on my canvas.
I need a way to copy the canvas to an image object and than copy the image back to the canvas without loosing quality. what are the specific methods to copy canvas to a javascript variable, and to to copy this variable back to the canvas later?
I'll be glad if you write down the code because I couldn't find any good explanation over the internet.
thanks,
The drawImage() method can draw to a canvas using another canvas instead of an image.
You could create a 'backup' canvas, of the same size as your original, draw the first one to there and then draw that one back to the original when you need it.
e.g.
// Assume we have a main canvas
// canvas = <main canvas>
ctx = canvas.getContext('2d');
..
// create backing canvas
var backCanvas = document.createElement('canvas');
backCanvas.width = canvas.width;
backCanvas.height = canvas.height;
var backCtx = backCanvas.getContext('2d');
// save main canvas contents
backCtx.drawImage(canvas, 0,0);
..
// restore main canvas
ctx.drawImage(backCanvas, 0,0);
There are a few ways to do it theres the getImageData and putImageData methods Reference, However putImageData and getImageData are pretty slow. Another way is to save the data to an image in memory and recall it from that which is much faster, then the third way is the one above mentioned by andrewmu which involves copying to another canvas element. I have included examples for the first and second type.
Image in memory method
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
savedData = new Image();
function save(){
// get the data
savedData.src = canvas.toDataURL("image/png");
}
function restore(){
// restore the old canvas
ctx.drawImage(savedData,0,0)
}
getImageData putImageData method
// Setup our vars, make a new image to store the canvas data
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
canvasData = '';
function save(){
// get the data
canvasData = ctx.getImageData(0, 0, 100, 100);
}
function restore(){
// restore the old canvas
ctx.putImageData(canvasData, 0, 0);
}
added image into canvas
var image = new Image();
image.src = "1.jpg";
image.onload = function () {
context.drawImage(image, 0, 0);
};