How to show a badges count of ToolBarItem Icon in Xamarin Forms

喜欢而已 提交于 2019-12-10 17:08:40

问题


It is not about how to show notification badges nor it's about to show toolbar item icon. It is clear question that how to show a badges count on a toolbar item icon. ?

I am sharing code to create ToolbarItem with icon in XF content page:

In cs File:

    ToolbarItem cartItem = new ToolbarItem();
    scanItem.Text = "My Cart";
    scanItem.Order = ToolbarItemOrder.Primary;
    scanItem.Icon = "carticon.png";

    ToolbarItems.Add(cartItem );

In Xaml File:

<ContentPage.ToolbarItems>
    <ToolbarItem Text="Cart" Priority="0" x:Name="menu1"> 
    </ToolbarItem>   
  </ContentPage.ToolbarItems>

Now I want to Place a badge count on the above added tool bar item icon. How it can be achieved ?


回答1:


Placing badge icon's in the native toolbars is actually more effort than its worth. If I need a badge icon, I remove the navigation page.

NavigationPage.SetHasNavigationBar(myPageInstance, false);

Then I create my own toolbar from scratch. In this toolbar, I can overlay an image in there, you can also place a number in it as needed. For example.

 <Grid>           
        <Grid.GestureRecognizers>
            <TapGestureRecognizer Command="{Binding IconCommand}" />
        </Grid.GestureRecognizers>

        <iconize:IconImage
                     Icon="fa-drawer"
                     IconColor="white"
                     IconSize="20" />

        <Grid Margin="15,-15,0,0">
            <iconize:IconImage Grid.Row="0"
                       HeightRequest="40"
                       WidthRequest="40"
                       Icon="fa-circle"
                       IconColor="red"
                       IsVisible="{Binding IsCircleVisible}"
                       IconSize="10" />
        </Grid>

    </Grid>

I use Iconize wtih FontAwesome for the icons




回答2:


With the help of Xamarin Forum Discussion, I have achieved it. Read ad understand the complete discussion before implement it. Thank you "Slava Chernikoff", "Emanuele Sabetta", "Mirza Sikander", "Satish" to discuss and yours share code.

Setp 1: Create a Helper Class in PCL and install NGraphics package from nugget.

 public class CartIconHelper
{
    private static Graphic _svgGraphic = null;
    public const string ResourcePath = "ToolBarAndroidBadge.Resources.cartIcon.svg";

    private static PathOp[] RoundRect(NGraphics.Rect rect, double radius)
    {
        return new PathOp[]
                   {
                   new NGraphics.MoveTo(rect.X + radius, rect.Y),
                   new NGraphics.LineTo(rect.X + rect.Width - radius, rect.Y),
                   new NGraphics.ArcTo(new NGraphics.Size(radius, radius), true, false, new NGraphics.Point(rect.X + rect.Width, rect.Y + radius)),
                   new NGraphics.LineTo(rect.X + rect.Width, rect.Y + rect.Height - radius),
                   new NGraphics.ArcTo(new NGraphics.Size(radius, radius), true, false, new NGraphics.Point(rect.X + rect.Width - radius, rect.Y + rect.Height)),
                   new NGraphics.LineTo(rect.X + radius, rect.Y + rect.Height),
                   new NGraphics.ArcTo(new NGraphics.Size(radius, radius), true, false, new NGraphics.Point(rect.X, rect.Y + rect.Height - radius)),
                   new NGraphics.LineTo(rect.X, rect.Y + radius), new NGraphics.ArcTo(new NGraphics.Size(radius, radius), true, false, new NGraphics.Point(rect.X + radius, rect.Y)),
                   new NGraphics.ClosePath()
                   };
    }

    public static string DrawCartIcon(int count, string path, double iconSize = 30, double scale = 2, string fontName = "Arial", double fontSize = 12, double textSpacing = 4)
    {
        var service = DependencyService.Get<IService>();

        var canvas = service.GetCanvas();

        if (_svgGraphic == null) using (var stream = typeof(CartIconHelper).GetTypeInfo().Assembly.GetManifestResourceStream(path))
                _svgGraphic = new SvgReader(new StreamReader(stream)).Graphic; 
                 //st = ReadFully(stream);

        var minSvgScale = Math.Min(canvas.Size.Width / _svgGraphic.Size.Width, canvas.Size.Height / _svgGraphic.Size.Height) / 1.15;

        var w = _svgGraphic.Size.Width / minSvgScale;
        var h = _svgGraphic.Size.Height / minSvgScale;

        _svgGraphic.ViewBox = new NGraphics.Rect(0, -14, w, h);
        _svgGraphic.Draw(canvas);

        if (count > 0)
        {
            var text = count > 99 ? "99+" : count.ToString();
            var font = new NGraphics.Font(fontName, fontSize);
            var textSize = canvas.MeasureText(text, font);

            var textRect = new NGraphics.Rect(canvas.Size.Width - textSize.Width - textSpacing, textSpacing, textSize.Width, textSize.Height);

            if (count < 10)
            {
                var side = Math.Max(textSize.Width, textSize.Height);
                var elipseRect = new NGraphics.Rect(canvas.Size.Width - side - 2 * textSpacing, 0, side + 2 * textSpacing, side + 2 * textSpacing);
                canvas.FillEllipse(elipseRect, NGraphics.Colors.Red);
                textRect -= new NGraphics.Point(side - textSize.Width, side - textSize.Height) / 2.0;
            }
            else
            {
                var elipseRect = new NGraphics.Rect(textRect.Left - textSpacing, textRect.Top - textSpacing, textRect.Width + 2 * textSpacing, textSize.Height + 2 * textSpacing);
                canvas.FillPath(RoundRect(elipseRect, 6), NGraphics.Colors.Red);
            }
            var testReact1= new NGraphics.Rect(20,12,0,0);
            // canvas.DrawText(text, textRect + new NGraphics.Point(0, textSize.Height), font, NGraphics.TextAlignment.Center, NGraphics.Colors.Black);
            canvas.DrawText("5", testReact1, font, NGraphics.TextAlignment.Left, NGraphics.Colors.White);

        }

        service.SaveImage(canvas.GetImage());
        string imagePath = service.GetImage();
        return imagePath;
       // return st;
    }

}

Step 2: Create a interface to IService in PCL

 public interface IService
{
    IImageCanvas GetCanvas();
    void SaveImage(NGraphics.IImage image);
    string GetImage();
}

Step 3 : Implement this interface in your Android project

 class CanvasServices:IService
{
        private readonly AndroidPlatform _platform;
        public CanvasServices()
        {
            _platform = new AndroidPlatform();
        }

        public void SaveImage(IImage image)
        {
        var dir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
        var filePath = System.IO.Path.Combine(dir, "cart.png");
        var stream = new FileStream(filePath, FileMode.Create);
        image.SaveAsPng(stream);
        //bitmap.Compress(image., 100, stream);
        stream.Close();
    }

        public string GetImage()
        {
            var dir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
            var filePath = System.IO.Path.Combine(dir, "cart.png");
            using (var streamReader = new StreamReader(filePath))
            {
                string content = streamReader.ReadToEnd();
                System.Diagnostics.Debug.WriteLine(content);
            }
            return filePath;
        }

        public IImageCanvas GetCanvas()
        {
            NGraphics.Size size = new NGraphics.Size(30);
            return _platform.CreateImageCanvas(size);
        }

        public NGraphics.AndroidPlatform GetPlatform()
        {
            return _platform;
        }
}

Setp 4: Now, use CartIcon Helper in your PCL project to show badges in TabBarItem.

 public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();


        var imagePath = CartIconHelper.DrawCartIcon(2, "ToolBarAndroidBadge.Resources.cartIcon.svg");
        string deviceSepecificFolderPath = Device.OnPlatform(null, imagePath, null);
        object convertedObject = new FileImageSourceConverter().ConvertFromInvariantString(deviceSepecificFolderPath);
        FileImageSource fileImageSource = (FileImageSource)convertedObject;                

        ToolbarItem cartItem = new ToolbarItem();
        cartItem.Text = "My Cart";
        cartItem.Order = ToolbarItemOrder.Primary;
        cartItem.Icon = fileImageSource;




        ToolbarItems.Add(cartItem);

    }
}



