Java SWT : Badge Notifications

标签 java swt swt-awt

我有一个基于桌面的 UI 应用程序,它是用 Java SWT 编写的,运行在 Windows 上。

我想在 UI 屏幕上添加一个按钮,其行为应该类似于 iphone 或 facebook 通知上的徽章,如下图所示。

徽章上的数字是动态的,会根据待处理通知的数量增加或减少。

如何在 SWT/AWT 中实现类似的东西?

IOS 徽章:

enter image description here

Facebook 通知:

enter image description here

最佳答案

我最近实现了类似的东西。您可以简单地使用 GC 绘制自定义图像,然后覆盖在您想要的图标上。

我在这里包括了我的助手类。这不是最干净的代码(很多东西都是硬编码的),但您会明白这一点的。通知气泡会根据通知数量(最大 999)自行调整大小。

如何使用(记得缓存和/或处置您的图像!):

Image decoratedIcon = new ImageOverlayer()
        .baseImage(baseImage) // You icon/badget
        .overlayImage(ImageOverlayer.createNotifImage(5)) // 5 notifications 
        .overlayImagePosition(OverlayedImagePosition.TOP_RIGHT)
        .createImage();


/**
 * <pre>
 * The difference between this and the ImageBuilder is 
 * that ImageOverlayer does not chain the images, rather
 * just overlays them one onto another.
 * 
 * 
 * Rules:
 * 
 * 1.) Images are not disposed. Resource handing must be done externally.
 * 2.) Only two images allowed, for now.
 * 3.) The size of the composite image should normally be the size of the
 *     base image, BUT: if the overlaying image is larger, then larger
 *     parameters are grabbed, and the base image is still underneath.
 * 4.) Use the builder APIs to set the base and overlaying images. The 
 *     position of the overlaying image is optional, and CENTER by default.
 *     When you've set these, simply call createImage()
 * 
 * Further improvements:
 * 
 * - Combine this with ImageBuilder. These two composers should be welded.
 * 
 * </pre>
 * 
 * @author grec.georgian@gmail.com
 *
 */
public class ImageOverlayer extends CompositeImageDescriptor
{

    // ==================== 1. Static Fields ========================

    public enum OverlayedImagePosition
    {
        TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CENTER;
    }


    // ====================== 2. Instance Fields =============================

    private ImageData baseImageData;

    private ImageData overlayedImageData;

    private OverlayedImagePosition overlayedImagePosition = OverlayedImagePosition.CENTER;



    // ==================== 3. Static Methods ====================

    /**
     * Creates a red circle with a white bold number inside it.
     * Does not cache the final image.
     */
    public static final Image createNotifImage(final int numberOfNotifications)
    {
        // Initial width and height - hardcoded for now
        final int width = 14;
        int height = 14;

        // Initial font size
        int fontSize = 100;

        int decorationWidth = width;

        String textToDraw = String.valueOf(numberOfNotifications);

        final int numberLength = Integer.toString(numberOfNotifications).length();

        if(numberLength > 3)
        {
            // spetrila, 2014.12.17: - set a width that fits the text
            //                       - smaller height since we will have a rounded rectangle and not a circle
            //                       - smaller font size so the new text will fit(set to 999+) if we have
            //                         a number of notifications with more than 3 digits
            decorationWidth += numberLength * 2;
            height -= 4;

            fontSize = 80;
            textToDraw = "999+"; //$NON-NLS-1$
        }
        else if (numberLength > 2)
        {
            // spetrila, 2014.12.17: - set a width that fits the text
            //                       - smaller height since we will have a rounded rectangle and not a circle
            decorationWidth += numberLength * 1.5;
            height -= 4;
        }

        final Font font = new Font(Display.getDefault(), "Arial", width / 2, SWT.BOLD); //$NON-NLS-1$

        final Image canvas = new Image(null, decorationWidth, height);

        final GC gc = new GC(canvas);

        gc.setAntialias(SWT.ON);
        gc.setAlpha(0);
        gc.fillRectangle(0, 0, decorationWidth, height);

        gc.setAlpha(255);
        gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_RED));

        // spetrila, 2014.12.17: In case we have more than two digits in the number of notifications,
        //                       we will change the decoration to a rounded rectangle so it can contain
        //                       all of the digits in the notification number
        if(decorationWidth == width)
            gc.fillOval(0, 0, decorationWidth - 1, height - 1);
        else
            gc.fillRoundRectangle(0, 0, decorationWidth, height, 10, 10);

        final FontData fontData = font.getFontData()[0];
        fontData.setHeight((int) (fontData.getHeight() * fontSize / 100.0 + 0.5));
        fontData.setStyle(SWT.BOLD);

        final Font newFont = new Font(Display.getCurrent(), fontData);

