Coners of Custom Frame are getting trimmed in Xamarin forms iOS - xamarin.forms

I have created a custom renderer for a frame to have rounded corners only on 2 sides. The code works fine in Android but in iOS the rounded corners are getting trimmed if the background color of frame is white and border color is blue like below image.
Custom Renderer IOS
public class CustomFrameRenderer : FrameRenderer
{
public override void LayoutSubviews()
{
base.LayoutSubviews();
UpdateCornerRadius();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(CustomFrame.CornerRadius) ||
e.PropertyName == nameof(CustomFrame))
{
UpdateCornerRadius();
}
}
// A very basic way of retrieving same one value for all of the corners
private double RetrieveCommonCornerRadius(CornerRadius cornerRadius)
{
var commonCornerRadius = cornerRadius.TopLeft;
if (commonCornerRadius <= 0)
{
commonCornerRadius = cornerRadius.TopRight;
if (commonCornerRadius <= 0)
{
commonCornerRadius = cornerRadius.BottomLeft;
if (commonCornerRadius <= 0)
{
commonCornerRadius = cornerRadius.BottomRight;
}
}
}
return commonCornerRadius;
}
private UIRectCorner RetrieveRoundedCorners(CornerRadius cornerRadius)
{
var roundedCorners = default(UIRectCorner);
if (cornerRadius.TopLeft > 0)
{
roundedCorners |= UIRectCorner.TopLeft;
}
if (cornerRadius.TopRight > 0)
{
roundedCorners |= UIRectCorner.TopRight;
}
if (cornerRadius.BottomLeft > 0)
{
roundedCorners |= UIRectCorner.BottomLeft;
}
if (cornerRadius.BottomRight > 0)
{
roundedCorners |= UIRectCorner.BottomRight;
}
return roundedCorners;
}
private void UpdateCornerRadius()
{
var cornerRadius = (Element as CustomFrame)?.CornerRadius;
if (!cornerRadius.HasValue)
{
return;
}
var roundedCornerRadius = RetrieveCommonCornerRadius(cornerRadius.Value);
if (roundedCornerRadius <= 0)
{
return;
}
var roundedCorners = RetrieveRoundedCorners(cornerRadius.Value);
var path = UIBezierPath.FromRoundedRect(Bounds, roundedCorners, new CGSize(roundedCornerRadius, roundedCornerRadius));
var mask = new CAShapeLayer { Path = path.CGPath };
NativeView.Layer.Mask = mask;
//NativeView.Layer.CornerRadius = 0;
NativeView.ClipsToBounds = true;
NativeView.Layer.MaskedCorners = (CoreAnimation.CACornerMask)3;
}
}
Can someone please help me to resolve below issue.
Thanks.

You could check the following code
private void UpdateCornerRadius()
{
var cornerRadius = (Element as Frame)?.CornerRadius;
if (!cornerRadius.HasValue)
{
return;
}
var roundedCornerRadius = RetrieveCommonCornerRadius(cornerRadius.Value);
if (roundedCornerRadius <= 0)
{
return;
}
var roundedCorners = RetrieveRoundedCorners(cornerRadius.Value);
NativeView.Layer.MasksToBounds = true;
var path = UIBezierPath.FromRoundedRect(Bounds, roundedCorners, new CGSize(roundedCornerRadius, roundedCornerRadius));
var mask = new CAShapeLayer { Path = path.CGPath };
mask.Frame = Bounds;
mask.LineWidth = 1;
mask.StrokeColor = UIColor.SystemBlueColor.CGColor; // border color
mask.FillColor = UIColor.Clear.CGColor; // bg color , you need to set it as clear otherwise it will cover its child element
mask.ShadowRadius = 0;
NativeView.Layer.AddSublayer(mask);
// NativeView.Layer.MaskedCorners = (CoreAnimation.CACornerMask)3;
}

A mask takes a layer, and trim that visual to match the mask.
So if a layer is a rectangle with black borders, and your mask is an oval,
an oval will appear with the color of the rectangle that it masks.
But it will not round those rectangle borders, just cut them out.
What you need to do instead is to build a layer that will replace NativeView.Layer
With your rounded rectangle, without using the mask.
Or change those values on the elements in the layer that already exist, directly.

Related

Get GraphicEdge at Mouse hovering in GraphStream

I want to show the weight of an edge when the mouse hovers over it.
So I use an MouseEvent.MOUSE_MOVED in my implemented MouseManager. With nodes, I just can call view.findGraphicElementAt(getManagedTypes(), event.getX(), event.getY()) to get the GraphicNode Object. Unfortunately, edges do not have one x and y value, and are thus not found by this method. Yes, I know getX()and getY() are implemented for a GraphicEdge but are just pointing at the center of the edge.
I need the Edge Object to get some further information stored at the edge (like weight). So how do I get the Edge Object using x,y or some other values I can retrieve from the received MouseEvent?
In fact, edge selection, mouseOver, and mouseLeft functions (which kind of includes hovering over edges) is already implemented in the MouseOverMouseManager or FxMouseOverMouseManager. This manager is automatically set when calling view.enableMouseOptions() but I already implemented an individual MouseManager for some other reasons plus hovering over an edge is only detected when hovering over the center of the edge. So my solution was to copy the code from MouseOverMouseManager into MyMousemanager and modify it.
Edit:
public class CompoundListNetworkMouseManager extends FxMouseManager {
private GraphicElement hoveredElement;
private long hoveredElementLastChanged;
private ReentrantLock hoverLock = new ReentrantLock();
private Timer hoverTimer = new Timer(true);
private HoverTimerTask latestHoverTimerTask;
/**
*(copied from the GraphsStream Library)
* The mouse needs to stay on an element for at least this amount of milliseconds,
* until the element gets the attribute "ui.mouseOver" assigned.
* A value smaller or equal to zero indicates, that the attribute is assigned without delay.
* */
private final long delayHover;
public CompoundListNetworkMouseManager(){
super(EnumSet.of(InteractiveElement.NODE, InteractiveElement.EDGE));
this.delayHover = 100;
}
#Override
public void init(GraphicGraph graph, View view) {
this.graph = graph;
this.view = view;
view.addListener(MouseEvent.MOUSE_MOVED, mouseMoved);
}
#Override
public void release() {
view.removeListener(MouseEvent.MOUSE_MOVED, mouseMoved);
}
#Override
public EnumSet<InteractiveElement> getManagedTypes() {
return super.getManagedTypes();
}
protected void mouseOverElement(GraphicElement element){
element.setAttribute("ui.mouseOver", true);
element.setAttribute("ui.class", "mouseOver"); //I defined a class/type for edges in the CSS styling sheet that is calles "mouseOver"
if(element instanceof GraphicEdge) {
mouseOverEdge((GraphicEdge) element);
}
else if(element instanceof GraphicNode){
mouseOverNode((GraphicNode) element);
}
protected void mouseOverEdge(GraphicEdge graphicEdge) {
view.freezeElement(graphicEdge, true);
Edge edge = graph.getEdge(graphicEdge.getId());
System.out.println("Mouse over edge " + edge.getId());
}
protected void mouseLeftElement(GraphicElement element) {
this.hoveredElement = null;
element.removeAttribute("ui.mouseOver");
element.removeAttribute("ui.class");
}
EventHandler<MouseEvent> mouseMoved = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
try {
hoverLock.lockInterruptibly();
boolean stayedOnElement = false;
curElement = view.findGraphicElementAt(getManagedTypes(), event.getX(), event.getY());
//adjusted implementation of search for edges
if(curElement == null && getManagedTypes().contains(InteractiveElement.EDGE)){
curElement = (GraphicElement) findEdgeAt(event.getX(), event.getY());
}
if(hoveredElement != null) {
//check if mouse stayed on the same element to avoid the mouseOverEvent being processed multiple times
stayedOnElement = curElement == null ? false : curElement.equals(hoveredElement);
if (!stayedOnElement && hoveredElement.hasAttribute("ui.mouseOver")) {
mouseLeftElement(hoveredElement);
}
}
if (!stayedOnElement && curElement != null) {
if (delayHover <= 0) {
mouseOverElement(curElement);
} else {
hoveredElement = curElement;
hoveredElementLastChanged = Calendar.getInstance().getTimeInMillis();
if (latestHoverTimerTask != null) {
latestHoverTimerTask.cancel();
}
latestHoverTimerTask = new HoverTimerTask(hoveredElementLastChanged, hoveredElement);
hoverTimer.schedule(latestHoverTimerTask, delayHover);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
hoverLock.unlock();
}
}
};
//copied from GraphStream Library
private final class HoverTimerTask extends TimerTask {
private final long lastChanged;
private final GraphicElement element;
public HoverTimerTask(long lastChanged, GraphicElement element) {
this.lastChanged = lastChanged;
this.element = element;
}
#Override
public void run() {
try {
hoverLock.lock();
if (hoveredElementLastChanged == lastChanged) {
mouseOverElement(element);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
hoverLock.unlock();
}
}
}
//findGraphicElement could be used but I wanted to implement the edgeContains method myself
private Edge findEdgeAt(double x, double y){
Camera cam = view.getCamera();
GraphMetrics metrics = cam.getMetrics();
//transform x and y
double xT = x + metrics.viewport[0];
double yT = y + metrics.viewport[0];
Edge edgeFound = null;
if (getManagedTypes().contains(InteractiveElement.EDGE)) {
Optional<Edge> edge = graph.edges().filter(e -> edgeContains((GraphicEdge) e, xT, yT)).findFirst();
if (edge.isPresent()) {
if (cam.isVisible((GraphicElement) edge.get())) {
edgeFound = edge.get();
}
}
}
return edgeFound;
}
//new edgeContains() method that finds edge at hovering not only when hovered over edge center
private boolean edgeContains(GraphicEdge edge, double x, double y) {
Camera cam = view.getCamera();
GraphMetrics metrics = cam.getMetrics();
Values size = edge.getStyle().getSize();
double deviation = metrics.lengthToPx(size, 0);
Point3 edgeNode0 = cam.transformGuToPx(edge.from.x, edge.from.y, 0);
Point3 edgeNode1 = cam.transformGuToPx(edge.to.x, edge.to.y, 0);
//check of point x,y is between nodes of the edge
boolean edgeContains = false;
//check x,y range
if(x > Math.min(edgeNode0.x, edgeNode1.x) - deviation
&& x < Math.max(edgeNode0.x, edgeNode1.x) + deviation
&& y > Math.min(edgeNode0.y, edgeNode1.y) - deviation
&& y < Math.max(edgeNode0.y, edgeNode1.y) + deviation){
//check deviation from edge
Vector2 vectorNode0To1 = new Vector2(edgeNode0, edgeNode1);
Point2 point = new Point2(x, y);
Vector2 vectorNode0ToPoint = new Vector2(edgeNode0, point);
//cross product of vectorNode0ToPoint and vectorNode0to1
double crossProduct = vectorNode0ToPoint.x() * vectorNode0To1.y() - vectorNode0To1.x() * vectorNode0ToPoint.y();
//distance of point to the line extending the edge
double d = Math.abs(crossProduct) / vectorNode0To1.length();
if(d <= deviation){
edgeContains = true;
}
}
return edgeContains;
}
}
CSS Stylesheet:
...
edge{
fill-color: black;
size: 2px;
arrow-shape: none;
shape: line;
text-mode: hidden;
}
edge.mouseOver {
fill-color: red;
stroke-color: red;
text-mode: normal;
text-background-mode: plain; /*plain or none*/
text-background-color: rgba(255, 255, 255, 200);
text-alignment: along;
}

Using pinch to zoomin and zoomout some unwanted background should copied color appeared and pixel quality low android below 9 version Xamarin android

Whenever i using wo fingers pinch to zoomin and zoomout some unwanted background should copied color appeared my camerapagerenderer if i single touch or touch it not seems to be apppear when i pinch to zoomin using two fingers it appears on my screen
public override bool OnTouchEvent(MotionEvent e)
{
switch (e.Action & MotionEventActions.Mask)
{
case MotionEventActions.Down:
oldDist = getFingerSpacing(e);
break;
case MotionEventActions.Move:
float newDist = getFingerSpacing(e);
if (newDist > oldDist)
{
//mCamera is your Camera which used to take picture, it should already exit in your custom Camera
handleZoom(true, camera);
}
else if (newDist < oldDist)
{
handleZoom(false, camera);
}
oldDist = newDist;
break;
}
return true;
}
private void handleZoom(bool isZoomIn, global::Android.Hardware.Camera camera)
{
global::Android.Hardware.Camera.Parameters parameters = camera.GetParameters();
if (parameters.IsZoomSupported)
{
int maxZoom = parameters.MaxZoom;
int zoom = parameters.Zoom;
if (isZoomIn && zoom < maxZoom)
{
zoom++;
}
else if(zoom > 0)
{
zoom--;
}
parameters.Zoom = zoom;
camera.SetParameters(parameters);
}
else
{
Android.Util.Log.Error("lv", "zoom not supported");
}
}
private static float getFingerSpacing(MotionEvent e)
{
if(e.PointerCount==2)
{
int pointerIndex = e.FindPointerIndex(_activePointerId);
float x = e.GetX(pointerIndex);
float y = e.GetY(pointerIndex);
return (float)Math.Sqrt(x * x + y * y);
}
}
You could check the code below. It works on Android 10.0 with no color shades.
class CameraPageRenderer : PageRenderer, TextureView.ISurfaceTextureListener
{
global::Android.Hardware.Camera camera;
global::Android.Widget.Button takePhotoButton;
global::Android.Widget.Button toggleFlashButton;
global::Android.Widget.Button switchCameraButton;
global::Android.Views.View view;
Activity activity;
CameraFacing cameraType;
TextureView textureView;
SurfaceTexture surfaceTexture;
bool flashOn;
public CameraPageRenderer(Context context) : base(context)
{
}
float oldDist = 1f;
public override bool OnTouchEvent(MotionEvent e)
{
switch (e.Action & MotionEventActions.Mask)
{
case MotionEventActions.Down:
oldDist = getFingerSpacing(e);
break;
case MotionEventActions.Move:
float newDist = getFingerSpacing(e);
if (newDist > oldDist)
{
//mCamera is your Camera which used to take picture, it should already exit in your custom Camera
handleZoom(true, camera);
}
else if (newDist < oldDist)
{
handleZoom(false, camera);
}
oldDist = newDist;
break;
}
return true;
}
private static float getFingerSpacing(MotionEvent e)
{
if (e.PointerCount == 2)
{
float x = e.GetX(0) - e.GetX(1);
float y = e.GetY(0) - e.GetY(1);
return (float)Math.Sqrt(x*x + y*y);
}
return 0;
}
private void handleZoom(bool isZoomIn, global::Android.Hardware.Camera camera)
{
//camera.StopPreview();
// camera.Release();
// camera = global::Android.Hardware.Camera.Open((int)cameraType);
global::Android.Hardware.Camera.Parameters parameters = camera.GetParameters();
if (parameters.IsZoomSupported)
{
int maxZoom = parameters.MaxZoom;
int zoom = parameters.Zoom;
if (isZoomIn && zoom < maxZoom)
{
zoom++;
}
else if (zoom > 0)
{
zoom--;
}
parameters.Zoom = zoom;
camera.SetParameters(parameters);
camera.SetPreviewTexture(surfaceTexture);
PrepareAndStartCamera();
}
else
{
Android.Util.Log.Error("lv", "zoom not supported");
}
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
try
{
SetupUserInterface();
//SetupEventHandlers();
AddView(view);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(#" ERROR: ", ex.Message);
}
}
void SetupUserInterface()
{
activity = this.Context as Activity;
view = activity.LayoutInflater.Inflate(Resource.Layout.CameraLayout, this, false);
cameraType = CameraFacing.Back;
textureView = view.FindViewById<TextureView>(Resource.Id.textureView);
textureView.SurfaceTextureListener = this;
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
view.Measure(msw, msh);
view.Layout(0, 0, r - l, b - t);
}
public void OnSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
camera = global::Android.Hardware.Camera.Open((int)cameraType);
textureView.LayoutParameters = new FrameLayout.LayoutParams(width, height);
surfaceTexture = surface;
camera.SetPreviewTexture(surface);
PrepareAndStartCamera();
}
public bool OnSurfaceTextureDestroyed(SurfaceTexture surface)
{
camera.StopPreview();
camera.Release();
return true;
}
public void OnSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
{
PrepareAndStartCamera();
}
public void OnSurfaceTextureUpdated(SurfaceTexture surface)
{
}
void PrepareAndStartCamera()
{
camera.StopPreview();
var display = activity.WindowManager.DefaultDisplay;
if (display.Rotation == SurfaceOrientation.Rotation0)
{
camera.SetDisplayOrientation(90);
}
if (display.Rotation == SurfaceOrientation.Rotation270)
{
camera.SetDisplayOrientation(180);
}
camera.StartPreview();
}
}
Update: The result on Android 6.0

Xamarin.iOS screenshot with transparent background

I build an application that users can load a report and draw on screen for highlight and share some information (as an image). I develop this feature using Rg.Plugins.Popup.Pages.PopupPage and SkiaSharp. The popup page has a transparent background and a sharecontent button. Users draw in the popup page.
In Android, this works fine because I can take a screeshot of the entire screen using currentview.Window.DecorView.RootView, but in iOS when I take a screenshot the image get a black background. I use the following code to take the screenshot:
public void ShareContent(string imageName)
{
UIGraphics.BeginImageContextWithOptions(new CoreGraphics.CGSize(UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height), false, 0);
UIImage capture = UIApplication.SharedApplication.KeyWindow.Capture();
UIGraphics.EndImageContext();
var activityItems = new[] { capture };
var activityController = new UIActivityViewController(activityItems, null);
var topController = UIApplication.SharedApplication.KeyWindow.RootViewController;
while (topController.PresentedViewController != null)
{
topController = topController.PresentedViewController;
}
topController.PresentViewController(activityController, true, () => { });
}
How can I draw an image with a transparent background in Xamarin iOS?
Codebehind of PopupPage in shared project
public partial class DrawAndSharePage : Rg.Plugins.Popup.Pages.PopupPage
{
Dictionary<long, SKPath> inProgressPaths = new Dictionary<long, SKPath>();
List<SKPath> completedPaths = new List<SKPath>();
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Blue,
StrokeWidth = 10,
StrokeCap = SKStrokeCap.Round,
StrokeJoin = SKStrokeJoin.Round
};
public DrawAndSharePage()
{
BackgroundColor = new Color(0, 0, 0, 0.1);
InitializeComponent();
}
private async void BtnClose_Clicked(object sender, System.EventArgs e)
{
await Rg.Plugins.Popup.Services.PopupNavigation.Instance.PopAllAsync();
}
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
switch (args.Type)
{
case TouchActionType.Pressed:
if (!inProgressPaths.ContainsKey(args.Id))
{
SKPath path = new SKPath();
path.MoveTo(ConvertToPixel(args.Location));
inProgressPaths.Add(args.Id, path);
canvasView.InvalidateSurface();
}
break;
case TouchActionType.Moved:
if (inProgressPaths.ContainsKey(args.Id))
{
SKPath path = inProgressPaths[args.Id];
path.LineTo(ConvertToPixel(args.Location));
canvasView.InvalidateSurface();
}
break;
case TouchActionType.Released:
if (inProgressPaths.ContainsKey(args.Id))
{
completedPaths.Add(inProgressPaths[args.Id]);
inProgressPaths.Remove(args.Id);
canvasView.InvalidateSurface();
}
break;
case TouchActionType.Cancelled:
if (inProgressPaths.ContainsKey(args.Id))
{
inProgressPaths.Remove(args.Id);
canvasView.InvalidateSurface();
}
break;
}
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKCanvas canvas = args.Surface.Canvas;
canvas.Clear();
foreach (SKPath path in completedPaths)
{
canvas.DrawPath(path, paint);
}
foreach (SKPath path in inProgressPaths.Values)
{
canvas.DrawPath(path, paint);
}
}
SKPoint ConvertToPixel(Point pt)
{
return new SKPoint((float)(canvasView.CanvasSize.Width * pt.X / canvasView.Width),
(float)(canvasView.CanvasSize.Height * pt.Y / canvasView.Height));
}
private void OnTouchEffectAction(object sender, object args)
{
}
}

