Is it possible to use ImageSpan in Xamarin.Forms? - xamarin.android

I'm trying to tweak the ImageButtonRenderer in Xamarin.Forms.Labs to render ImageButtons with the image & text centered (as a block) on the image button, rather than having the image aligned to the left/right edge of the button, as is the default behavior of the renderer.
I came across a StackOverflow article stating that this kind of layout can be achieved in Android using ImageSpan - however, when I translated the code to Xamarin.Forms C# the text is displayed, but not the image. Can anyone see something obviously wrong with this code?...
source is the ImageSource value of the ImageButton control
var handler = new FileImageSourceHandler();
var bitmap = await handler.LoadImageAsync(source, this.Context);
if (bitmap != null)
{
var bitmapWidth = this.ImageButton.ImageWidthRequest.Equals(0) ? bitmap.Width : (int)(this.ImageButton.ImageWidthRequest);
var bitmapHeight = this.ImageButton.ImageHeightRequest.Equals(0) ? bitmap.Height : (int)(this.ImageButton.ImageHeightRequest);
Drawable drawable = new BitmapDrawable(Resources, bitmap);
var scaledDrawable = new ScaleDrawable(drawable, 0, bitmapWidth, bitmapHeight).Drawable;
scaledDrawable.SetBounds(0, 0, bitmapWidth, bitmapHeight);
var scaledBitmap = Bitmap.CreateScaledBitmap(bitmap, bitmapWidth, bitmapHeight, true);
var leftPadding = (int)this.ImageButton.ButtonPadding.Left;
var rightPadding = (int)this.ImageButton.ButtonPadding.Right;
var topPadding = (int)this.ImageButton.ButtonPadding.Top;
var bottomPadding = (int)this.ImageButton.ButtonPadding.Bottom;
Control.SetPadding(leftPadding, topPadding, rightPadding, bottomPadding);
Control.Gravity = GravityFlags.CenterHorizontal | GravityFlags.CenterVertical;
switch (this.ImageButton.Orientation)
{
case ImageOrientation.ImageToRight:
var rightButtonLabel = new SpannableString(string.Format("{0} ", this.Control.Text));
//rightButtonLabel.SetSpan(new ImageSpan(scaledBitmap), rightButtonLabel.Length() - 1, rightButtonLabel.Length(), SpanTypes.InclusiveInclusive);
//rightButtonLabel.SetSpan(new ImageSpan(scaledDrawable, SpanAlign.Bottom), rightButtonLabel.Length() - 1, rightButtonLabel.Length(), SpanTypes.InclusiveInclusive);
//rightButtonLabel.SetSpan(new ImageSpan(scaledDrawable), rightButtonLabel.Length() - 1, rightButtonLabel.Length(), SpanTypes.InclusiveInclusive);
rightButtonLabel.SetSpan(new ImageSpan(Forms.Context, scaledBitmap, SpanAlign.Bottom), rightButtonLabel.Length() - 1, rightButtonLabel.Length(), SpanTypes.InclusiveInclusive);
Control.SetText(rightButtonLabel, Android.Widget.TextView.BufferType.Spannable);
break;
case ImageOrientation.ImageToLeft:
default:
var leftButtonLabel = new SpannableString(string.Format(" {0}", this.Control.Text));
//leftButtonLabel.SetSpan(new ImageSpan(scaledBitmap), 0, 1, SpanTypes.InclusiveInclusive);
//leftButtonLabel.SetSpan(new ImageSpan(scaledDrawable, SpanAlign.Bottom), 0, 1, SpanTypes.InclusiveInclusive);
//leftButtonLabel.SetSpan(new ImageSpan(scaledDrawable), 0, 1, SpanTypes.ExclusiveExclusive);
leftButtonLabel.SetSpan(new ImageSpan(Forms.Context, scaledBitmap, SpanAlign.Bottom), 0, 1, SpanTypes.InclusiveInclusive);
Control.SetText(leftButtonLabel, Android.Widget.TextView.BufferType.Spannable);
break;
}
}
The text centers just fine, but there's no image. I've tried a few different ImageSpan overloads with Drawable, Bitmaps, using the Forms.Context, and none of them work.
Is there some limitation with using ImageSpan in Xamarin.Forms?

Yes, it is year old question. Whatever, I guess this post can help with that problem.
Just need to load images to some (invisible) view, then your ImageSpan images would display.

Related

How do I get text to wrap and add another line below in a Xamarin Forms iOS custom renderer?

