How can I set the color of the text in an android renderer? I have the following renderer:
public class CustomSwitchRenderer : SwitchRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.TextOn = "Yes";
Control.TextOff = "No";
Android.Graphics.Color colorOn = Android.Graphics.Color.Rgb(239, 201, 6);
Android.Graphics.Color colorOff = Android.Graphics.Color.LightGray;
Android.Graphics.Color colorDisabled = Android.Graphics.Color.Gray;
Android.Graphics.Color textColor = Android.Graphics.Color.Black;
Control.SetTextColor (ColorStateList.ValueOf (textColor));
Control.SetTextColor (textColor);
StateListDrawable drawable = new StateListDrawable();
drawable.AddState(new int[] { Android.Resource.Attribute.StateChecked }, new ColorDrawable(colorOn));
drawable.AddState(new int[] { -Android.Resource.Attribute.StateEnabled }, new ColorDrawable(colorDisabled));
drawable.AddState(new int[] { }, new ColorDrawable(colorOff));
Control.ThumbDrawable = drawable;
}
}
}
I am able to change the color of the switch, but I can't figure out how to change the color of the YES/NO text. SetTextColor doesn't seem to work.
Create an xml file under the Resources\values directory in your droid project.
doesn't matter the name but it needs to end in .xml and contain
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CodeFont" parent="#android:style/TextAppearance.Medium">
<item name="android:textColor">#00FF00</item>
</style>
</resources>
Then in your renderer call Control.SetSwitchTextAppearance. Pass in the Context and the ResId of the resource you created. You can get the Id from the generated file under Resources titled Resource.designer.cs. Alternately you can call the Const that generated as follows.
Control.SetSwitchTextAppearance (Control.Context, Resource.Style.CodeFont);
Hope it helps. If you cant get it let me know Ill upload a sample app.
Related
Using Xamarin Forms (version 2.5.0.121934), I'm working on an app targeting Android, iOS, and UWP. I need to add underlining and strikethrough to some text, which require custom renderers. For Android and iOS, everything is working fine, and on UWP, applying strikethrough or underline works correctly, but removing those decorations isn't working.
Here's the entirety of the UWP renderer:
[assembly: ExportRenderer(typeof(EnhancedLabel), typeof(EnhancedLabelRenderer))]
namespace myApp.UWP
{
public class EnhancedLabelRenderer : LabelRenderer
{
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var strikethrough = ((EnhancedLabel)sender).Strikethrough;
var underline = ((EnhancedLabel)sender).Underline;
if (strikethrough && underline)
{
Control.TextDecorations = TextDecorations.Strikethrough | TextDecorations.Underline;
}
else if (strikethrough)
{
Control.TextDecorations = TextDecorations.Strikethrough;
}
else if (underline)
{
Control.TextDecorations = TextDecorations.Underline;
}
else
{
Control.TextDecorations = TextDecorations.None;
}
}
}
}
EnhancedLabel is a simple class that extends Xamarin.Forms.Label and adds the simple BindableProperty fields that specify strikethrough or underlining.
The renderer is properly setting TextDecorations.None, but that isn't having an effect on the UI. I've worked through this in the debugger, and can actually see that the state of the TextBlock within the ExtendedLabel has TextDecorations.None, but the UI is still drawing it with underlining or strikethrough (essentially, either of those can be added, but neither can be removed).
I've gone through the Xamarin documentation and looked at the bugs in Bugzilla, and haven't found any clues. Has any one else encountered this? Wondering if there's a UWP-specific call I need to make that I missed, or if using TextDecorations is the wrong way to apply the styles, or if I've actually stumbled across a bug.
Bug in UWP as in Xaml below:
<TextBlock>
<Run Text="Decorations can be toggled on and off"/>
</TextBlock>
<TextBlock Text="Decorations will not toggle off"/>
It is the same issue if you code the TextBlock:
TextBlock textBlock = new TextBlock { FontSize = 18.0 };
textBlock.Inlines.Add(new Windows.UI.Xaml.Documents.Run { Text = "This text will not stick on text decoration." });
TextBlock textBlockBad = new TextBlock
{
FontSize = 18.0,
Text = "This text will not enable the TextDecorations to be turned off"
};
Same behaviour found with Typography.Capitals
Just need to use only Inlines for TextBlocks and presumably RichTextBlocks to avoid these issues.
Wondering if there's a UWP-specific call I need to make that I missed, or if using TextDecorations is the wrong way to apply the styles, or if I've actually stumbled across a bug.
If you want yo use TextDecorations, you could use the Run instance to pack the decorated text like the follow.
Underline ul = new Underline();
ul.TextDecorations = TextDecorations.Strikethrough;
Run r = new Run();
r.Text = "Here is an underlined text";
ul.Inlines.Add(r);
MyTextBlock.Inlines.Add(ul);
For you requirement, I have create a CustomLabel that you could use directly.
CustomLabel.cs
public class CustomLabel : Label
{
public static readonly BindableProperty DeckProperty = BindableProperty.Create(
propertyName: "Deck",
returnType: typeof(TextDeck),
declaringType: typeof(CustomLabel),
defaultValue: default(TextDeck));
public TextDeck Deck
{
get { return (TextDeck) GetValue(DeckProperty); }
set { SetValue(DeckProperty, value); }
}
}
public enum TextDeck
{
None = 0,
//
// Summary:
// Underline is applied to the text.
Underline = 1,
//
// Summary:
// Strikethrough is applied to the text.
Strikethrough = 2
}
CustomLabelRenderer.cs
public class CustomLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var element = Element as CustomLabel;
var underline = new Underline();
var run = new Run();
switch (element.Deck)
{
case TextDeck.None:
underline.TextDecorations = TextDecorations.None;
break;
case TextDeck.Strikethrough:
underline.TextDecorations = TextDecorations.Strikethrough;
break;
case TextDeck.Underline:
underline.TextDecorations = TextDecorations.Underline;
break;
}
run.Text = element.Text;
underline.Inlines.Add(run);
Control.Inlines.Clear();
Control.Inlines.Add(underline);
}
}
}
Usage
<local:CustomLabel Deck="Underline" Text="Welcome to Xamarin.Forms!" />
assume we have the following text :
Contact us on 015546889 or email#hotmail.com
How I can display the above text in same label in xamarin.forms and handle click on email by send email and handle phone call by click on the number.
I can use the the following code to make clickable label
Label label = new Label;
label.GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => {
//do some function here
})
});
How to hyperlink same as messaging app or Whatsapp application
After a lot of search i found the perfect solution Here :
https://theconfuzedsourcecode.wordpress.com/tag/xamarin-hyperlink-label/
Hope this will help others :)
Check out the Label element in Forms9Patch. It has a HtmlText property that allows simple markup.
using System;
using Xamarin.Forms;
namespace Forms9PatchDemo
{
public class LabelLink : ContentPage
{
public LabelLink()
{
var label = new Forms9Patch.Label
{
HtmlText = "Contact us on <a id=\"phone\" href=\"tel:+353015546889\">015546889</a> or <a id=\"email\" href=\"mailto:email#hotmail.com\">email#hotmail.com</a>"
};
label.ActionTagTapped += (object sender, Forms9Patch.ActionTagEventArgs e) =>
{
var id = e.Id;
var href = e.Href;
var uri = new Uri(e.Href);
Device.OpenUri(uri);
};
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
new Label { Text = "Forms9Patch.Label.HtmlText <a> example" },
new BoxView { BackgroundColor = Color.Black, HeightRequest = 1 },
label
}
};
}
}
}
Note that the above example won't work on iOS emulators because the tel: and mailto: schemes are not supported. It does work on actual iOS devices.
The Frame class in Xamarin Forms is quite limited, and can't allow me to get a shadow behind the Frame. I've made a custom renderer for iOS using this code:
public class RatingInfoFrameRenderer : FrameRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
Layer.BorderColor = UIColor.White.CGColor;
Layer.CornerRadius = 10;
Layer.MasksToBounds = false;
Layer.ShadowOffset = new CGSize(-2, 2);
Layer.ShadowRadius = 5;
Layer.ShadowOpacity = 0.4f;
}
}
Making a similar one on Android is causing me problems, since my knowledge on Android native is kind of limited. Could anyone tell me what to look at, perhaps some good code example? I haven't found anything that looks similar to this.
It can be very easy in Android platform, but first of all, you need to create your shadow under Drawable folder of Android resources. For example:
<?xml version="1.0" encoding="utf-8" ?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#CABBBBBB" />
<corners android:radius="2dp" />
</shape>
</item>
<item
android:left="0dp"
android:right="0dp"
android:top="0dp"
android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#android:color/white" />
<corners android:radius="2dp" />
</shape>
</item>
</layer-list>
Name this file as "shadow.xml" and place it under the Drawable folder of Android project, then in your RatingInfoFrameRenderer:
public class RatingInfoFrameRenderer : FrameRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
ViewGroup.SetBackgroundResource(Resource.Drawable.shadow);
}
}
}
To change the style of shadow, you can modify the shadow.xml file, for more information about this, you may refer to google's official document: LayerList.
I know this question is old, but there is an updated way of getting a better shadow effect then the accepted answer. inherit from Xamarin.Forms.Platform.Android.FastRenderers.FrameRenderer and then use SetOutlineSpotShadowColor(Color color) to set the shadow color. You can use CardElevation to determine the strength and spread of the shadow as well.
[assembly: ExportRenderer(typeof(Myframe), typeof(MyFrameRenderer))]
namespace MyApp.Droid.Renderers
{
public class MyFrameRenderer: Xamarin.Forms.Platform.Android.FastRenderers.FrameRenderer
{
public MyFrameRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
CardElevation = 10;
if(((App)Application.Current).Theme != Core.Enums.Theme.Dark)
{
SetOutlineSpotShadowColor(Xamarin.Forms.Color.Gray.ToAndroid());
}
else
{
SetOutlineSpotShadowColor(Xamarin.Forms.Color.HotPink.ToAndroid());
}
}
}
}
Hope this helps someone who stumbles in here like I did.
I was able to get a shadow effect in Xamarin Forms for a box view, I'm pretty sure it can used similarly for a Frame. I got the clue from Android Documentation
I added a new property called HasShadow
public static readonly BindableProperty HasShadowProperty =
BindableProperty.Create("HasShadow", typeof(bool), typeof(ExtendedBoxView), false);
public bool HasShadow
{
get { return (bool)GetValue(HasShadowProperty); }
set { SetValue(HasShadowProperty, value); }
}
Here's the code for the Renderer in Android
public class ExtendedBoxViewRenderer : BoxRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
var element = e.NewElement as ExtendedBoxView;
if (element == null) return;
if (element.HasShadow)
{
ViewGroup.Elevation = 8.0f;
ViewGroup.TranslationZ = 10.0f;
}
}
}
And this is how it looks
UPDATE
I found out that this approach causes app crash for older versions of Android. Although I haven't found a way to display Shadows in Android versions prior to Lollipop. This will prevent any app crashes
public class ExtendedBoxViewRenderer : BoxRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
var element = e.NewElement as ExtendedBoxView;
if (element == null) return;
if (element.HasShadow)
{
//For some reason ViewCompat has issues when running in debug hence the workaround.
#if DEBUG
double dAndroidVersion;
if (double.TryParse(Build.VERSION.Release, out dAndroidVersion))
{
if (dAndroidVersion < 21)
return;
}
#else
ViewCompat.SetElevation(ViewGroup, 8.0f);
ViewCompat.SetTranslationZ(ViewGroup, 10.0f);
#endif
}
}
}
I have created a custom LabelRenderer in my Android app to apply a custom font in a Xamarin Android app (https://developer.xamarin.com/guides/xamarin-forms/user-interface/text/fonts/).
Everything works great for a normal label with the content added to the .Text property. However, if I create a label using .FormattedText property, the custom font is not applied.
Anyone have success doing this? An option, since I'm just stacking lines of different sized text, is to use separate label controls for each, but I'd prefer to use a formatted string if possible.
Here's the guts of my custom renderer:
[assembly: ExportRenderer (typeof (gbrLabel), typeof (gbrLabelRenderer))]
public class gbrLabelRenderer: LabelRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Label> e)
{
base.OnElementChanged (e);
var label = (TextView)Control;
Typeface font = Typeface.CreateFromAsset (Forms.Context.Assets, "Lobster-Regular.ttf");
label.Typeface = font;
}
}
And here's my simple label control... all it does is apply the font to iOS, and leaves applying the font for Android up to the custom renderer.
public class gbrLabel: Label
{
public gbrLabel ()
{
Device.OnPlatform (
iOS: () => {
FontFamily = "Lobster-Regular";
FontSize = Device.GetNamedSize(NamedSize.Medium,this);
}
}
}
Works fine for labels with just the .Text property... but not for labels with the .FormattedText property.
Should I keep digging, or just stack my labels since that's an option in this case?
Here's an example of the various ways I've tried this in the Formatted text, since that was requested:
var fs = new FormattedString ();
fs.Spans.Add (new Span {
Text = string.Format("LINE 1\n",Title),
FontSize = Device.GetNamedSize(NamedSize.Large,typeof(Label))
});
fs.Spans.Add (new Span {
Text = string.Format ("LINE 2\n"),
FontSize = Device.GetNamedSize(NamedSize.Large,typeof(Label)) * 2,
FontAttributes = FontAttributes.Bold,
FontFamily = "Lobster-Regular"
});
fs.Spans.Add (new Span {
Text = string.Format ("LINE 3\n"),
FontSize = Device.GetNamedSize(NamedSize.Medium,typeof(Label)),
FontFamily = "Lobster-Regular.ttf"
});
gbrLabel lblContent = new gbrLabel {
FormattedText = fs
}
None of these (the first should be set by the default class / renderer, and the second 2 are variations of including the font in a span definition itself) work on Android.
Note: Android and iOS issues have been summarized on a blog post: smstuebe.de/2016/04/03/formattedtext.xamrin.forms/
The font is set as long as you do not set FontSize or FontAttributes. So I had the look at the implementation and found that the FormattedText is trying to load the font like the default renderer which doesn't work on Android.
The android formatting system works very similar to that one of Xamarin.Forms. It's using spans to define text attributes. The renderer is adding a FontSpan for every Span with a custom font, size or attribute. Unfortunately, the FontSpanclass is a private inner class of FormattedStringExtensions so we have to deal with reflections.
Our Renderer is updating the Control.TextFormatted on initialization and when the FormattedText property changes. In the update method, we get all FontSpans and replace them with our CustomTypefaceSpan.
Renderer
public class FormattedLabelRenderer : LabelRenderer
{
private static readonly Typeface Font = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Regular.ttf");
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
Control.Typeface = Font;
UpdateFormattedText();
}
private void UpdateFormattedText()
{
if (Element.FormattedText != null)
{
var extensionType = typeof(FormattedStringExtensions);
var type = extensionType.GetNestedType("FontSpan", BindingFlags.NonPublic);
var ss = new SpannableString(Control.TextFormatted);
var spans = ss.GetSpans(0, ss.ToString().Length, Class.FromType(type));
foreach (var span in spans)
{
var start = ss.GetSpanStart(span);
var end = ss.GetSpanEnd(span);
var flags = ss.GetSpanFlags(span);
var font = (Font)type.GetProperty("Font").GetValue(span, null);
ss.RemoveSpan(span);
var newSpan = new CustomTypefaceSpan(Control, font);
ss.SetSpan(newSpan, start, end, flags);
}
Control.TextFormatted = ss;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Label.FormattedTextProperty.PropertyName)
{
UpdateFormattedText();
}
}
}
I'm not sure, why you introduced a new element type gbrLabel, but as long as you only wan't to change the renderer, you don't have to create a custom element. You can replace the renderer of the default element:
[assembly: ExportRenderer(typeof(Label), typeof(FormattedLabelRenderer))]
CustomTypefaceSpan
public class CustomTypefaceSpan : MetricAffectingSpan
{
private readonly Typeface _typeFace;
private readonly Typeface _typeFaceBold;
private readonly Typeface _typeFaceItalic;
private readonly Typeface _typeFaceBoldItalic;
private readonly TextView _textView;
private Font _font;
public CustomTypefaceSpan(TextView textView, Font font)
{
_textView = textView;
_font = font;
// Note: we are ignoring _font.FontFamily (but thats easy to change)
_typeFace = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Regular.ttf");
_typeFaceBold = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Bold.ttf");
_typeFaceItalic = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Italic.ttf");
_typeFaceBoldItalic = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-BoldItalic.ttf");
}
public override void UpdateDrawState(TextPaint paint)
{
ApplyCustomTypeFace(paint);
}
public override void UpdateMeasureState(TextPaint paint)
{
ApplyCustomTypeFace(paint);
}
private void ApplyCustomTypeFace(Paint paint)
{
var tf = _typeFace;
if (_font.FontAttributes.HasFlag(FontAttributes.Bold) && _font.FontAttributes.HasFlag(FontAttributes.Italic))
{
tf = _typeFaceBoldItalic;
}
else if (_font.FontAttributes.HasFlag(FontAttributes.Bold))
{
tf = _typeFaceBold;
}
else if (_font.FontAttributes.HasFlag(FontAttributes.Italic))
{
tf = _typeFaceItalic;
}
paint.SetTypeface(tf);
paint.TextSize = TypedValue.ApplyDimension(ComplexUnitType.Sp, _font.ToScaledPixel(), _textView.Resources.DisplayMetrics);
}
}
Our Custom CustomTypefaceSpanis similar to the FontSpan of Xamarin.Forms, but is loading the custom fonts and can load different fonts for different FontAttributes.
The result is a nice colorful Text :)
I would like to create a layout with a fullscreen background image and some UI elements on top of it. The twist is this:
I would like the background image to swipeable like a carousel, but I would like the UI elements to stay in place. That is if I swipe the screen, the background image should slide to the side and a new image should replace it. I know about CarouselPage, but it seems to me that it won't do the trick, since a Page can have only one child which it replaces on swipe, meaning that the UI elements would be descendants of the CarouselPage and therefore would also be animated.
I am guessing I need some sort of custom renderer here, but how should I go about designing it? Should it be one fullscreen Image control replaced be another fullscreen Image control with the UI elements on top of it? And how can I do this? Or is there an all together better approach?
I am developing for iOS and Android using Xamarin.Forms.
Thanks in advance.
I don't like repeating myself much, and I think that multiple layers of actionable items can lead to confusion, but the problems appeals to me and I can see a niche for this kind of UI, so here's my take on your question.
Let's assume this is the (Xamarin.Forms.)Page you want to render with a custom carousel background:
public class FunkyPage : ContentPage
{
public IList<string> ImagePaths { get; set; }
public FunkyPage ()
{
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
Spacing = 12,
Children = {
new Label { Text = "Foo" },
new Label { Text = "Bar" },
new Label { Text = "Baz" },
new Label { Text = "Qux" },
}
};
ImagePaths = new List<string> { "red.png", "green.png", "blue.png", "orange.png" };
}
}
The renderer for iOS could look like this:
[assembly: ExportRenderer (typeof (FunkyPage), typeof (FunkyPageRenderer))]
public class FunkyPageRenderer : PageRenderer
{
UIScrollView bgCarousel = new UIScrollView (RectangleF.Empty) {
PagingEnabled = true,
ScrollEnabled=true
};
List<UIImageView> uiimages = new List<UIImageView> ();
protected override void OnElementChanged (VisualElementChangedEventArgs e)
{
foreach (var sub in uiimages)
sub.RemoveFromSuperview ();
uiimages.Clear ();
if (e.NewElement != null) {
var page = e.NewElement as FunkyPage;
foreach (var image in page.ImagePaths) {
var uiimage = new UIImageView (new UIImage (image));
bgCarousel.Add (uiimage);
uiimages.Add (uiimage);
}
}
base.OnElementChanged (e);
}
public override void ViewDidLoad ()
{
Add (bgCarousel);
base.ViewDidLoad ();
}
public override void ViewWillLayoutSubviews ()
{
base.ViewWillLayoutSubviews ();
bgCarousel.Frame = View.Frame;
var origin = 0f;
foreach (var image in uiimages) {
image.Frame = new RectangleF (origin, 0, View.Frame.Width, View.Frame.Height);
origin += View.Frame.Width;
}
bgCarousel.ContentSize = new SizeF (origin, View.Frame.Height);
}
}
This was tested and works. Adding a UIPageControl (the dots) is easy on top of this. Autoscrolling of the background is trivial too.
The process is similar on Android, the overrides are a bit different.