1. 程式人生 > >java SWT:基於Composite定製背景透明的浮動影象按鈕(image button)

java SWT:基於Composite定製背景透明的浮動影象按鈕(image button)

SWT對於圖形按鈕沒有很好的支援,反正我折騰了半天,發現用org.eclipse.swt.widgets.Button是沒辦法做出好看的圖形按鈕的.
於是就參考org.eclipse.ui.forms.widgets.ImageHyperlink自己擼了一個:
效果嘛,參見下圖, 請忽略左邊的美女:
滑鼠不在按鈕區域時的未啟用狀態
這裡寫圖片描述
滑鼠進入按鈕區域時的啟用狀態
這裡寫圖片描述
下面是完整程式碼,很簡單。
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; /** * 按鍵狀態列舉型別,不同的狀態有不同的透明度值<br> * {@link #ACTIVE} 啟用<br> * {@link #UNACTIVE} 未啟用<br> * {@link #DOWN}按下<br> * @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樣式,當按鈕在有影象的元件之上時這樣的效果

這裡寫圖片描述
使用SWT.TRANSPARENT樣式,才是想要的效果

這裡寫圖片描述

用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,修正按鈕在影象上浮雲時效果不正確的問題