How to show PopUp with in the screen area In Flex and AS3

Hi i am trying to show my popup on the image mouse over
it is showing fine
when i trying mouse over right side last images popup is going out of the screen
Here TalentInfoPopUp is **TitleWindow
This is my sample code
private static var staticWindow :TalentInfoPopUp = null;
private static var visibleWindow:TalentInfoPopUp = null;
public static function show(t:Object, parent : DisplayObject, x:Number , y:Number):void
{
if(staticWindow == null)
{
visibleWindow = staticWindow = PopUpManager.createPopUp( parent , TalentInfoPopUp , false) as TalentInfoPopUp;
}
else if(visibleWindow == null)
{
visibleWindow = staticWindow;
PopUpManager.addPopUp(staticWindow, parent, false);
}
PopUpManager.centerPopUp(staticWindow);
staticWindow.talent = t;
staticWindow.x = x;
staticWindow.y =y;
PopUpManager.bringToFront(staticWindow);
staticWindow.talent = t;
staticWindow.move(x,y);
staticWindow.callLater(staticWindow.setPosition,[x,y]);
//staticWindow.setPosition(x,y);
}
private function setPosition(nx:int,ny:int):void
{
var maxWidth:int = stage.width ;
var maxHeight:int = stage.height;
if(nx>maxWidth-width)
{
nx=nx-width;
}
if(ny>maxHeight-height)
{
ny=ny-height;
}
this.move(nx,ny);
}
Try using systemManager.screen.width and systemManager.screen.height instead of stage.width and stage.height; also learn about the localToGlobal and globalToLocal methods and how to use them.

