簡體   English   中英

如何在Xamarin Forms中顯示ToolBarItem Icon的徽章數量

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

它不是關於如何顯示通知標記,也不是顯示工具欄項圖標。 很明顯,如何在工具欄項目圖標上顯示徽章數量。

我正在共享代碼以在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>

現在我想在上面添加的工具欄項目圖標上放置徽章計數。 如何實現?

在原生工具欄中放置徽章圖標實際上是比它的價值更多的努力。 如果我需要徽章圖標,我會刪除導航頁面。

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>

我使用帶有FontAwesome的 Iconize作為圖標

Xamarin論壇討論的幫助下,我實現了它。 閱讀廣告在實施之前了解完整的討論。 謝謝你“Slava Chernikoff”,“Emanuele Sabetta”,“Mirza Sikander”,“Satish”來討論和你的共享代碼。

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

第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);

    }
}

對於任何想要使用自定義ui嘗試在工具欄項上添加徽章的人,可以使用NavigationPage.SetHasNavigationBar(this, false);隱藏默認導航欄NavigationPage.SetHasNavigationBar(this, false);而不是使用默認工具欄項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以獲取工具欄項目上的徽章。

實現下面的代碼,在工具欄圖標上繪制帶有文本的地面圓圈

  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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM