You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms如何为ToolbarItem设置字体?自定义Effect失效求助

嘿,我来帮你拆解下这个问题——你的自定义Effect在Label上正常工作,但在ToolbarItem上Android端代码没执行,主要有两个关键原因:

1. Effect的命名匹配出错了

你在共享项目里定义的RoutingEffect用的是base("CustomEffectTest.ChangeToolbarFontEffect"),但Android端的ResolutionGroupName是"bottomNavTest",ExportEffect的名称是"ChangeToolbarFontEffect"。Xamarin.Forms要求共享端的Effect ID必须严格遵循[ResolutionGroupName].[EffectName]的格式,否则系统找不到对应的平台实现,自然不会触发OnAttached方法。

怎么改?
把共享项目里内部类ChangeToolbarFontEffect的构造函数改成这样:

class ChangeToolbarFontEffect : RoutingEffect {
    // 匹配Android端的分组+Effect名称
    public ChangeToolbarFontEffect() : base("bottomNavTest.ChangeToolbarFontEffect") { }
}

2. ToolbarItem在Android上的原生控件不是TextView

就算修正了命名,你还会遇到一个问题:ToolbarItem在Android里对应的是MenuItem,不是直接的TextView,所以你在Android端Effect里写的control = Control as TextView会得到null,字体设置代码根本跑不起来。

解决方案:绕开Control,直接操作Toolbar的菜单
我们需要获取当前页面的原生Toolbar,找到对应的MenuItem再设置字体。修改Android端的Effect代码如下:

[assembly: ResolutionGroupName("bottomNavTest")]
[assembly: ExportEffect(typeof(ChangeToolbarFontEffect), "ChangeToolbarFontEffect")]
namespace bottomNavTest.Droid {
    public class ChangeToolbarFontEffect : PlatformEffect {
        private Android.Support.V7.Widget.Toolbar _toolbar;
        private ToolbarItem _formsItem;
        private string _fontPath;

        protected override void OnAttached() {
            try {
                _formsItem = Element as ToolbarItem;
                if (_formsItem == null) return;

                // 获取当前Activity的Toolbar(需要安装Plugin.CurrentActivity或Xamarin.Essentials)
                var activity = Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity as MainActivity;
                _toolbar = activity?.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
                if (_toolbar == null) return;

                _fontPath = ChangeFontEffect.GetFont(Element);
                if (string.IsNullOrEmpty(_fontPath)) return;

                // 监听菜单准备事件,确保菜单创建后再修改
                _toolbar.MenuPrepared += OnMenuPrepared;
                // 强制刷新菜单,让修改生效
                activity.SupportInvalidateOptionsMenu();
            } catch (Exception e) {
                Console.WriteLine($"Effect attach error: {e.Message}");
            }
        }

        private void OnMenuPrepared(object sender, Android.Support.V7.Widget.Toolbar.MenuPreparedEventArgs e) {
            if (_toolbar == null || string.IsNullOrEmpty(_fontPath)) return;

            // 遍历菜单找到对应的项
            for (int i = 0; i < _toolbar.Menu.Size(); i++) {
                var menuItem = _toolbar.Menu.GetItem(i);
                if (menuItem.TitleFormatted.ToString() == _formsItem.Text) {
                    // 获取MenuItem的TextView(适配不同的菜单布局)
                    var textView = menuItem.ActionView as TextView 
                                  ?? menuItem.ActionProvider?.View.FindViewById<TextView>(Resource.Id.title);
                    if (textView != null) {
                        var typeface = Typeface.CreateFromAsset(Forms.Context.Assets, _fontPath);
                        textView.Typeface = typeface;
                    }
                    break;
                }
            }
        }

        protected override void OnDetached() {
            if (_toolbar != null) {
                _toolbar.MenuPrepared -= OnMenuPrepared;
            }
        }

        protected override void OnElementPropertyChanged(PropertyChangedEventArgs args) {
            base.OnElementPropertyChanged(args);
            if (args.PropertyName == ChangeFontEffect.FontProperty.PropertyName) {
                _fontPath = ChangeFontEffect.GetFont(Element);
                // 字体变化时刷新菜单
                var activity = Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity as MainActivity;
                activity?.SupportInvalidateOptionsMenu();
            }
        }
    }
}

补充提示:

  • 上面的代码用到了Plugin.CurrentActivity来获取当前Activity,你需要在NuGet里安装这个包(或者用Xamarin.Essentials的Platform.CurrentActivity)。
  • 确保你的Android项目里Toolbar的ID是toolbar,如果不是,要把FindViewById里的ID改成你实际用的。
  • 如果你的ToolbarItem带图标,可能需要调整TextView的查找逻辑,比如用自定义的菜单布局来更精准定位。

为什么TitleView能正常工作?

因为TitleView是普通的View容器,里面的Label对应的原生控件就是TextView,所以你的Effect能直接生效。而ToolbarItem属于Android的菜单系统,原生实现逻辑和普通View完全不同,所以需要特殊处理。

内容的提问来源于stack exchange,提问作者j.Doe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 03:59:23