//      gc.setFont(AEFUIActivator.getDefault().getCustomizedFont(font, fontSize, SWT.BOLD));
        gc.setFont(newFont);
        gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));

        final Point textSize = gc.stringExtent(textToDraw);
        final int xPos = (decorationWidth - textSize.x) / 2;
        final int yPos = (height - textSize.y) / 2;
        gc.drawText(textToDraw, xPos + 1, yPos, true);

        gc.dispose();

        final ImageData imgData = canvas.getImageData();

        // Remove white transparent pixels
        final int whitePixel = imgData.palette.getPixel(new RGB(255,255,255));
        imgData.transparentPixel = whitePixel;
        final Image finalImage = new Image(null, imgData);

        canvas.dispose();
        font.dispose();
        newFont.dispose();

        return finalImage;
    }


    // ==================== 5. Creators ====================

    @Override
    public Image createImage()
    {
        if (baseImageData == null || overlayedImageData == null)
            throw new IllegalArgumentException("Please check the ImageOverlayer. One of the overlaying images is NULL."); //$NON-NLS-1$

        return super.createImage();
    }


    // ==================== 6. Action Methods ====================

    @Override
    protected void drawCompositeImage(final int width, final int height)
    {
        /*
         * These two determine where the overlayed image top left
         * corner should go, relative to the base image behind it.
         */
        int xPos = 0;
        int yPos = 0;

        switch (overlayedImagePosition)
        {
            case TOP_LEFT:
                break;

            case TOP_RIGHT:
                xPos = baseImageData.width - overlayedImageData.width;
                break;

            case BOTTOM_LEFT:
                yPos = baseImageData.height - overlayedImageData.height;
                break;

            case BOTTOM_RIGHT:
                xPos = baseImageData.width - overlayedImageData.width;
                yPos = baseImageData.height - overlayedImageData.height;
                break;

            case CENTER:
                xPos = (baseImageData.width - overlayedImageData.width) / 2;
                yPos = (baseImageData.height - overlayedImageData.height) / 2;
                break;

            default:
                break;
        }

        drawImage(baseImageData, 0, 0);
        drawImage(overlayedImageData, xPos, yPos);
    }


    // ==================== 7. Getters & Setters ====================

    final public ImageOverlayer overlayImagePosition(final OverlayedImagePosition overlayImagePosition)
    {
        this.overlayedImagePosition = overlayImagePosition;
        return this;
    }


    final public ImageOverlayer baseImage(final ImageData baseImageData)
    {
        this.baseImageData = baseImageData;
        return this;
    }


    final public ImageOverlayer baseImage(final Image baseImage)
    {
        this.baseImageData = baseImage.getImageData();
        return this;
    }


    final public ImageOverlayer baseImage(final ImageDescriptor baseImageDescriptor)
    {
        this.baseImageData = baseImageDescriptor.getImageData();
        return this;
    }


    final public ImageOverlayer overlayImage(final ImageData overlayImageData)
    {
        this.overlayedImageData = overlayImageData;
        return this;
    }


    final public ImageOverlayer overlayImage(final Image overlayImage)
    {
        this.overlayedImageData = overlayImage.getImageData();
        return this;
    }


    final public ImageOverlayer overlayImage(final ImageDescriptor overlayImageDescriptor)
    {
        this.overlayedImageData = overlayImageDescriptor.getImageData();
        return this;
    }


    @Override
    protected Point getSize()
    {
        // The size of the composite image is determined by the maximum size between the two building images,
        // although keep in mind that the base image always comes underneath the overlaying one.
        return new Point( max(baseImageData.width, overlayedImageData.width), max(baseImageData.height, overlayedImageData.height) );
    }

}

关于Java SWT : Badge Notifications,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28893454/

相关文章:

java - Eclipse SWT java.lang.StackOverflowError : why my listener executes many times?

java - 使用 SWT 在新浏览器中使用新 session 和 cookie 打开 URL

java - 如何将多个信任库路径添加到 “java.net.ssl.trustStore” ?

java - Java 中的 session

Eclipse SWT - WizardPage 小部件验证

Java SWT Slider.getMaximum() 等于100,但是最多只能拖到90

java - 有效控制数组的进入-JAVA

java - 使用带有两个 int 参数而不是 int 的 void 方法?

java - 如何从 SWT 与 Javascript 函数对话

linux - 在 Linux 中使用 SWT-AWT 桥时出现 SWT 模式对话框问题