java SWT:基于Composite定制背景透明的浮动图像按钮(image button)

SWT对于图形按钮没有很好的支持,反正我折腾了半天,发现用org.eclipse.swt.widgets.Button是没办法做出好看的图形按钮的.
于是就参考org.eclipse.ui.forms.widgets.ImageHyperlink自己撸了一个:
效果嘛,参见下图, 请忽略左边的美女:
鼠标不在按钮区域时的未激活状态
java SWT:基于Composite定制背景透明的浮动图像按钮(image button)_第1张图片
鼠标进入按钮区域时的激活状态
java SWT:基于Composite定制背景透明的浮动图像按钮(image button)_第2张图片
下面是完整代码,很简单。
ImageButton.java

package net.gdface.ui;

import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.wb.swt.SWTResourceManager;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;

/** * 透明背景图像按钮 * @author guyadong * */
public class ImageButton extends Composite{
    /** * 按钮背景图 */
    private Image image;
    /** * dispose时是否释放image资源( {@link #dispose()}不释放 没有指定透明色的 {@link #image} 对象) */
    private boolean disposeImage=false;
    /** * 按键状态枚举类型,不同的状态有不同的透明度值
* {@link #ACTIVE} 激活
* {@link #UNACTIVE} 未激活
* {@link #DOWN}按下
* @author guyadong */
private enum State{ ACTIVE(150),DOWN(255),UNACTIVE(50); private State(int alpha) { this.alpha = alpha; } int alpha; } /** * 按钮激活标志 */ private State active = State.UNACTIVE; /** * 最小按钮尺寸 */ private Point minSize=null; /** * 最大按钮尺寸 */ private Point maxSize=null; /** * @param parent * @param transparent 指定透明色,为null时没有透明色 * @param style * @wbp.parser.constructor */ public ImageButton(Composite parent, Image image, RGB transparent) { super(parent, SWT.TRANSPARENT);// 透明背景样式 image = image == null ? SWTResourceManager.getMissingImage() : image; if(null!=transparent){ ImageData imageData = image.getImageDataAtCurrentZoom(); imageData.transparentPixel = imageData.palette.getPixel(transparent); image = new Image(parent.getDisplay(), imageData); disposeImage=true; } this.image=image; this.maxSize=new Point(this.image.getBounds().width,this.image.getBounds().height); // 鼠标进入离开时修改激活标志并重绘窗口 addMouseTrackListener(new MouseTrackAdapter() { final Cursor defCursor = getCursor(); @Override public void mouseEnter(MouseEvent e) { active = State.ACTIVE; setCursor(SWTResourceManager.getCursor(SWT.CURSOR_HAND)); redraw(); } @Override public void mouseExit(MouseEvent e) { active = State.UNACTIVE; setCursor(defCursor); redraw(); } }); addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { boolean isAdvanced = e.gc.getAdvanced(); try { if(!isAdvanced) e.gc.setAdvanced(true); e.gc.setAntialias(SWT.ON); // 激活时设置alpha参数以区分按钮状态 e.gc.setAlpha(active.alpha); e.gc.drawImage(ImageButton.this.image, 0, 0, ImageButton.this.image.getBounds().width, ImageButton.this.image.getBounds().height, 0, 0, getBounds().width, getBounds().height); } finally { if(!isAdvanced) e.gc.setAdvanced(isAdvanced); } } }); addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { if(1==e.button){ active=State.DOWN; redraw(); } } @Override public void mouseUp(MouseEvent e) { if(1==e.button){ active=State.ACTIVE; redraw(); } } }); } /** * 指定透明色为白色 * @param parent * @param image * @see #ImageButton(Composite, Image, RGB) */ public ImageButton(Composite parent, Image image) { this(parent,image,new RGB(255, 255, 255)); } @Override protected void checkSubclass() { // Disable the check that prevents subclassing of SWT components } @Override public void dispose() { if(disposeImage) image.dispose(); super.dispose(); } @Override public Point computeSize(int wHint, int hHint, boolean changed) { // 重写此方法,保证layout时按钮尺寸不超过设置的最大最小值 if(wHint!=SWT.DEFAULT){ if(null!=minSize)wHint=Math.max(minSize.x, wHint); wHint=Math.min(maxSize.x, wHint); } if(hHint!=SWT.DEFAULT){ if(null!=minSize)hHint=Math.max(minSize.y, hHint); hHint=Math.min(maxSize.y, hHint); } return super.computeSize(wHint, hHint, changed); } /** * @return * @see #minSize */ public Point getMinSize() { return minSize; } /** * @param minSize * @return * @see #minSize */ public ImageButton setMinSize(Point minSize) { this.minSize = minSize; return this; } /** * @return * @see #maxSize */ public Point getMaxSize() { return maxSize; } /** * @param maxSize * @return * @see #maxSize */ public ImageButton setMaxSize(Point maxSize) { if(null!=maxSize&&maxSize.x>minSize.x&&maxSize.y>minSize.y) this.maxSize = maxSize; return this; } }

注意:
上面的代码在类构造函数中使用了SWT.TRANSPARENT样式进行初始化,SWT.TRANSPARENT指定透明背景
如果不指定SWT.TRANSPARENT样式,当按钮在有图像的组件之上时这样的效果

java SWT:基于Composite定制背景透明的浮动图像按钮(image button)_第3张图片
使用SWT.TRANSPARENT样式,才是想要的效果

java SWT:基于Composite定制背景透明的浮动图像按钮(image button)_第4张图片

用WindowBuilder生成的测试代码也一并附上:
TestApp.java

package testwb;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import java.net.MalformedURLException;
import java.net.URL;

import org.eclipse.swt.SWT;
import org.eclipse.wb.swt.SWTResourceManager;

import net.gdface.ui.ActiveConstant;
import net.gdface.ui.ImageButton;
import net.gdface.ui.ImageCanvas;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;

public class TestApp {

