状态模式在绘图程序中的应用
来源:互联网 发布:站外优化 编辑:程序博客网 时间:2024/05/01 00:09
状态模式在绘图程序中的应用
设计绘图程序会面临根据绘图上下文,确定用户动作的问题。例如用户在画布上的操作只有鼠标点击,双击,拖动等很少几种,但在与不同的绘图工具组合后,各种操作的结果有很大不同。例如在选择绘制矩形后,鼠标点击应创建一个矩形,继续拖动应改变矩形的大小;而选择绘制折线时,鼠标点击应创建一个点,并根据绘图上下文与上一个点进行直线连接,直到用户双击结束。随着绘图工具种类的增加,鼠标事件处理函数会变得非常负责,而且会有很多的判断结构,不利于软件的维护和扩展。
状态模式就很适于解决这个问题。首先将每个绘图工具视为一个绘制状态,例如客户选择矩形工具后进入矩形绘制状态,而选择折线工具则进入折线绘制模式等。每个模式均实现了绘图的操作,只是操作的内容有所不同。因此可以将操作提取成为一个接口,而所有的模式均实现该接口。
绘图操作不仅仅依赖于鼠标或键盘的操作,也依赖于一些用户的全局设置,例如图形文件、笔画粗细和填充颜色等,由于这些选择可以应用于多种图形,因此可以将其保存在绘图上下文对象中。在用户更改了图形文件,例如新建了一个文件,改变颜色选择后,可以修改绘图上下文对象。
上面提到的当前绘图状态也是绘图上下文的一部分,因此绘图上下文对象中也保存当前绘图状态对象,用户可以通过选择绘图工具切换绘图状态对象。在绘制时,绘图状态对象得到该绘图上下文,并根据其设置进行相应的操作。
绘图上下文对象一般是全局唯一的,因此可以考虑设计成单例模式。
该部分的类图如下:
其中Context为绘图上下文,MouseDraw为鼠标操作接口,State为状态基类,实现了MouseDraw接口,但没有具体的操作。DrawRect为绘图矩形的状态类,SelectRect为选择矩形的状态类。
Context为单例模式,可以通过getInstance得到它的全局唯一对象,其中保存了当前图形文档,颜色和笔画的选择以及当前的绘图模式对象。
Context context = Context.getInstance();
Context类实现了事件处理接口,并将得到事件操作派发到当前状态对象实现的操作中。在使用时,可以使用以下代码切换绘图操作到绘制矩形的模式。状态模式的设计也很好的使用接口隔离了具体操作。
DrawRect dr = new DrawRect(context);
context.setState(dr);
绘图状态在创建时也得到了绘图上下文,实际上对单例模式并不需要通过参数传入。具体的绘图在当前状态对象中根据鼠标操作和上下文进行。
与状态模式相关的代码示例如下:
import java.awt.Cursor;
import org.apache.batik.dom.events.DOMMouseEvent;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.swing.JSVGCanvas;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGLocatable;
import org.w3c.dom.svg.SVGMatrix;
import org.w3c.dom.svg.SVGPoint;
import org.w3c.dom.svg.SVGSVGElement;
//Singleton
class Context implements EventListener
{
privatestaticfinal Context m_instance = new Context();
//Because we have only one instance of Context class, so we don't
//need set context attribute to static
private State s;
//Drawing Context
private JSVGCanvas svgCanvas;
Element target = null;
privateintstrokeWidth;
privateintfrontColor = 1;
privateintbackColor = 0;
privateintdragFlag = 0;
//Private constructor
private Context()
{
s = new State(this);
}
//Return the only one instance
publicstatic Context getInstance()
{
returnm_instance;
}
publicvoid handleEvent(Event evt) {
//System.out.println(evt.getType());
if(evt.getType().equalsIgnoreCase("mousedown"))
{
DOMMouseEvent mevt = (DOMMouseEvent)evt;
target = (Element)mevt.getTarget();
s.mousePress(mevt.getClientX(), mevt.getClientY(),target);
dragFlag = 1;
}
elseif(evt.getType().equalsIgnoreCase("mouseup"))
{
DOMMouseEvent mevt = (DOMMouseEvent)evt;
s.mouseRelease(mevt.getClientX(), mevt.getClientY());
dragFlag = 0;
}
elseif(evt.getType().equalsIgnoreCase("mousemove"))
{
if(dragFlag==1)
{
DOMMouseEvent mevt = (DOMMouseEvent)evt;
s.mouseDrag(mevt.getClientX(), mevt.getClientY());
}
}
}
//Attribute Getter/Setters
publicint getFrontColor() {
returnfrontColor;
}
publicvoid setFrontColor(int frontColor) {
this.frontColor = frontColor;
}
public State getState() {
returns;
}
publicvoid setState(State s) {
this.s = s;
}
public JSVGCanvas getSvgCanvas() {
returnsvgCanvas;
}
publicvoid setSvgCanvas(JSVGCanvas svgCanvas) {
this.svgCanvas = svgCanvas;
}
public Element getTarget() {
returntarget;
}
publicvoid setTarget(Element target) {
this.target = target;
}
}
interface MouseDraw {
publicvoid mousePress(int x, int y, Element e);
publicvoid mouseDrag(int x, int y);
publicvoid mouseRelease(int x, int y);
}
class State implements MouseDraw
{
private Context currentContext;
String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
State(Context c)
{
currentContext = c;
}
publicvoid mousePress(int x, int y, Element e)
{
}
publicvoid mouseDrag(int x, int y)
{
}
publicvoid mouseRelease(int x, int y)
{
}
// Convert client point to current svg document point
public SVGPoint clientPointToSvgDocument(JSVGCanvas canvas, int x, int y)
{
Document svgDoc = canvas.getSVGDocument();
Element svgRoot = svgDoc.getDocumentElement();
SVGMatrix ctm = ((SVGLocatable)svgRoot).getScreenCTM();
SVGPoint point = ((SVGSVGElement)svgRoot).createSVGPoint();
point.setX(x);
point.setY(y);
point = point.matrixTransform(ctm.inverse());
return point;
}
public Context getCurrentContext() {
returncurrentContext;
}
publicvoid setCurrentContext(Context currentContext) {
this.currentContext = currentContext;
}
}
class DrawRect extends State
{
SVGPoint orgPoint, currentPoint;
SVGDocument svgDoc;
DrawRect(Context c)
{
super(c);
}
publicvoid mousePress(int x, int y, Element e)
{
JSVGCanvas svgCanvas = getCurrentContext().getSvgCanvas();
svgCanvas.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
orgPoint = clientPointToSvgDocument(svgCanvas, x, y);
svgDoc = svgCanvas.getSVGDocument();
if(svgDoc == null)
return;
svgCanvas.getUpdateManager().getUpdateRunnableQueue().invokeLater(new Runnable(){
publicvoid run(){
Element current = svgDoc.createElementNS(svgNS, "rect");
current.setAttributeNS(null, "x", String.valueOf(orgPoint.getX()));
current.setAttributeNS(null, "y", String.valueOf(orgPoint.getY()));
current.setAttributeNS(null, "width", "1");
current.setAttributeNS(null, "height", "1");
current.setAttributeNS(null, "fill", "red");
String currentId = "Elem" + String.valueOf(Math.random());
current.setAttributeNS(null, "id", currentId);
// attach the rectangle to the svg root element
svgDoc.getDocumentElement().appendChild(current);
getCurrentContext().setTarget(current);
}
});
}
publicvoid mouseDrag(int x, int y)
{
JSVGCanvas svgCanvas = getCurrentContext().getSvgCanvas();
svgDoc = svgCanvas.getSVGDocument();
if(svgDoc == null)
return;
Element current = getCurrentContext().getTarget();
if(current == null)
return;
currentPoint = clientPointToSvgDocument(svgCanvas, x, y);
svgCanvas.getUpdateManager().getUpdateRunnableQueue().invokeLater(new Runnable(){
publicvoid run(){
Element current = getCurrentContext().getTarget();
if(currentPoint.getX() < orgPoint.getX())
current.setAttributeNS(null, "x", String.valueOf(currentPoint.getX()));
current.setAttributeNS(null, "width", String.valueOf(Math.abs(currentPoint.getX()-orgPoint.getX())));
if(currentPoint.getY() < orgPoint.getY())
current.setAttributeNS(null, "y", String.valueOf(currentPoint.getY()));
current.setAttributeNS(null, "height", String.valueOf(Math.abs(currentPoint.getY()-orgPoint.getY())));
}
});
}
publicvoid mouseRelease(int x, int y)
{
getCurrentContext().setTarget(null);
}
}
////////////////////////////////////////
class SelectRect extends State
{
SVGPoint orgPoint, currentPoint;
SVGDocument svgDoc;
SelectRect(Context c)
{
super(c);
}
//Select a rect
publicvoid mousePress(int x, int y, Element e)
{
JSVGCanvas svgCanvas = getCurrentContext().getSvgCanvas();
svgCanvas.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
orgPoint = clientPointToSvgDocument(svgCanvas, x, y);
getCurrentContext().target = e;
}
//When select a rect and drag it, change rect x and y attribute
publicvoid mouseDrag(int x, int y)
{
if(getCurrentContext().target == null)
return;
JSVGCanvas svgCanvas = getCurrentContext().getSvgCanvas();
currentPoint = clientPointToSvgDocument(svgCanvas, x, y);
svgCanvas.getUpdateManager().getUpdateRunnableQueue().invokeLater(new Runnable(){
publicvoid run(){
Element current = getCurrentContext().getTarget();
float xa = Float.parseFloat(current.getAttributeNS(null,"x"));
float ya = Float.parseFloat(current.getAttributeNS(null,"y"));
xa = (xa + currentPoint.getX() - orgPoint.getX());
ya = (ya + currentPoint.getY() - orgPoint.getY());
current.setAttributeNS(null, "x", String.valueOf(xa));
current.setAttributeNS(null, "y", String.valueOf(ya));
orgPoint = currentPoint;
}
});
}
//Release mouse and set selection to null
publicvoid mouseRelease(int x, int y)
{
getCurrentContext().setTarget(null);
}
}
代码中使用Batik SVG作为绘图基础实现方法,相信使用的人并不多,但其结构完全可以在其它绘图实现中使用,例如GDI或Java2D等,因为设计模式作为设计思想的体现,是超越具体实现的。
示例代码中仅实现了三种鼠标操作和两种绘图工具,基于这个结构,用户可以很方便的扩展自己的操作和工具种类。
- 状态模式在绘图程序中的应用
- State(状态)模式在web程序中的应用
- 设计模式在鼠标绘图中的应用
- JAVA中的观察者机制在绘图程序中的应用
- 状态模式在服务器中的应用
- 绘图程序中的坐标系应用
- 依赖注入模式在程序中的应用
- current point 在pgf绘图中的应用
- Unity客户端框架笔记(状态模式和策略模式在游戏中的应用)
- Unity客户端框架笔记(状态模式和策略模式在游戏中的应用)
- 数理逻辑在程序中的应用
- 光滑寻路算法在绘图中的应用
- 基于topic的发布/订阅模式在UI程序中的应用
- 状态模式-订单应用
- 状态模式的应用
- CHM帮助文件在VB程序中的应用
- 人工智能在围棋程序中的应用
- CHM帮助文件在VB程序中的应用
- 备战ACM/ICPC资料
- asp.net Js 小技巧 :在框架中,点击退出按钮,跳出框架,转向登陆页
- 康托尔与集合论
- ROR中数据库连接的问题
- 有点意思的宏替换
- 状态模式在绘图程序中的应用
- Eclipse 主要插件搭配
- 理想中的项目经理
- 对csdn博客的建议
- jboss中ejb3“标识过长”问题。
- JAVASCRIPT精彩200例
- javascript检测图片大小、类型、长宽
- 第三十届ACM/ICPC 世界总决赛题目解析
- 宝元没前途,我的命就这样子吗