I am creating a custom renderer to display a title and description in a translucent white space on an image. The title should wrap to the second line if wider than the image, and an optional description may appear below if there is room. In Android I am able to do this using a StaticLayout:
// Create text rectangle
var height = Height / 3;
canvas.Save();
canvas.ClipRect(0, Height - height, Width, Height);
canvas.DrawARGB(191, 255, 255, 255);
canvas.Restore();
var item = ((ImageTile) Element).Item;
var textSize = (height - 15) / 2;
var textPaint = new TextPaint
{
StrokeWidth = 5,
TextSize = textSize,
FakeBoldText = true,
};
if (Build.VERSION.SdkInt >= BuildVersionCodes.Honeycomb)
SetLayerType(LayerType.Software, textPaint);
textPaint.SetStyle(Paint.Style.Fill);
textPaint.Color = global::Android.Graphics.Color.Black;
// init StaticLayout for text
var titleLayout = new StaticLayout(
item.Title, textPaint, Width - 10, Android.Text.Layout.Alignment.AlignNormal, 1.0f, 0.0f, false);
canvas.Translate(5, Height - height + 5);
titleLayout.Draw(canvas);
canvas.Restore();
textPaint = new TextPaint
{
StrokeWidth = 4,
TextSize = textSize - 10,
};
if (Build.VERSION.SdkInt >= BuildVersionCodes.Honeycomb)
SetLayerType(LayerType.Software, textPaint);
var descLayout = new StaticLayout(
item.Description, textPaint, Width - 10, Android.Text.Layout.Alignment.AlignNormal, 1.0f, 0.0f, false);
canvas.Translate(5, Height - height + titleLayout.Height + 5);
descLayout.Draw(canvas);
canvas.Restore();
in iOS I am using CATextLayers, but I am unable to get the text to wrap even thought I define the frame and set both Wrapped to true and TextTruncationMode to None. I also don't know how to get the actual height of the tilteLayer so I can position the descLayer below it. This is what I have so far, which draws the title and description on top of each other without wraping.
var textLayer = new CALayer();
var textRec = new CGRect(0, element.HeightRequest - textheight, element.WidthRequest,
textheight);
textLayer.Frame = textRec;
var backgroundcolor = Color.FromRgba(255, 255, 255, .25).ToCGColor();
textLayer.BackgroundColor = backgroundcolor;
Control.Layer.AddSublayer(textLayer);
var titleLayer = new CATextLayer
{
String = element.Item.Title,
ForegroundColor = Color.Black.ToCGColor(),
FontSize = 14,
Wrapped = true,
TextTruncationMode = CATextLayerTruncationMode.None,
TextAlignmentMode = CATextLayerAlignmentMode.Left,
//Bounds = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
// textheight - 4),
};
var titleRec = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
textheight - 4);
titleLayer.Frame = titleRec;
Control.Layer.AddSublayer(titleLayer);
var descLayer = new CATextLayer
{
String = element.Item.Description,
ForegroundColor = Color.Black.ToCGColor(),
FontSize = 12,
Wrapped = true,
TextTruncationMode = CATextLayerTruncationMode.None,
};
var descRec = new CGRect(2, element.HeightRequest - textheight + 2, element.WidthRequest - 4,
textheight - 4);
descLayer.ContentsRect = descRec;
Control.Layer.AddSublayer(descLayer);
Why not try autoLayout? You want to add a background view and two types of text on the original image. CALayer may achieve your effect, but it can't use autoLayout so that you need to use hard code(calculate the text height and the layer's position) to construct that. Also you said
I am unable to get the text to wrap. And I also don't know how to get
the actual height of the tilteLayer so I can position the descLayer
below it.
In the image's custom renderer, since this control has not been rendered, its Frame and HeightRequest are also unknown. Then you won't get the correct frame neither the layer, so the text will not be shown. I think the best way to do that is using AutoLayout:
// Create a view to hold content just like your textLayer
UIView bgView = new UIView();
bgView.BackgroundColor = UIColor.FromRGBA(1, 1, 1, 0.25f);
bgView.TranslatesAutoresizingMaskIntoConstraints = false;
Control.AddSubview(bgView);
bgView.LeadingAnchor.ConstraintEqualTo(Control.LeadingAnchor).Active = true;
bgView.TopAnchor.ConstraintGreaterThanOrEqualTo(Control.TopAnchor).Active = true;
bgView.TrailingAnchor.ConstraintEqualTo(Control.TrailingAnchor).Active = true;
bgView.BottomAnchor.ConstraintEqualTo(Control.BottomAnchor).Active = true;
UILabel titleLabel = new UILabel();
bgView.AddSubview(titleLabel);
titleLabel.TranslatesAutoresizingMaskIntoConstraints = false;
// Set this property to 0, then your label will move to several lines if your text is too large.
titleLabel.Lines = 0;
titleLabel.Font = UIFont.SystemFontOfSize(14);
titleLabel.Text = Element.Item.Title;
titleLabel.LeadingAnchor.ConstraintEqualTo(bgView.LeadingAnchor).Active = true;
titleLabel.TopAnchor.ConstraintEqualTo(bgView.TopAnchor).Active = true;
titleLabel.TrailingAnchor.ConstraintEqualTo(bgView.TrailingAnchor).Active = true;
// This constraint will show the titleLabel's content at high priority. It means show the descLabel if the image has enough place.
titleLabel.SetContentHuggingPriority(249, UILayoutConstraintAxis.Vertical);
UILabel descLabel = new UILabel();
bgView.AddSubview(descLabel);
descLabel.TranslatesAutoresizingMaskIntoConstraints = false;
descLabel.Lines = 0;
descLabel.Text = Element.Item.Description;
descLabel.Font = UIFont.SystemFontOfSize(12);
descLabel.LeadingAnchor.ConstraintEqualTo(bgView.LeadingAnchor).Active = true;
descLabel.TopAnchor.ConstraintEqualTo(titleLabel.BottomAnchor).Active = true;
descLabel.TrailingAnchor.ConstraintEqualTo(bgView.TrailingAnchor).Active = true;
descLabel.BottomAnchor.ConstraintEqualTo(bgView.BottomAnchor).Active = true;
In this way, bgView will expand its height depending on the titleLabel and descLabel. The largest height will be the original image's height. Moreover titleLabel will auto calculate its size depending on its content. Also the descLabel will always lies below the titleLabel if the room allows.
You can adjust these constraints to fit your own requirement.