    protected Shell shell;
    /** * Launch the application. * * @param args */
    public static void main(String[] args) {
        try {
            TestApp window = new TestApp();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** * Open the window. */
    public void open() {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /** * Create contents of the window. */
    protected void createContents() {
        shell = new Shell();
        // 鼠标左键点击改变背景色测试明秀色效果
        shell.addMouseListener(new MouseAdapter() {
            Color defColor=SWTResourceManager.getColor(SWT.COLOR_RED);
            boolean c=false;
            @Override
            public void mouseDown(MouseEvent e) {
                c=!c;
                shell.setBackground(c?SWTResourceManager.getColor(SWT.COLOR_BLACK):defColor);
            }
        });
        shell.setBackground(SWTResourceManager.getColor(SWT.COLOR_BLACK));
        shell.setSize(526, 467);
        shell.setText("SWT Application");
        // 强制所有组件使用父窗口的背景色
        shell.setBackgroundMode(SWT.INHERIT_FORCE);

        try {
            Image image = SWTResourceManager.getImage(new URL(
                    "http://d.hiphotos.baidu.com/image/pic/item/562c11dfa9ec8a13f075f10cf303918fa1ecc0eb.jpg"));
            ImageCanvas canvas = new ImageCanvas(shell, SWT.NONE, image);
            canvas.setBounds(10, 10, 124, 252);

            ImageButton canvas_1 = new ImageButton(shell, SWTResourceManager.getImage(TestApp.class, "/image/arrow_left.png"), null);
            canvas_1.setBounds(156, 47, 118, 184);
        } catch (MalformedURLException e1) {
            // TODO 自动生成的 catch 块
            e1.printStackTrace();
        }

    }

}

注意:
上面的测试代码有这一行shell.setBackgroundMode(SWT.INHERIT_FORCE);是强制所有组件使用父窗口的背景色。
这一行也很重要,如果没有这样,当按钮所在组件改变背景色的时候(setBackground),透明色就失效了。

SWT对图像背景透明的设置有几种方式,本文中我选择了最简单的一种,就是指定图像中某种颜色(本例为白色)为透明色。
当然使用这种方式也有缺点就是除了透明色之外,相近的颜色(比如 255,255,254)就没办法透明,所以修图时要把图清干净保持背景色是纯色。
因为jpeg是有损压缩格式,会破坏纯色的背景色,所以这种透明方式对于jpeg格式的图像效果不好。
所以建议使用png,bmp等无损压缩格式来存储图像文件。

2016/12/07 补充:按钮状态增加到3种,分为ACTIVE,UNACTIVE,DOWN,增加layout支持
2016/12/08 补充:修改为控件样式(style)改为SWT.TRANSPARENT,修正按钮在图像上浮云时效果不正确的问题

关于图像透明色的设置参考:
《Java Source Code: com.asprise.books.javaui.ch15.Transparency》
《Taking a look at SWT Images》

你可能感兴趣的:(java,ui)