回答3:


For any one who wants to add badge on toolbar item using custom ui try, Instead of using default toolbar item, you can hide the default navigation bar by NavigationPage.SetHasNavigationBar(this, false); in the constructor.

Then prepare the custom navigation bar with toolbar item with badge as mentioned in above answers.

If you are using master detail page, hiding default navigation bar will hide hamburger icon, so need to slide from left to see sliding menu. Alternate method would be place a button with hamburger icon in custom navigation bar, on button click use messaging center to present the sliding menu.

Example: On page in which hamburger button is clicked

 private void Button_Clicked(object sender, System.EventArgs e)
        {
            MessagingCenter.Send(this, "presnt");           
        }

On MasterDetail page

MessagingCenter.Subscribe<YourPage>(this, "presnt", (sender) =>
            {
                IsPresented = true;
            });

Before making IsPresented=true, check for sliding menu is not all-ready presented.

Check https://github.com/LeslieCorrea/Xamarin-Forms-Shopping-Cart for badge on toolbar item.




回答4:


Implement below code to draw a ground circle with text over toolbar icon

  1. BarButtonItemExtensions.cs
using CoreAnimation;    
using CoreGraphics;    
using Foundation;    
using ObjCRuntime;    
using System;    
using System.Linq;
using System.Runtime.InteropServices;
using UIKit;

namespace TeamCollaXform.Views.Services
{
    public static class BarButtonItemExtensions
    {
        enum AssociationPolicy
        {
            ASSIGN = 0,
            RETAIN_NONATOMIC = 1,
            COPY_NONATOMIC = 3,
            RETAIN = 01401,
            COPY = 01403,
        }

        static NSString BadgeKey = new NSString(@"BadgeKey");

        [DllImport(Constants.ObjectiveCLibrary)]
        static extern void objc_setAssociatedObject(IntPtr obj, IntPtr key, IntPtr value, AssociationPolicy policy);


        [DllImport(Constants.ObjectiveCLibrary)]
        static extern IntPtr objc_getAssociatedObject(IntPtr obj, IntPtr key);

        static CAShapeLayer GetBadgeLayer(UIBarButtonItem barButtonItem)
        {
            var handle = objc_getAssociatedObject(barButtonItem.Handle, BadgeKey.Handle);

            if (handle != IntPtr.Zero)
            {
                var value = ObjCRuntime.Runtime.GetNSObject(handle);
                if (value != null)
                    return value as CAShapeLayer;
                else
                    return null;
            }
            return null;
        }

        static void DrawRoundedRect(CAShapeLayer layer, CGRect rect, float radius, UIColor color, bool filled)
        {
            layer.FillColor = filled ? color.CGColor : UIColor.White.CGColor;
            layer.StrokeColor = color.CGColor;
            layer.Path = UIBezierPath.FromRoundedRect(rect, radius).CGPath;
        }

        public static void AddBadge(this UIBarButtonItem barButtonItem, string text, UIColor backgroundColor, UIColor textColor, bool filled = true, float fontSize = 11.0f)
        {

            if (string.IsNullOrEmpty(text))
            {
                return;
            }

            CGPoint offset = CGPoint.Empty;

            if (backgroundColor == null)
                backgroundColor = UIColor.Red;

            var font = UIFont.SystemFontOfSize(fontSize);

            if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0))
            {
                font = UIFont.MonospacedDigitSystemFontOfSize(fontSize, UIFontWeight.Regular);
            }

            var view = barButtonItem.ValueForKey(new NSString(@"view")) as UIView;
            var bLayer = GetBadgeLayer(barButtonItem);
            bLayer?.RemoveFromSuperLayer();


            var badgeSize = text.StringSize(font);


            var height = badgeSize.Height;
            var width = badgeSize.Width + 5; /* padding */

            //make sure we have at least a circle
            if (width < height)
            {
                width = height;
            }

            //x position is offset from right-hand side
            var x = view.Frame.Width - width + offset.X;


            var badgeFrame = new CGRect(new CGPoint(x: x - 4, y: offset.Y + 5), size: new CGSize(width: width, height: height));