Instagram Filters RGBA Values

I need to add image filters to an iOS app that is being developed using Adobe Air.
To be more specific, i need to apply the following filters (or similar):
Nashville
Hemingway
Jarques
Cross Process
Hazy Days
You can use this page as a reference http://techslides.com/demos/canvas/instagram.html
I know how to apply filters to a BitmapData on AS3, but i need the RGBA values of the above filters, does anyone knows them or knows how to obtain them?
I need something like the following values:
private static const NASHVILLE_FILTER_MATRIX: Array = [
1, 0, 0, 0, 0, //R
0, 0, 0, 0, 0, //G
0, 0, 0, 0, 0, //B
0, 0, 0, 1, 0 //A
];
Thank you for any help you can give me.
I recommend you to to check this framework GPUImage
There is an example here
You can create filters like:
// creating GPU Image
_gpuImg = new GPUImage();
_gpuImg.init(context3D, antiAlias, false, stageW, stageH, streamW, streamH);
// saving all filters
_imageProcessors = new Vector.<IGPUImageProcessor>();
// setup filters
var _gpuSepia:GPUImageSepia = new GPUImageSepia();
var _gpuGauss:GPUImageGaussianBlur = new GPUImageGaussianBlur(1.0, 4);
var _gpuGray:GPUImageGrayscale = new GPUImageGrayscale();
// Bloom Filter
var bloomEffect:GPUImageBloomEffect = new GPUImageBloomEffect(GPUImageBloomEffect.PRESET_DESATURATED, 4);
bloomEffect.initPreset(GPUImageBloomEffect.PRESET_SATURATED);
// TiltShift
var tiltShift:GPUImageTiltShiftEffect = new GPUImageTiltShiftEffect(2, 0.4, 0.6, 0.2);
// adding filter to image processor
_imageProcessors.push(bloomEffect);
// adding processor to the respective GPU Image
_gpuImg.addProcessor(_imageProcessors[0]);

Make dynamically loaded images ease in and out using actionscript

I have 4 buttons and 4 images when I click a button a different image appears. I want the images to ease in and out after each button click. I have tried the code below but can't seem to figure it out.
home_btn.addEventListener(MouseEvent.CLICK, homeClick);
function homeClick(e:MouseEvent):void{
backgroundimg.source = "images/bluebg.jpg";
var bluebgTween:Tween = new Tween(backgroundimg, "alpha", Strong.easeIn, 1, 0, 3, true);
}
If fading in then fading out, set the initial alpha of backgroundimg to 0.
backgroundimg.alpha = 0;
You can then fade in on the CLICK event. Note that I've swapped the begin and finish values for the Tween.
Once the tween has finished, the yoyo() method can then be used to fade out again.
home_btn.addEventListener(MouseEvent.CLICK, homeClick);
function homeClick(e:MouseEvent):void{
backgroundimg.source = "images/bluebg.jpg";
var bluebgTween:Tween = new Tween(backgroundimg, "alpha", Strong.easeIn, 0, 1, 3, true);
bluebgTween.addEventListener(TweenEvent.MOTION_FINISH, onFinish);
function onFinish(te:TweenEvent):void {
bluebgTween.yoyo();
bluebgTween.removeEventListener(TweenEvent.MOTION_FINISH, onFinish);
}
}
I used these sites as a reference for this:
http://www.republicofcode.com/tutorials/flash/as3tweenclass/
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/fl/transitions/Tween.html

IOS drawing a simple image turns out blurry using Xamarin C#

I'm developing in Xamarin and would like to draw a simple circle with the text "CIRCLE" inside it and display that image in a UIImageView. The problem is that the circle and text appear very blurry. I've read a bit about subpixels but I don't think that's my problem.
Here's the blurry image and code, hoping someone has some ideas :)
UIGraphics.BeginImageContext (new SizeF(150,150));
var context = UIGraphics.GetCurrentContext ();
var content = "CIRCLE";
var font = UIFont.SystemFontOfSize (16);
const float width = 150;
const float height = 150;
context.SetFillColorWithColor (UIColor.Red.CGColor);
context.FillEllipseInRect (new RectangleF (0, 0, width, height));
var contentString = new NSString (content);
var contentSize = contentString.StringSize (font);
var rect = new RectangleF (0, ((height - contentSize.Height) / 2) + 0, width, contentSize.Height);
context.SetFillColorWithColor (UIColor.White.CGColor);
new NSString (content).DrawString (rect, font, UILineBreakMode.WordWrap, UITextAlignment.Center);
var image = UIGraphics.GetImageFromCurrentImageContext ();
imageView.Image = image;
The reason why it's blurry is because it was resized. That bitmap is not 150x150 (like the context you created), it's 333x317 pixels.
It could be because imageView is scaling it's image (there's no source code) or because of pixel doubling (for retina displays). In the later case what you really want to use is:
UIGraphics.BeginImageContextWithOptions (size, false, 0);
which will (using 0) automagically use the right scaling factor for retina (or not) display - and look crisp (and not oversized) on all types of devices.

THREE.JS Shadow on opposite side of light

I used THREE.js r49 create 2 cube geometry with a directional light to cast shadow on them and got result as in the following picture.
I noticed that the shadow in the green circle should not be appeared, since the directional light is behind both of the cube. I guess that this is the material issue, I've tried changing various material parameters as well as change the material type itself, but the result still the same. I also tested the same code with r50 and r51 and got the same result.
Could anybody please give me some hint how to get rid of that shadow.
Both cube are creating using CubeGeometry and MeshLambertMaterial as following code.
The code:
// ambient
var light = new THREE.AmbientLight( 0xcccccc );
scene.add( light );
// the large cube
var p_geometry = new THREE.CubeGeometry(10, 10, 10);
var p_material = new THREE.MeshLambertMaterial({ambient: 0x808080, color: 0xcccccc});
var p_mesh = new THREE.Mesh( p_geometry, p_material );
p_mesh.position.set(0, -5, 0);
p_mesh.castShadow = true;
p_mesh.receiveShadow = true;
scene.add(p_mesh);
// the small cube
var geometry = new THREE.CubeGeometry( 2, 2, 2 );
var material = new THREE.MeshLambertMaterial({ambient: 0x808080, color: Math.random() * 0xffffff});
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set(0, 6, 3);
mesh.castShadow = true;
mesh.receiveShadow = true;
// add small cube as the child of large cube
p_mesh.add(mesh);
p_mesh.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), 0.25 * Math.PI );
// the light source
var light = new THREE.DirectionalLight( 0xffffff );
light.castShadow = true;
light.position.set(0, 10, -8); // set it light source to top-behind the cubes
light.target = p_mesh // target the light to the large cube
light.shadowCameraNear = 5;
light.shadowCameraFar = 25;
light.shadowCameraRight = 10;
light.shadowCameraLeft = -10;
light.shadowCameraTop = 10;
light.shadowCameraBottom = -10;
light.shadowCameraVisible = true;
scene.add( light );
Yes, this is a known, and long-standing, WebGLRenderer issue.
The problem is that the dot product of the face normal and the light direction is not taken into consideration in the shadow calculation. As a consequence, "shadows show through from the back".
As a work-around, you could have a different material for each face, with only certain materials receiving shadows.
three.js r.71

Resources