?? swingdesigner.java
字號(hào):
/*
* BeanDesigner.java
*
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package dyno.swing.designer.beans;
import dyno.swing.designer.beans.actions.ActionCategory;
import dyno.swing.designer.beans.actions.AlignmentAction;
import dyno.swing.designer.beans.actions.EditingAction;
import dyno.swing.designer.beans.actions.SameSizeAction;
import dyno.swing.designer.beans.actions.ViewAction;
import dyno.swing.designer.beans.events.AddingMouseListener;
import dyno.swing.designer.beans.events.DesignerEditAdapter;
import dyno.swing.designer.beans.events.DesignerEditListener;
import dyno.swing.designer.beans.events.DesignerEvent;
import dyno.swing.designer.beans.events.DesignerStateListener;
import dyno.swing.designer.beans.events.EditingMouseListener;
import dyno.swing.designer.beans.events.HotKeyProxy;
import dyno.swing.designer.beans.location.Location;
import dyno.swing.designer.beans.models.AddingModel;
import dyno.swing.designer.beans.models.SelectionModel;
import dyno.swing.designer.beans.models.StateModel;
import dyno.swing.designer.beans.toolkit.BeanInfoToggleButton;
import dyno.swing.designer.beans.events.EditListenerTable;
import dyno.swing.designer.beans.events.StateListenerTable;
import dyno.swing.designer.beans.toolkit.ComponentPalette;
import dyno.swing.designer.treeview.ComponentTree;
import dyno.swing.designer.treeview.ComponentTreeEvent;
import dyno.swing.designer.treeview.ComponentTreeListener;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.RootPaneContainer;
import javax.swing.Scrollable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreePath;
/**
* 設(shè)計(jì)界面組件。該組件是界面設(shè)計(jì)工具的核心,主要負(fù)責(zé)的是被設(shè)計(jì)界面的顯示,界面設(shè)計(jì)操作狀態(tài)的
* 顯示,編輯狀態(tài)的顯示等等。
*
* @author William Chen
*/
public class SwingDesigner extends JComponent implements Constants, TreeSelectionListener, ComponentTreeListener, Scrollable, InvocationHandler {
/**
* 當(dāng)前正在設(shè)計(jì)的組件樹(shù)的根節(jié)點(diǎn)。目前只支持JPanel作為根節(jié)點(diǎn)。可以很容易的修改使其支持其他
* 容器。被設(shè)計(jì)的組件其name屬性都不為空,其值為該組件的變量名稱(chēng)。
*/
private Component rootComponent;
/**
* 設(shè)計(jì)界面的左上角的坐標(biāo)。目的是留出空間顯示設(shè)計(jì)界面的邊框。
*/
private int leftOffset;
private int topOffset;
/**
* 當(dāng)前設(shè)計(jì)界面狀態(tài)是否是添加模式。界面設(shè)計(jì)工具的狀態(tài)可以分編輯狀態(tài)和添加狀態(tài)。編輯狀態(tài)的
* 設(shè)計(jì)界面可以選擇組件、刪除、剪切、粘帖、對(duì)其、屬性編輯等等操作。添加狀態(tài)是指在選擇組件
* 之后設(shè)計(jì)界面所處于的狀態(tài)。添加狀態(tài)和編輯狀態(tài)下鼠標(biāo)處理方式相差很大,所以分為此兩種狀態(tài)
*/
private boolean addingMode;
/**
* 下面的變量都是非序列化成員,不記錄設(shè)計(jì)狀態(tài),只作為設(shè)計(jì)時(shí)臨時(shí)狀態(tài)使用。
*/
//編輯狀態(tài)時(shí)鼠標(biāo)處理器
private transient EditingMouseListener editingMouseListener;
//界面設(shè)計(jì)器的鍵盤(pán)熱鍵處理器,主要處理編輯如復(fù)制、剪切、刪除、粘帖等熱鍵
private transient HotKeyProxy keyListener;
//添加組件狀態(tài)下的鼠標(biāo)處理器
private transient AddingMouseListener addingMouseListener;
//編輯狀態(tài)下的model,存儲(chǔ)編輯狀態(tài)下的臨時(shí)狀態(tài),比如拖拽區(qū)域、鼠標(biāo)熱點(diǎn)等等
private transient StateModel stateModel;
//添加狀態(tài)下的model,存儲(chǔ)添加狀態(tài)下的臨時(shí)狀態(tài),比如要添加的組件、當(dāng)前鼠標(biāo)位置等等
private transient AddingModel addingModel;
//當(dāng)前負(fù)責(zé)額外渲染的painter,主要目的用來(lái)渲染添加組件的位置提示,它通常由外部類(lèi)設(shè)置,在
//設(shè)計(jì)器渲染時(shí)被調(diào)用渲染這些位置提示。
private transient Painter painter;
private boolean invalidated = true;
//存儲(chǔ)被選擇組件和剪切板的model
private transient SelectionModel selectionModel;
private EditListenerTable edit;
private StateListenerTable state;
private VariableSpace space;
private Border outline_border;
private Action[] designer_actions;
private Border inner_border;
private ComponentPalette palette;
private LookAndFeel designLnf;
private LookAndFeel ideLnf;
public void setPalette(ComponentPalette palette) {
this.palette = palette;
}
public VariableSpace getVariableSpace() {
return space;
}
public Dimension getPreferredSize() {
Dimension size = rootComponent.getSize();
Insets insets = getOutlineInsets();
size.width = 2 * leftOffset + size.width + insets.left + insets.right;
size.height = 2 * topOffset + size.height + insets.top + insets.bottom;
return size;
}
/** Creates a new instance of BeanDesigner */
public SwingDesigner() {
setBackground(Color.white);
setDoubleBuffered(true);
space = new VariableSpace();
designLnf = UIManager.getLookAndFeel();
ideLnf = UIManager.getLookAndFeel();
//初始化
leftOffset = 20;
topOffset = 20;
//為了處理鍵盤(pán)事件,需要SwingDesigner能夠獲取焦點(diǎn)
setFocusable(true);
edit = new EditListenerTable();
state = new StateListenerTable();
//初始化
selectionModel = new SelectionModel(this);
stateModel = new StateModel(this);
//初始化界面設(shè)計(jì)工具的UI實(shí)例
updateUI();
//初始化缺省的設(shè)計(jì)組件
initRootComponent();
//初始化事件處理器
initializeListener();
}
public EditListenerTable getEditListenerTable() {
return edit;
}
public StateListenerTable getStateListenerTable() {
return state;
}
private void initActionListener(Action[] actions) {
for (Action action : actions) {
if (action != null) {
if (action instanceof DesignerEditListener) {
addDesignerEditListener((DesignerEditListener) action);
}
if (action instanceof DesignerStateListener) {
addDesignerStateListener((DesignerStateListener) action);
}
if (action instanceof ActionCategory) {
Action[] sub = ((ActionCategory) action).getSubActions();
initActionListener(sub);
}
}
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Container container = (Container) rootComponent;
Util.layoutContainer(container);
repaint();
return null;
}
/**
* 初始化事件處理器,初始狀態(tài)為編輯狀態(tài),所以下初始化并添加編輯類(lèi)的事件處理器
*/
private void initializeListener() {
//點(diǎn)擊
editingMouseListener = new EditingMouseListener(this);
//熱鍵
keyListener = new HotKeyProxy(this);
addMouseMotionListener(editingMouseListener);
addMouseListener(editingMouseListener);
addKeyListener(keyListener);
addInvocationHandler(this);
addDesignerEditListener(new DesignerEditAdapter() {
public void componentMoved(DesignerEvent evt) {
setInvalidated(true);
repaint();
}
public void componentResized(DesignerEvent evt) {
setInvalidated(true);
repaint();
}
@Override
public void componentCut(DesignerEvent evt) {
setInvalidated(true);
}
@Override
public void componentDeleted(DesignerEvent evt) {
setInvalidated(true);
}
@Override
public void componentPasted(DesignerEvent evt) {
setInvalidated(true);
}
public void componentAdded(DesignerEvent evt) {
setInvalidated(true);
}
public void componentEdited(DesignerEvent evt) {
setInvalidated(true);
Component comp = evt.getAffectedComponents().get(0);
Container par = comp.getParent();
if (par != null) {
LayoutManager layout = par.getLayout();
if (layout != null) {
Util.layoutContainer(par);
}
}
}
});
}
public void addInvocationHandler(InvocationHandler h) {
ClassLoader loader = getClass().getClassLoader();
Class[] interfaces = new Class[]{DesignerEditListener.class, DesignerStateListener.class};
Object proxyListener = Proxy.newProxyInstance(loader, interfaces, h);
addDesignerEditListener((DesignerEditListener) proxyListener);
addDesignerStateListener((DesignerStateListener) proxyListener);
}
/**
* 開(kāi)始添加狀態(tài),在用戶(hù)選擇面板上的組件時(shí)啟動(dòng)
*
* @param beanInfo 當(dāng)前選中組件的BeanInfo對(duì)象
*/
public void startAddingState(BeanInfo beanInfo) {
//刪除編輯狀態(tài)的事件處理器
removeMouseListener(editingMouseListener);
removeMouseMotionListener(editingMouseListener);
removeKeyListener(keyListener);
//根據(jù)所選擇的組件的BeanInfo生成相應(yīng)的AddingModel
//AddingModel和StateModel不一樣,適合當(dāng)前選擇的組件相關(guān)的
addingModel = new AddingModel(this, beanInfo);
addingMouseListener = new AddingMouseListener(this, beanInfo);
//添加事件
addMouseListener(addingMouseListener);
addMouseMotionListener(addingMouseListener);
//設(shè)置當(dāng)前模式位添加模式
setAddingMode(true);
//觸發(fā)狀態(tài)添加模式事件
fireStartDesigning();
repaint();
}
/**
* 停止添加模式、返回編輯模式
*/
public void stopAddingState() {
//刪除添加模式下的鼠標(biāo)處理器
removeMouseListener(addingMouseListener);
removeMouseMotionListener(addingMouseListener);
//恢復(fù)為空,UI類(lèi)根據(jù)addingModel是否空決定是否停止渲染要添加的組件
addingModel = null;
addingMouseListener = null;
painter = null;
//添加編輯狀態(tài)下的鼠標(biāo)事件處理器和鍵盤(pán)事件處理器
//由于他們是無(wú)狀態(tài),是和組件無(wú)關(guān)的,因此不需要重新生成
addMouseMotionListener(editingMouseListener);
addMouseListener(editingMouseListener);
addKeyListener(keyListener);
//設(shè)置模式為編輯模式
setAddingMode(false);
//觸發(fā)停止添加模式的事件
fireStopDesigning();
repaint();
}
//設(shè)置其UI類(lèi)為DesignerUI,負(fù)責(zé)渲染
@Override
public void updateUI() {
setUI(new DesignerUI());
}
/**
* 在拖拽區(qū)域選擇方式鼠標(biāo)釋放時(shí)調(diào)用此函數(shù)來(lái)更新所選擇的組件
* @param e 當(dāng)前鼠標(biāo)事件,用來(lái)和起始點(diǎn)構(gòu)成選擇框,計(jì)算被圈入的組件
*/
public void selectComponents(MouseEvent e) {
//調(diào)用stateModel的selectComponent更新被選擇的組件,stateModel定義了拖拽起始點(diǎn)
stateModel.selectComponents(e);
//清除stateModel為非拖拽狀態(tài)
stateModel.reset();
repaint();
}
/**
* 鼠標(biāo)拖拽組件改變其位置和尺寸,釋放鼠標(biāo)時(shí)調(diào)用
*/
public void releaseComponentsDragging(MouseEvent e) {
setInvalidated(true);
//恢復(fù)初始狀態(tài),改變被拖拽組件的位置和尺寸
stateModel.releaseDragging(e);
//觸發(fā)拖拽事件
fireComponentDragged(stateModel.getHotspotComponents());
repaint();
}
/**
* 從root組件遞歸查找x,y所在的組件,注意是正在被設(shè)計(jì)的組件,因此其name屬性必須不為空
*/
private Component componentAt(int x, int y, Component root) {
if (!(root instanceof RootPaneContainer) && !root.isVisible()) {
return null;
}
x -= root.getX();
y -= root.getY();
if (root instanceof Container) {
Container rootContainer = (Container) root;
int count = rootContainer.getComponentCount();
if (count > 0) {
for (int i = 0; i < count; i++) {
Component child = rootContainer.getComponent(i);
//只有name不為空的組件才是搜索范圍,這兒遞歸下溯調(diào)用
Component dest = componentAt(x, y, child);
if (dest != null) {
return dest;
}
}
}
}
Rectangle rect = Util.computeVisibleRect(root);
if (Util.isDesigning(root) && (x >= rect.getX()) && (x <= (rect.getX() + rect.getWidth())) && (y >= rect.getY()) && (y <= (rect.getY() + rect.getHeight()))) {
//判斷是否處于交叉區(qū)域
return root;
}
return null;
}
/**
* 根據(jù)當(dāng)前stateModel中所標(biāo)識(shí)的鼠標(biāo)位置狀態(tài)數(shù)據(jù)更新鼠標(biāo)的形狀
*/
public void updateCursor() {
Location location = stateModel.getLocation();
//調(diào)用位置枚舉的多態(tài)方法getCursor獲取鼠標(biāo)形狀
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -