-
-
Save rudyryk/8cbe067a1363b45351f6 to your computer and use it in GitHub Desktop.
// | |
// Badge.cs | |
// Created by Alexey Kinev on 19 Jan 2015. | |
// | |
// Licensed under The MIT License (MIT) | |
// http://opensource.org/licenses/MIT | |
// | |
// Copyright (c) 2015 Alexey Kinev <[email protected]> | |
// | |
using System; | |
using Xamarin.Forms; | |
namespace ZeroFiveBit.Forms.Basic | |
{ | |
/// <summary> | |
/// Badge. | |
/// </summary> | |
public class Badge : AbsoluteLayout | |
{ | |
/// <summary> | |
/// The text property. | |
/// </summary> | |
public static readonly BindableProperty TextProperty = | |
BindableProperty.Create("Text", typeof(String), typeof(Badge), ""); | |
/// <summary> | |
/// The box color property. | |
/// </summary> | |
public static readonly BindableProperty BoxColorProperty = | |
BindableProperty.Create("BoxColor", typeof(Color), typeof(Badge), Color.Default); | |
/// <summary> | |
/// The text. | |
/// </summary> | |
public string Text | |
{ | |
get { return (string)GetValue(TextProperty); } | |
set { SetValue(TextProperty, value); } | |
} | |
/// <summary> | |
/// Gets or sets the color of the box. | |
/// </summary> | |
public Color BoxColor | |
{ | |
get { return (Color)GetValue(BoxColorProperty); } | |
set { SetValue(BoxColorProperty, value); } | |
} | |
/// <summary> | |
/// The box. | |
/// </summary> | |
protected RoundedBox Box; | |
/// <summary> | |
/// The label. | |
/// </summary> | |
protected Label Label; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ZeroFiveBit.Forms.Basic.Badge"/> class. | |
/// </summary> | |
public Badge(double size, double fontSize) | |
{ | |
HeightRequest = size; | |
WidthRequest = HeightRequest; | |
// Box | |
Box = new RoundedBox { | |
CornerRadius = HeightRequest / 2 | |
}; | |
Box.SetBinding(BackgroundColorProperty, new Binding("BoxColor", source: this)); | |
Children.Add(Box, new Rectangle(0, 0, 1.0, 1.0), AbsoluteLayoutFlags.All); | |
// Label | |
Label = new Label { | |
TextColor = Color.White, | |
FontSize = fontSize, | |
XAlign = TextAlignment.Center, | |
YAlign = TextAlignment.Center | |
}; | |
Label.SetBinding(Label.TextProperty, new Binding("Text", | |
BindingMode.OneWay, source: this)); | |
Children.Add(Label, new Rectangle(0, 0, 1.0, 1.0), AbsoluteLayoutFlags.All); | |
// Auto-width | |
SetBinding(WidthRequestProperty, new Binding("Text", BindingMode.OneWay, | |
new BadgeWidthConverter(WidthRequest), source: this)); | |
// Hide if no value | |
SetBinding(IsVisibleProperty, new Binding("Text", BindingMode.OneWay, | |
new BadgeVisibleValueConverter(), source: this)); | |
// Default color | |
BoxColor = Color.Red; | |
} | |
} | |
/// <summary> | |
/// Badge visible value converter. | |
/// </summary> | |
class BadgeVisibleValueConverter : IValueConverter | |
{ | |
#region IValueConverter implementation | |
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) | |
{ | |
var text = value as string; | |
return !String.IsNullOrEmpty(text); | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
#endregion | |
} | |
/// <summary> | |
/// Badge width converter. | |
/// </summary> | |
class BadgeWidthConverter : IValueConverter | |
{ | |
/// <summary> | |
/// The width of the base. | |
/// </summary> | |
readonly double baseWidth; | |
/// <summary> | |
/// The width ratio. | |
/// </summary> | |
const double widthRatio = 0.33; | |
public BadgeWidthConverter(double baseWidth) | |
{ | |
this.baseWidth = baseWidth; | |
} | |
#region IValueConverter implementation | |
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) | |
{ | |
var text = value as string; | |
if ((text != null) && (text.Length > 1)) | |
{ | |
// We won't measure text length exactly here! | |
// May be we should, but it's too tricky. So, | |
// we'll just approximate new badge width as | |
// linear function from text legth. | |
return baseWidth * (1 + widthRatio * (text.Length - 1)); | |
} | |
return baseWidth; | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
#endregion | |
} | |
} | |
// | |
// RoundedBox.cs | |
// Created by Alexey Kinev on 19 Jan 2015. | |
// | |
// Licensed under The MIT License (MIT) | |
// http://opensource.org/licenses/MIT | |
// | |
// Copyright (c) 2015 Alexey Kinev <[email protected]> | |
// | |
using System; | |
using Xamarin.Forms; | |
namespace ZeroFiveBit.Forms.Basic | |
{ | |
public class RoundedBox : BoxView | |
{ | |
/// <summary> | |
/// The corner radius property. | |
/// </summary> | |
public static readonly BindableProperty CornerRadiusProperty = | |
BindableProperty.Create("CornerRadius", typeof(double), typeof(RoundedBox), 0.0); | |
/// <summary> | |
/// Gets or sets the corner radius. | |
/// </summary> | |
public double CornerRadius | |
{ | |
get { return (double)GetValue(CornerRadiusProperty); } | |
set { SetValue(CornerRadiusProperty, value); } | |
} | |
} | |
} | |
// | |
// RoundedBoxRenderer_Droid.cs | |
// Created by Alexey Kinev on 26 Apr 2015. | |
// | |
// Licensed under The MIT License (MIT) | |
// http://opensource.org/licenses/MIT | |
// | |
// Copyright (c) 2015 Alexey Kinev <[email protected]> | |
// | |
using System; | |
using System.ComponentModel; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Platform.Android; | |
using Android.Graphics; | |
using ZeroFiveBit.Forms.Basic; | |
using ZeroFiveBit.Forms.Basic.Droid; | |
[assembly: ExportRenderer(typeof(RoundedBox), typeof(RoundedBoxRenderer))] | |
namespace ZeroFiveBit.Forms.Basic.Droid | |
{ | |
public class RoundedBoxRenderer : BoxRenderer | |
{ | |
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e) | |
{ | |
base.OnElementChanged(e); | |
SetWillNotDraw(false); | |
Invalidate(); | |
} | |
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
base.OnElementPropertyChanged(sender, e); | |
if (e.PropertyName == RoundedBox.CornerRadiusProperty.PropertyName) | |
{ | |
Invalidate(); | |
} | |
} | |
public override void Draw(Canvas canvas) | |
{ | |
var box = Element as RoundedBox; | |
var rect = new Rect(); | |
var paint = new Paint() { | |
Color = box.BackgroundColor.ToAndroid(), | |
AntiAlias = true, | |
}; | |
GetDrawingRect(rect); | |
var radius = (float)(rect.Width() / box.Width * box.CornerRadius); | |
canvas.DrawRoundRect(new RectF(rect), radius, radius, paint); | |
} | |
} | |
} | |
// | |
// RoundedBoxRenderer_iOS.cs | |
// Created by Alexey Kinev on 19 Jan 2015. | |
// | |
// Licensed under The MIT License (MIT) | |
// http://opensource.org/licenses/MIT | |
// | |
// Copyright (c) 2015 Alexey Kinev <[email protected]> | |
// | |
using System; | |
using System.ComponentModel; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Platform.iOS; | |
using ZeroFiveBit.Forms.Basic; | |
using ZeroFiveBit.Forms.Basic.iOS; | |
[assembly: ExportRenderer(typeof(RoundedBox), typeof(RoundedBoxRenderer))] | |
namespace ZeroFiveBit.Forms.Basic.iOS | |
{ | |
public class RoundedBoxRenderer : BoxRenderer | |
{ | |
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e) | |
{ | |
base.OnElementChanged(e); | |
if (Element != null) | |
{ | |
Layer.MasksToBounds = true; | |
UpdateCornerRadius(Element as RoundedBox); | |
} | |
} | |
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
base.OnElementPropertyChanged(sender, e); | |
if (e.PropertyName == RoundedBox.CornerRadiusProperty.PropertyName) | |
{ | |
UpdateCornerRadius(Element as RoundedBox); | |
} | |
} | |
void UpdateCornerRadius(RoundedBox box) | |
{ | |
Layer.CornerRadius = (float)box.CornerRadius; | |
} | |
} | |
} | |
Hey @rudyryk - I get an error for this line:
HeightRequest = Metrics.Pixels(size);
Says Metrics not found :(
HeightRequest = Metrics.Pixels(size);
Says Metrics not found . what should i do.. if i just give some random value, nothing is working, pls reply.
In case people are still having issues getting the roundedBox to work. I made the following changes which fixed the issue.
// Android MainActivity.cs
protected override void OnCreate (Bundle bundle)
{
//Add in the beginning
App.ScreenHeight = (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density);
....
}
//iOS AppDelegate.cs
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
App.ScreenHeight = (int)UIScreen.MainScreen.Bounds.Height;
...
}
//Shared Code App.cs
public class App : Application
{
static public int ScreenHeight;
...
//HeightRequest replacement Badge.cs
public Badge(double size, double fontSize)
{
HeightRequest = App.ScreenHeight;
WidthRequest = HeightRequest;
Oh, sorry I didn't see comments earlier.
Metrics.Pixels(size)
is actually not necessary and could be replaced with size
, so I did it. That was my helper to scale elements automatically for iPhone / iPad development.
Hi @stammi922! Hmm, seems like you're trying to set badge height to screen height. That's not an idea :) Badge just should have size (in points) specified as constructor parameter.
Hi @rudyryk, thank's for this! I implemented the same thing with a complete different approach in iOS but needed it for Android too so tried using your code. Works great on iOS, however if I put badges in a ListView, initially they show up good, but when I start scrolling, there are some weird artifacts that sometimes appear. Have you noticed this? Is their a way to fix that?
@rudyryk will you please give me screen shots how this code's output is coming? It will be helpful.
Thanks in advance :)
Hey @rudyryk the example was very helpful. But I have a problem, I am using ViewRender for RelativeLayout to convert that it into rounded corner layout. And I am able to do it with your codes help. But the problem is that whatever children I have added, does not shows up inside that. Is it because of the Draw method, because it is called after I add the children in the extended RelativeLayout. Thanks
I am having the same issue as @repper, anyone got any ideas please?
Hi @repper
I am using ViewRender for RelativeLayout to convert that it into rounded corner layout.
Hm, I think that won't work simply like that. Probably I'm missing something, but Xamarin.Forms didn't provide valid hooks to replace layout renderers. There are predefined renderers for simple views in Forms like BoxRenderer
etc. and we can tweak them as I usually did.
I noticed that i had to add horizontal and vertical option. Otherwise i was getting rectangles.
<controls:Badge Grid.Row="0" Grid.Column="2" Text="3" WidthRequest="25" HeightRequest="25"
VerticalOptions="Center"HorizontalOptions="Center"/>
@rudyryk, I'm not able to add to toolbaritem is there any way add on that at run time?
Waiting for your response,.....
Hi Sandeep,
Have you able to added the badge to toolbar item?
@padhisandeepkumar @sportzbee Adding a badge to toolbar item can't be simply done with Xamarin.Forms. It has to be implemented per-platform and this component isn't really helpful for that case.
Actually, it's probably even easier :) Because iOS8+ natively supports UIBarButton.badgeValue
! Not sure about Android, though.
Xaml sample:
<badge:Badge Text="3" WidthRequest="25" HeightRequest="25" VerticalOptions="Center" HorizontalOptions="Center" BoxColor="Blue">
<x:Arguments>
<x:Double>30</x:Double>
<x:Double>12</x:Double>
</x:Arguments>
</badge:Badge>
@jaisont07: Do you have a fix already for the misalignment? I'm facing the same issue
How to use it? can someone show an example of implementation? I just learning xamarin forms.
@sportzbee I added the badge to the ToolbarItem for iOS using a custom renderer as explained in this blog post.
By the way, if anyone has been able to do the same for Android, I'm interested, I've googled for a solution without any luck.
How can i use it? I have grid and inside it i have 7 image(Grid.Row="0" Grid.Column="0" and so on) how can i do to show badge each cell? The values that will be shown comes from a method like this(the value to be show is resultado):
decimal realizado = 0;
decimal previsto = 0;
foreach (var item in indItem)
{
var val = item.Nome == "Previsto" ? previsto = item.Valor : realizado = item.Valor;
}
decimal desvio = Math.Abs(previsto - realizado);
if (desvio < 1000)
resultado = Math.Truncate(desvio).ToString();
else
while (desvio >= 1000 && siglas.Count > 0)
{
desvio /= 1000;
resultado = Math.Truncate(desvio) + siglas[0];
siglas.RemoveAt(0);
}
if (CorIndicador == "VERDE")
image.Source = ImageSource.FromResource("Estapar.AppOperacional.Images.faturamento caixa-28.png");
else
{
var pinta = CorIndicador == "VERMELHO" ? image.Source = ImageSource.FromResource("Estapar.AppOperacional.Images.faturamento caixa-26.png") : image.Source = ImageSource.FromResource("Estapar.AppOperacional.Images.faturamento caixa-27.png");
}
label.Text = resultado;
and my xaml is like this
<ContentPage.Content>
<StackLayout Padding="0" Spacing="0" Margin="0">
<Grid x:Name="grd" RowSpacing="1" ColumnSpacing="1" Padding="0" Margin="0" >
<Grid.RowDefinitions>
<RowDefinition Height="1" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="imgDesvioFat" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Source="{local:ImageResource Operacional.Images.faturamento caixa-28.png}" Aspect="AspectFill">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnDesvioFaturamentoTapReconizerTapped" NumberOfTapsRequired="1"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
<!--<Label x:Name="lblFaturamento" Text="" Grid.Row="0" Grid.Column="0" TextColor="White" FontSize="9" FontAttributes="Bold" />-->
<!--</Grid>-->
<Image x:Name="imgTckCancelados" Grid.Row="1" Grid.Column="1" Source="{local:ImageResource Operacinal.Images.tickets cancelados-05.png}" Aspect="AspectFill">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTckCanceladosTapGestureReconizerTapped" NumberOfTapsRequired="1"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
i shown just two images, but i have 7 images and the code is such. How can i show the badge and the value inside with my code?
Hi Loan! That's strange,
BoxRenderer
should be available viaXamarin.Forms.Platform.Android.BoxRenderer
orXamarin.Forms.Platform.iOS.BoxRenderer
.