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