            bLayer = new CAShapeLayer();
            DrawRoundedRect(bLayer, badgeFrame, 7.0f, backgroundColor, filled);
            view.Layer.AddSublayer(bLayer);

            // Initialiaze Badge's label
            var label = new CATextLayer();
            label.String = text;
            label.TextAlignmentMode = CATextLayerAlignmentMode.Center;
            label.SetFont(CGFont.CreateWithFontName(font.Name));
            label.FontSize = font.PointSize;
            label.Frame = badgeFrame;
            label.ForegroundColor = filled ? textColor.CGColor : UIColor.White.CGColor;
            label.BackgroundColor = UIColor.Clear.CGColor;
            label.ContentsScale = UIScreen.MainScreen.Scale;
            bLayer.AddSublayer(label);

            // Save Badge as UIBarButtonItem property
            objc_setAssociatedObject(barButtonItem.Handle, BadgeKey.Handle, bLayer.Handle, AssociationPolicy.RETAIN_NONATOMIC);

        }
        public static void UpdateBadge(this UIBarButtonItem barButtonItem, string text, UIColor backgroundColor, UIColor textColor)
        {
            var bLayer = GetBadgeLayer(barButtonItem);

            if (string.IsNullOrEmpty(text) || text == "0")
            {
                bLayer?.RemoveFromSuperLayer();

                objc_setAssociatedObject(barButtonItem.Handle, BadgeKey.Handle, new CAShapeLayer().Handle, AssociationPolicy.ASSIGN);
                return;
            }

            var textLayer = bLayer?.Sublayers?.First(p => p is CATextLayer) as CATextLayer;
            if (textLayer != null)
            {
                textLayer.String = text;
            }
            else
            {
                barButtonItem.AddBadge(text, backgroundColor, textColor);
            }
        }

    }
}
  1. ToolbarItemBadgeService.cs
using TeamCollaXform.Views.Services; 
using Xamarin.Forms; 
using Xamarin.Forms.Platform.iOS;

[assembly: Dependency(typeof(ToolbarItemBadgeService))] 
namespace TeamCollaXform.Views.Services 
{
    /// <summary>
    /// 
    /// </summary>
    public interface IToolbarItemBadgeService
    {
        void SetBadge(Page page, ToolbarItem item, string value, Color backgroundColor, Color textColor);
    }

    /// <summary>
    /// 
    /// </summary>
    public class ToolbarItemBadgeService : IToolbarItemBadgeService
    {
        public void SetBadge(Page page, ToolbarItem item, string value, Color backgroundColor, Color textColor)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                var renderer = Platform.GetRenderer(page);
                if (renderer == null)
                {
                    renderer = Platform.CreateRenderer(page);
                    Platform.SetRenderer(page, renderer);
                }
                var vc = renderer.ViewController;

                var rightButtomItems = vc?.ParentViewController?.NavigationItem?.RightBarButtonItems;
                var idx = rightButtomItems.Length - page.ToolbarItems.IndexOf(item) - 1;            //Revert

                if (rightButtomItems != null && rightButtomItems.Length > idx)
                {
                    var barItem = rightButtomItems[idx];
                    if (barItem != null)
                    {
                        barItem.UpdateBadge(value, backgroundColor.ToUIColor(), textColor.ToUIColor());
                    }
                }

            });
        }
    } 
}
  1. Usage
    void OnAttachClicked(object sender, EventArgs e)
    {
        //var answer = await DisplayAlert("Question?", "Would you like to play a game", "Yes", "No");
        //Debug.WriteLine("Answer: " + answer);

        ToolbarItem cmdItem = sender as ToolbarItem;

        DependencyService.Get<IToolbarItemBadgeService>().SetBadge(this, cmdItem, $"2", Color.DarkOrange, Color.White);
    }

Links: 1) for instruction and 2) for sample code

  1. https://www.xamboy.com/2018/03/08/adding-badge-to-toolbaritem-in-xamarin-forms/
  2. https://github.com/CrossGeeks/ToolbarItemBadgeSample


来源:https://stackoverflow.com/questions/43843654/how-to-show-a-badges-count-of-toolbaritem-icon-in-xamarin-forms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!