用Android写一个简易版的Visio对我是个极大的挑战,我学习进度很慢,还要工作,还要学习,不会经常更新博客。
在Visio中可以实现任意绘图,矩形,线,圆,椭圆等规则或不规则图形,可以对在一片区域内的图形进行组合、拆分,移动。放大、缩小、回退上一步等操作。这其中的难点在于如何实现组合、拆分、移动。
在2D绘图中,要画一个图形,需要使用画笔Paint、画布Canvas、图元。要让所有绘制的图形在一个View上显示就需要保证Canvas的相同,画笔可以不同。而图元,其实就是数据的意思,比如画一个圆,图元就应该只有三个数据:圆心的X、Y和半径。
这些都不管,我们先把图画出来。
画一个圆得需要圆心半径,矩形需要4个点的path,椭圆需要RectF值等,还需要画笔,画笔的Style等属性,为了不仅仅只是画一种图,我们需要把所有要用到的东西都列出来。所以我写个一个画图的父类,让绘制具体图形的类去继承他,在父类里边添加各种的属性,并实现getter、setter方法,而子类就不必再添加这么多的东西。
private int paintColor; private boolean paintAntiAlias; private float paintWight; private boolean paintSawtooth; private int paintAlpha; private Paint.Style paintStyle; private static Paint paint; private Path path = new Path(); private float[] pts; private int offset; private int count; private RectF rectF = new RectF(); private BaseData mBaseData;为了不每次画图都new许多的笔,我实例化了一个笔,因为是静态的,就不会new出许多
public interface DrawInterface { void changeState(); /** * 输出对象的自身结构,开始画图 * */ void drawPicture(Canvas canvas); /** * 聚集管理方法,增加一个子构件对象 * @param child 子构件对象 */ public void addChild(BaseDraw child); /** * 聚集管理方法,删除一个子构件对象 * @param index 子构件对象的下标 */ public void removeChild(int index); /** * 聚集管理方法,返回所有子构件对象 */ public List getChild(); }这是绘图的目录结构,写一个接口,是为了从外部调用的时候可以不用管是谁在执行这个方法。
在这个中有一个Draw方法,传入的是一个Canvas,是为了保证,所有的绘制,都是在同一个Canvas上进行的。
圆的绘制
public class CircularDraw extends BaseDraw { public CircularDraw(BaseData baseData) { super(baseData); } @Override public void changeState() { } @Override public void drawPicture(Canvas canvas) { setPaintStyle(getmBaseData().getPaintStyle()); setPaintAntiAlias(true); setPaintWight(getmBaseData().getWight()); setRectF(getmBaseData().getRectF()); setOffset(getmBaseData().getOffset()); setCount(getmBaseData().getCount()); setPaintAlpha(getmBaseData().getPaintAlpha()); setPts(getmBaseData().getFloats()); canvas.drawCircle(getPts()[0], getPts()[1], getPts()[2], getPaint()); } }椭圆的
public class EllipseDraw extends BaseDraw { public EllipseDraw(BaseData baseData) { super(baseData); } @Override public void drawPicture(Canvas canvas ) { setPaintStyle(getmBaseData().getPaintStyle()); setPaintAntiAlias(true); setPaintWight(getmBaseData().getWight()); setRectF(getmBaseData().getRectF()); setOffset(getmBaseData().getOffset()); setCount(getmBaseData().getCount()); setPaintAlpha(getmBaseData().getPaintAlpha()); setPts(getmBaseData().getFloats()); canvas.drawOval(getRectF(), getPaint()); } }其他的大同小异,所有子类所承担的任务有且只有绘制,图形的移动、放大、缩小,其实只是图元的改变
其实整个的绘图到这里就完成了。
现在开始写图元了。对于图元,因为Visio的图可以组合,拆分,组合图形的移动是内部所有图形的移动,而非组合的移动只是自己的移动。对于图元我们得使用组合模式
什么是组合模式
像这样的感觉,在一个list中,会有树叶、树枝或者只有树叶。我们需要让这一个实例中得包含所有的实例。
就像一颗树一样,他自己就知道自己是树叶还是树枝。
对于图元来说,我们需要他拥有绘制所有图元所必须得值,基于与同样的理由所以我有了
以下的数据结构
CompositeData是树枝,BaseData是父类,其他的都是子类
接口
public void addChild(BaseData baseData); public void removeChild(int index); public void move(float moveX ,float moveY); public void draw(Canvas canvas); public List getChild(); public DataType[] pointIsInside(float moveX , float moveY);draw方法传入的Canvas会传入到绘图的draw方法中去完成绘图
BaseData
public class BaseData implements DataInterface { @Override public void addChild(BaseData baseData) { Log.e("BaseData", "对象不支持此功能"); } @Override public void removeChild(int index) { Log.e("BaseData", "对象不支持此功能"); } @Override public List getChild() { Log.e("BaseData", "对象不支持此功能"); return null; } @Override public DataType[] pointIsInside(float moveX , float moveY) { return null; } @Override public void move(float moveX, float moveY) { } @Override public void draw(Canvas canvas) { } private TypeEnum typeEnum; private int offset; private int count; private float[] floats; private RectF rectF = new RectF(); private Paint.Style paintStyle; private float wight; private int paintAlpha; public BaseData(TypeEnum typeEnum, int wight, int paintAlpha, int offset, int count, float[] floats, RectF rectF, Paint.Style style) { this.typeEnum = typeEnum; this.wight = wight; this.paintAlpha = paintAlpha; this.offset = offset; this.count = count; this.floats = floats; this.rectF = rectF; this.paintStyle = style; } public TypeEnum getTypeEnum() { return typeEnum; } public void setTypeEnum(TypeEnum typeEnum) { this.typeEnum = typeEnum; } public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public float[] getFloats() { return floats; } public void setFloats(float[] floats) { this.floats = floats; } public RectF getRectF() { return rectF; } public void setRectF(RectF rectF) { this.rectF = rectF; } public Paint.Style getPaintStyle() { return paintStyle; } public void setPaintStyle(Paint.Style paintStyle) { this.paintStyle = paintStyle; } public float getWight() { return wight; } public void setWight(float wight) { this.wight = wight; } public int getPaintAlpha() { return paintAlpha; } public void setPaintAlpha(int paintAlpha) { this.paintAlpha = paintAlpha; } }