How do you set different columns to have alternatingItemColors on DataGridColumns?

On a DataGrid, setting alternatingItemColors will apply the color scheme to all of the columns of that grid. I'm looking for a way to define different alternating colors for each column. Is there a baked in way to do this?
Have a look on this:http://blog.flexexamples.com/2008/09/24/setting-background-colors-on-a-datagrid-column-in-flex/
I hope this would be helpful for you ;)
public class BlocksTable extends DataGrid
{
public static const VALID_COLOR:uint = 0xDBAB21;
public static const INVALID_COLOR:uint = 0xC7403E;
public function BlocksTable()
{
super();
}
override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void
{
var contentHolder:ListBaseContentHolder = ListBaseContentHolder(s.parent);
var background:Shape;
if (rowIndex < s.numChildren)
{
background = Shape(s.getChildAt(rowIndex));
}
else
{
background = new FlexShape();
background.name = "background";
s.addChild(background);
}
background.y = y;
// Height is usually as tall is the items in the row, but not if
// it would extend below the bottom of listContent
var height:Number = Math.min(height,
contentHolder.height -
y);
var g:Graphics = background.graphics;
g.clear();
var fillColor:uint;
if(dataIndex < this.dataProvider.length)
{
if(this.dataProvider.getItemAt(dataIndex).IS_VALID)
{
fillColor = VALID_COLOR;
}
else
{
fillColor = INVALID_COLOR;
}
}
else
{
fillColor = color;
}
g.beginFill(fillColor, getStyle("backgroundAlpha"));
g.drawRect(0, 0, contentHolder.width, height);
g.endFill();
}
}

Resources