Olá, neste post irei demonstrar como criar um BadgeView utilizando custom renderer em aplicações Xamarin.Forms.
Em seu projeto portable, crie a classe CircleBox.
using Xamarin.Forms; | |
namespace BadgeView | |
{ | |
public class CircleBox : BoxView | |
{ | |
// Corner radius property. | |
public static readonly BindableProperty CornerRadiusProperty = | |
BindableProperty.Create("CornerRadius", typeof(double), typeof(CircleBox), 0.0); | |
// Get and set the corner radius. | |
public double CornerRadius | |
{ | |
get { return (double) GetValue(CornerRadiusProperty); } | |
set { SetValue(CornerRadiusProperty, value); } | |
} | |
} | |
} |
Ainda no portable, crie uma classe Badge, como demonstrado a seguir:
using System; | |
using Xamarin.Forms; | |
namespace BadgeView | |
{ | |
public class Badge: AbsoluteLayout | |
{ | |
protected Label Label; | |
protected CircleBox Box; | |
// Text property | |
public static readonly BindableProperty TextProperty = | |
BindableProperty.Create("Text", typeof(String), typeof(Badge), ""); | |
//Box color property | |
public static readonly BindableProperty BoxColorProperty = | |
BindableProperty.Create("BoxColor", typeof(Color), typeof(Badge), Color.Default); | |
public Badge(double size, double fontSize) | |
{ | |
HeightRequest = size; | |
WidthRequest = size; | |
// Box | |
Box = new CircleBox | |
{ | |
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)); | |
// If not value = hide | |
SetBinding(IsVisibleProperty, new Binding("Text", BindingMode.OneWay, new BadgeVisibleValueConverter(), source: this)); | |
// Default color | |
BoxColor = Color.Red; | |
} | |
// Text | |
public string Text | |
{ | |
get { return (string)GetValue(TextProperty); } | |
set { SetValue(TextProperty, value); } | |
} | |
// Color of the box | |
public Color BoxColor | |
{ | |
get { return (Color)GetValue(BoxColorProperty); } | |
set { SetValue(BoxColorProperty, value); } | |
} | |
} | |
class BadgeWidthConverter : IValueConverter | |
{ | |
// Width ratio. | |
const double widthRatio = 0.33; | |
//Width of the base. | |
readonly double baseWidth; | |
public BadgeWidthConverter(double baseWidth) | |
{ | |
this.baseWidth = baseWidth; | |
} | |
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) | |
{ | |
var text = value as string; | |
if ((text != null) && (text.Length > 1)) | |
{ | |
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(); | |
} | |
} | |
class BadgeVisibleValueConverter : IValueConverter | |
{ | |
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(); | |
} | |
} | |
} |
(Portable):
Android
Em seu projeto .android crie a classe CircleBoxRenderer.
using Xamarin.Forms; | |
using Xamarin.Forms.Platform.Android; | |
using System.ComponentModel; | |
using Android.Graphics; | |
using BadgeView; | |
using BadgeView.Droid; | |
[assembly: ExportRenderer(typeof(CircleBox), typeof(CircleBoxRenderer))] | |
namespace BadgeView.Droid | |
{ | |
public class CircleBoxRenderer : 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 == CircleBox.CornerRadiusProperty.PropertyName) | |
{ | |
Invalidate(); | |
} | |
} | |
public override void Draw(Canvas canvas) | |
{ | |
var box = Element as CircleBox; | |
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); | |
} | |
} | |
} |
.Android:
iOS
Também crie uma classe CircleBoxRenderer no seu projeto .iOS, porém como demonstrada a seguir:
using Xamarin.Forms; | |
using BadgeView.iOS; | |
using BadgeView; | |
using Xamarin.Forms.Platform.iOS; | |
using System.ComponentModel; | |
[assembly: ExportRenderer(typeof(CircleBox), typeof(CircleBoxRenderer))] | |
namespace BadgeView.iOS | |
{ | |
public class CircleBoxRenderer : BoxRenderer | |
{ | |
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e) | |
{ | |
base.OnElementChanged(e); | |
if (Element != null) | |
{ | |
Layer.MasksToBounds = true; | |
UpdateCornerRadius(Element as CircleBox); | |
} | |
} | |
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
base.OnElementPropertyChanged(sender, e); | |
if (e.PropertyName == CircleBox.CornerRadiusProperty.PropertyName) | |
{ | |
UpdateCornerRadius(Element as CircleBox); | |
} | |
} | |
void UpdateCornerRadius(CircleBox box) | |
{ | |
Layer.CornerRadius = (float)box.CornerRadius; | |
} | |
} | |
} |
.iOS:
Pronto, seu BadgeView já esta criado e agora você pode utiliza-lo em qualquer página do seu aplicativo.
Xaml
No arquivo xaml, crie um “Badge”, adicione a cor desejada na propriedade “BoxColor”, informe o tamanho do círculo nas propriedades “WidthRequest” e “HeightRequest”. Por último, passe dois argumentos do tipo Double, sendo eles: CornerRadius e FontSize (Tamanho do texto no interior do Badge).
Apenas para fins de demonstração eu adicionei um botão que quando clicado irá incrementar o valor 1 no contador de notificações. Porém, no seu caso você pode realizar binding com a propriedade que desejar. (<local:Badge Text=”{Binding Notifications}”)
… | |
<ContentPage.Content> | |
<StackLayout VerticalOptions="Center"> | |
<StackLayout Orientation="Horizontal" HorizontalOptions="Center"> | |
<Label HorizontalTextAlignment="Center" Text="Notifications" FontSize="18"/> | |
<local:Badge x:Name="BadgeNotifications" BoxColor="red" | |
WidthRequest="18" HeightRequest="18" | |
VerticalOptions="Center" HorizontalOptions="Center"> | |
<x:Arguments> | |
<x:Double>30</x:Double> | |
<x:Double>10</x:Double> | |
</x:Arguments> | |
</local:Badge> | |
</StackLayout> | |
<Button Text="Add Notification" Clicked="AddNotification"/> | |
</StackLayout> | |
</ContentPage.Content> | |
… |
C#
… | |
public MainPage() | |
{ | |
InitializeComponent(); | |
BadgeNotifications.Text = "2"; | |
} | |
private void AddNotification(object sender, EventArgs e) | |
{ | |
if (BadgeNotifications.Text != "99+" && int.Parse(BadgeNotifications.Text) < 99 ) | |
BadgeNotifications.Text = (int.Parse(BadgeNotifications.Text)+1).ToString(); | |
else | |
{ | |
BadgeNotifications.Text = "99+"; | |
} | |
} | |
… |
Resultado
Esse e todos os exemplos deste blog encontram-se disponíveis no GitHub.
Juliano, como eu faria para mostrar esse badge em cima de uma botão ou de uma small image, tipo ícone ou um png de 48 X 48?
CurtirCurtir
Olá Paulo,
Uma alternativa é você criar o seu botão utilizando um StackLayout com GestureRecognizers para realizar o click. E dentro dele adicionar a badge.
Espero ter ajudado. Abraço
CurtirCurtir
Então, o badge está ficando fora do botão. Se eu aumento o width do botão o badge é jogado mais ao lado. O que devo alterar para ter o badge em cima do botão, não quero aqui dizer que sou novo e por isso tenho dúvida, mas não estou conseguindo utilizar corretamente. Tentei incluir no meu projeto e lá não funciona, não dá erro mas não aparece nada. É só isso, mas o seu exemplo é exatamente o que eu necessito, exatamente. Me desculpa mas falta alguma coisa ainda.
CurtirCurtir
Olá Paulo,
O exemplo que encontra-se no Github está funcionando, eu escrevi um código que faz a mesma coisa do exemplo, a diferença que é um botão que eu criei com um StackLayout.
Se preferir baixe o projeto do Github e substitua o xaml
MainPage.xaml
hosted with ❤ by GitHub
Espero ter ajudado.
CurtirCurtir
Eu tenho esse link e não tem essa nova versão:
https://julianocustodio.com/2017/11/10/badgeview/
CurtirCurtir
Tentei incluir as classes e o badge(xaml) e não estou conseguindo fazer funcionar. É como se ele não reconhecesse a classe Badge no PCL. Baixei seu exemplo do git e funciona, mas se coloco no meu projeto eu não consigo fazer funcionar. Poderia me ajudar nisso?
CurtirCurtir
Juliano, descobri porque que não estava funcionando no meu projeto. Era por causa disto:
BoxColor=”red” e eu troquei para isso: BoxColor=”Red” mas no seu exemplo está funcionando e não se porque, mas resolveu no meu. Valeu!!
CurtirCurtir