使用字体库的图标

为了对主题进行更深度的定制化,可能需要创建一些图标并将图标嵌入字体库,或者使用外部的图标字体库。作为示例,我们使用 Brands 样式的 Font Awesome 5 库。

  1. 为新的图标创建枚举类,实现 com.vaadin.server.FontIcon 接口:

    public enum FontAwesome5Brands implements FontIcon {
    
        JAVA(0XF4E4);
    
        public static final String FONT_FAMILY = "FontAwesome5Brands";
        private final int codepoint;
    
        FontAwesome5Brands(int codepoint) {
            this.codepoint = codepoint;
        }
    
    
        @Override
        public String getFontFamily() {
            return FONT_FAMILY;
        }
    
        @Override
        public int getCodepoint() {
            return codepoint;
        }
    
        @Override
        public String getHtml() {
            return GenericFontIcon.getHtml(FONT_FAMILY, codepoint);
        }
    
        @Override
        public String getMIMEType() {
            throw new UnsupportedOperationException(FontIcon.class.getSimpleName()
                    + " should not be used where a MIME type is needed.");
        }
    
        public static FontAwesome5Brands fromCodepoint(final int codepoint) {
            for (FontAwesome5Brands f : values()) {
                if (f.getCodepoint() == codepoint) {
                    return f;
                }
            }
            throw new IllegalArgumentException(
                    "Codepoint " + codepoint + " not found in FontAwesome 5");
        }
    }
  2. 将新样式添加至 自定义主题。我们建议在自定义主题的主目录创建一个子目录 fonts,例如,themes/helium-extended/fonts。然后将样式和字体文件放在各自的子目录中,例如,fonts/fontawesome

    字体文件支持下列扩展名:

    • .eot

    • .svg

    • .ttf

    • .woff

    • .woff2

    Brands 样式的 fontawesome 字体由 5 个文件组成:fa-brands-400.eotfa-brands-400.svgfa-brands-400.ttffa-brands-400.wofffa-brands-400.woff2

    如需使用其他样式(SolidRegular 等),则需要为每一种样式定义唯一的类名。同时,也需要分别实现 IconSetsProviders

  3. 创建一个包含 @font-face 样式的文件和具有图标样式的 CSS 类。下面是 fontawesome5.scss 文件的示例,其中 FontAwesome5Brands CSS 类名对应于 FontIcon.getFontFamily() 方法返回的值:

    @mixin font-icon-style {
      speak: none;
      font-style: normal;
      font-weight: normal;
      font-variant: normal;
      text-transform: none;
      line-height: 1;
    
      /* Better Font Rendering =========== */
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    
    /* FontAwesome 5 Brands */
    
    @mixin font-awesome-5-brands-style {
      font-family: 'FontAwesome5Brands';
      @include font-icon-style;
    }
    
    @font-face {
      font-family: 'FontAwesome5Brands';
      src: url('fa-brands-400.eot?hwgbks');
      src: url('fa-brands-400.eot?hwgbks#iefix') format('embedded-opentype'),
      url('fa-brands-400.ttf?hwgbks') format('truetype'),
      url('fa-brands-400.woff?hwgbks') format('woff'),
      url('fa-brands-400.svg?hwgbks#icomoon') format('svg');
      font-weight: normal;
      font-style: normal;
    }
    
    .FontAwesome5Brands {
      @include font-awesome-5-brands-style;
    }
  4. 在自定义主题的 helium-extended.scss 或其他文件中,引入该字体样式文件:

    @import "fonts/fontawesome5/fontawesome5";
  5. 然后,创建一个新的 图标集,这是一个实现了 Icons.Icon 接口的枚举:

    public enum FontAwesome5Icon implements Icons.Icon {
    
        JAVA("font-awesome5-brands-icon:JAVA");
    
        protected String source;
    
        FontAwesome5Icon(String source) {
            this.source = source;
        }
    
        @Override
        public String source() {
            return source;
        }
    
        @Override
        public String iconName() {
            return name();
        }
    }
  6. 创建新的 IconProvider

    为了管理自定义的图标集,Jmix 框架提供了 IconProviderIconResolver

    IconProvider 是一个标记接口,可以用图标路径提供资源(com.vaadin.server.Resource)。

    IconResolver bean 会获取所有实现了 IconProvider 接口的 bean,并与之交互找到可以提供对应图标资源的类。

    因此,需要自己实现 IconProvider

    @Order(10)
    @Component("sample_FontAwesome5BrandsIconProvider")
    public class FontAwesome5BrandsIconProvider implements IconProvider {
    
        public static final String FONT_AWESOME_5_BRANDS_PREFIX = "font-awesome5-brands-icon:";
        private final Logger log = LoggerFactory.getLogger(FontAwesome5BrandsIconProvider.class);
    
        @Override
        public Resource getIconResource(String iconPath) {
            Resource resource = null;
    
            iconPath = iconPath.split(":")[1];
    
            try {
                resource = ((Resource) FontAwesome5Brands.class
                        .getDeclaredField(iconPath)
                        .get(null));
            } catch (IllegalAccessException | NoSuchFieldException e) {
                log.warn("There is no icon with name {} in the FontAwesome5Brands icon set", iconPath);
            }
    
            return resource;
        }
    
        @Override
        public boolean canProvide(String iconPath) {
            return !Strings.isNullOrEmpty(iconPath)
                    && iconPath.startsWith(FONT_AWESOME_5_BRANDS_PREFIX);
        }
    }

    这里,我们使用 @Order 注解显式地为这个 bean 指定了顺序。

  7. application.properties 文件注册图标集:

    jmix.ui.icons-config = ui.ex1.icon.FontAwesome5Icon

现在可以直接在界面 XML 描述中通过类和枚举值元素的方式使用新图标:

<button icon="font-awesome5-brands-icon:JAVA"/>

或者在 Java 控制器内:

cIconBtn.setIconFromSet(FontAwesome5Icon.JAVA);

图标覆盖

图标集的机制使得我们可以用其他集合中的图标覆盖某些图标。需要使用相同的图标(选项)创建并注册一个新的图标集(枚举),但是使用不同的图标路径(source)。下面的示例中,用新创建的 MyIcon 枚举覆盖了标准 JmixIcon 图标集中的图标。

  1. 创建新的图标集

    public enum NewIcon implements Icons.Icon {
    
        OK("classpath:/icon/custom-ok.png");
    }
  2. application.properties 注册新图标集:

    jmix.ui.icons-config = ui.ex1.icon.NewIcon

然后,OK 会使用新的图标而非默认自带的:

@Autowired
private Icons icons;

@Subscribe
protected void onInit(InitEvent event) {
    okIconBtn.setIcon(icons.get(JmixIcon.OK));
}

如果不需要使用重新定义,仍然可以通过使用图标源而不是选项名称来使用标准图标:

<button caption="Custom" icon="font-icon:CHECK"/>

或者

oIconBtn.setIcon(JmixIcon.OK.source());