【问题标题】:How to show a badges count of ToolBarItem Icon in Xamarin Forms如何在 Xamarin 表单中显示 ToolBarItem 图标的徽章计数
【发布时间】:2017-10-06 05:17:23
【问题描述】:

这不是关于如何显示通知徽章,也不是关于显示工具栏项目图标。很明显的问题是,如何在工具栏项目图标上显示徽章计数。 ?

我正在共享代码以在 XF 内容页面中创建带有图标的 ToolbarItem:

在cs文件中:

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

    ToolbarItems.Add(cartItem );

在 Xaml 文件中:

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

现在我想在上面添加的工具栏项目图标上放置一个徽章计数。它是如何实现的?

【问题讨论】:

  • 您不能直接从表单中执行此操作。我知道iOS有它内置的,不确定Android
  • @GeraldVersluis 是的,你是对的。 iOS 有添加徽章的功能,但 android 没有。我坚持在 Android 中创建,就像在 iOS 中一样。
  • 本教程有你所需要的:xamboy.com/2018/03/08/…
  • @AjaySharma 即使我需要放置购物车图标...你能告诉我你是怎么做到的吗?

标签: xaml xamarin xamarin.android xamarin.forms xamarin-studio


【解决方案1】:

在本机工具栏中放置徽章图标实际上比它的价值更多。如果我需要徽章图标,我会删除导航页面。

NavigationPage.SetHasNavigationBar(myPageInstance, false);

然后我从头开始创建自己的工具栏。在这个工具栏中,我可以在其中叠加一个图像,您也可以根据需要在其中放置一个数字。例如。

 <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>

我使用 Iconize 和 FontAwesome 作为图标

【讨论】:

  • 感谢您的回答,毫无疑问,这可能是一个很好的方法。还有更多几种方法可以实现这一点。但我绝对想对工具栏项做同样的事情,这就是我发布这个问题的原因。
  • @AdamPedley 一旦你有了那个网格,你是怎么把它放到你的工具栏中的?然后将数字放在圆圈中,您只需使用边距将数字覆盖在“fa-circle”上?
  • 我不使用工具栏,我只是在页面顶部放置一个 Grid,并创建自己的工具栏。目前,内置的 Xamarin.Forms 是一种超越基本功能的真正痛苦。
  • 嗨,Adam,Iconize 是否会增加项目规模,是否会减慢 Android 的启动时间?
【解决方案2】:

Xamarin Forum Discussion的帮助下,我实现了。在实施之前阅读广告了解完整的讨论。感谢“Slava Chernikoff”、“Emanuele Sabetta”、“Mirza Sikander”、“Satish”的讨论和你们的分享代码。

Setp 1:在 PCL 中创建一个 Helper 类并从 nugget 安装 NGraphics 包。

 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;
    }

}

第 2 步:在 PCL 中创建到 IService 的接口

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

第 3 步:在您的 Android 项目中实现此接口

 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:现在,在您的 PCL 项目中使用 CartIcon Helper 在 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);

    }
}

【讨论】:

  • iOS 呢?如何实施?
  • @rraallvv - 我在 Xamarin 的工作经验最少,但在 .NET 方面有足够的经验。我可以知道你为什么问这个吗?
【解决方案3】:

对于任何想要使用自定义 ui 在工具栏项上添加徽章的人,请尝试, 您可以通过NavigationPage.SetHasNavigationBar(this, false);隐藏默认导航栏,而不是使用默认工具栏项 在构造函数中。

然后按照上面的答案准备带有带有徽章的工具栏项的自定义导航栏。

如果您使用主详情页面,隐藏默认导航栏将隐藏汉堡图标,因此需要从左侧滑动才能看到滑动菜单。另一种方法是在自定义导航栏中放置一个带有汉堡图标的按钮,单击按钮时使用消息中心显示滑动菜单。

示例:点击汉堡按钮的页面

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

在 MasterDetail 页面上

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

在制作IsPresented=true之前,检查滑动菜单是否已经准备就绪。

检查https://github.com/LeslieCorrea/Xamarin-Forms-Shopping-Cart 工具栏项目上的徽章。

【讨论】:

    【解决方案4】:

    实现下面的代码以在工具栏图标上绘制一个带有文本的圆形

    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. 用法
        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);
        }
    

    链接:1)用于说明和 2)用于示例代码

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-22
      • 1970-01-01
      • 1970-01-01
      • 2020-01-18
      • 1970-01-01
      • 2013-10-11
      • 2018-10-04
      相关资源
      最近更新 更多