AFreeSVG–安卓版的JFreeSVG,安卓上的svg图片绘制库
最近在做项目时需要在安卓上绘制生成svg图片,当时觉得这个需求应该会有很多现成的开源库支持,于是就打开百度,准备大干一番,但是天不遂人愿,找了好久都没有发现可以在安卓上使用的开源库,只找到了一个JFreeSVG,但是是基于java awt的,不能在安卓上使用。于是,我决定自己手撸一个安卓上的svg绘制库,并开源出来,以方便各位苦逼的程序员使用。
项目介绍
目前该库已经在github上开源了,并且已经发布了第一个正式版本,已经支持大部分功能,项目地址如下
https://github.com/feiyin0719/AFreeSvg
部分代码借鉴于JFreeSVG,在此也非常感谢JFreeSVG的开发者。不过既然是给安卓开发者使用的,所以该库的api设计仿照安卓canvas来实现,因此大部分安卓程序员都可以快速上手,下面来简单介绍下如何使用该库。
快速上手
- 首先在项目中引入依赖
需要添加jitpack仓库
maven { url 'https://jitpack.io' }
implementation 'com.github.feiyin0719:AFreeSvg:0.0.1'
- 参考代码
下面参考代码展示了该库的大多数功能,大家使用时可以参考
SVGCanvas svgCanvas = null;
try {
svgCanvas = new SVGCanvas(500, 500);
SVGPaint paint = new SVGPaint();
paint.setStyle(Paint.Style.STROKE);
paint.setFillColor(0xff0000ff);
paint.setDashArray(new float[]{5, 5, 10});
paint.setColor(Color.RED);
paint.setStrokeWidth(2);
svgCanvas.drawLine(10, 10, 200, 200, paint);
SVGPaint paint1 = new SVGPaint();
//线性渐变
SVGLinearGradient svgLinearGradient = new SVGLinearGradient(new PointF(0, 0), new PointF(1, 0));
svgLinearGradient.addStopColor(0, 0xffff0000);
svgLinearGradient.addStopColor(0.5f, 0xff00ff00);
svgLinearGradient.addStopColor(0.75f, 0xff00eeee);
svgLinearGradient.addStopColor(1, 0xff0000ff);
paint1.setGradient(svgLinearGradient);
paint1.setStyle(Paint.Style.FILL_AND_STROKE);
paint1.setStrokeWidth(2);
paint1.setARGB(100, 200, 200, 0);
svgCanvas.drawRect(new RectF(300, 300, 400, 450), paint1);
svgCanvas.drawOval(new RectF(150, 150, 200, 200), paint1);
SVGPaint paint2 = new SVGPaint();
paint2.setStyle(Paint.Style.FILL);
//放射渐变
SVGRadialGradient gradient = new SVGRadialGradient(0.5f, 0.5f, 1, 0.8f, 0.8f);
gradient.addStopColor(0.5f, 0xffff0000);
gradient.addStopColor(1f, 0xff0000ff);
paint2.setGradient(gradient);
paint2.setFillRule(SVGPaint.FILL_RULE_EVENODD);
//save功能
svgCanvas.save();
svgCanvas.translate(10, 10);
//clipShape创建
SVGShapeGroup clipGroup = new SVGShapeGroup();
SVGPath clipPath = new SVGPath();
clipPath.oval(0.2f, 0.2f, 0.2f, 0.2f);
SVGPath clipPath1 = new SVGPath();
clipPath1.oval(0.6f, 0.2f, 0.2f, 0.2f);
clipGroup.addShape(clipPath);
clipGroup.addShape(clipPath1);
SVGClipShape clipShape = new SVGClipShape(clipGroup, SVGModes.MODE_BOX);
svgCanvas.save();
//clipShape设置
svgCanvas.clip(clipShape);
svgCanvas.drawRect(new RectF(300, 300, 400, 450), paint2);
svgCanvas.save();
SVGClipShape clipShape1 = new SVGClipShape(clipPath, SVGModes.MODE_BOX);
svgCanvas.clip(clipShape1);
svgCanvas.restore();
//绘制多条线段
svgCanvas.drawPolyline(new float[]{20, 20, 40, 25, 60, 40, 80, 120, 120, 140, 200, 180}, paint);
svgCanvas.restore();
//绘制多边形
svgCanvas.drawPolygon(new float[]{100, 10, 40, 198, 190, 78, 10, 78, 160, 198}, paint2);
//创建path
SVGPath svgPath = new SVGPath();
svgPath.moveTo(200, 200);
svgPath.oval(200, 200, 50, 50);
svgPath.rect(100, 50, 50, 50);
svgPath.moveTo(100, 300);
svgPath.quadraticBelzierCurve(150, 250, 200, 400);
//绘制path
svgCanvas.drawPath(svgPath, paint);
//绘制贝塞尔曲线
svgCanvas.drawCurve(50, 50, 200, 50, 100, 25, paint);
//绘制圆弧
svgCanvas.drawArc(300, 100, 50, 50, 90, 270, paint);
SVGPath path = new SVGPath();
path.rect(20, 20, 100, 400);
svgCanvas.restore();
SVGShapeGroup group = new SVGShapeGroup();
group.addShape(path);
group.addShape(svgPath);
//绘制shape
svgCanvas.drawShape(group, paint);
//绘制文字
SVGPaint textPaint = new SVGPaint();
textPaint.setStyle(Paint.Style.FILL);
textPaint.setGradient(svgLinearGradient);
textPaint.setFont(new SVGFont.Builder().setFontFamily("sans-serif")
.setFontStyle(SVGFont.STYLE_ITALIC)
.setFontWeight("bold")
.setFontSize(24)
.build());
svgCanvas.drawText("hello world", 200, 20, textPaint, "");
SVGPath textPath = new SVGPath();
textPath.oval(100, 400, 100, 100);
// svgCanvas.drawTextOnPath("hello", 0, 0, 0, 0, textPath, textPaint, null);
svgCanvas.drawTextOnPath("world", 0, 0, 80, 0, textPath, textPaint, null);
//设置文本clip
svgCanvas.save();
SVGTextPath svgTextPath = new SVGTextPath.Builder()
.setPath(textPath)
.setPaint(textPaint)
.setText("hello").build();
svgCanvas.clip(new SVGClipShape(svgTextPath, SVGModes.MODE_USERSPACE));
svgCanvas.drawPath(textPath, paint2);
svgCanvas.restore();
svgCanvas.drawPath(textPath, paint);
//绘制图片
String url = "https://raw.githubusercontent.com/feiyin0719/AFreeSvg/dev/dog.jpg";
svgCanvas.drawImage(url, 200, 250, 100, 100, null);
SVGPath path1 = new SVGPath();
path1.rect(200, 450, 100, 50);
SVGTextPath svgTextPath1 = new SVGTextPath.Builder()
.setText("SVGDOG")
.setPath(path1)
.setPaint(textPaint)
.setTextLength(200)
.build();
svgCanvas.save();
svgCanvas.clip(new SVGClipShape(svgTextPath1, SVGModes.MODE_USERSPACE));
svgCanvas.drawImage(url, 200, 400, 100, 100, null);
svgCanvas.restore();
String s = svgCanvas.getSVGXmlString();
Log.i("myyf", s);
File file = new File(getExternalCacheDir(), "test.svg");
svgCanvas.writeSVGXMLToStream(new FileOutputStream(file));
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
大家应该可以发现,使用方法和安卓canvas几乎一模一样,除了因为一些原因新增一些类和一些功能,因此熟悉安卓的同学可以快速上手,几乎没有任何学习成本。
参考代码生成的svg图片效果如下
api介绍
- SVGCanvas
绘制canvas,使用方法类似安卓canvas。
图形绘制api
1. drawRect(RectF rectF, SVGPaint paint) //绘制矩形
2. drawLine(float x1, float y1, float x2, float y2, SVGPaint paint) //绘制线段
3. drawOval(RectF rectF, SVGPaint paint) //绘制椭圆或者圆
4. drawPolygon(float[] points, SVGPaint paint) //绘制多边形
5. drawPolyline(float points[], SVGPaint paint) //绘制多线段
6. public void drawArc(float x, float y, float width, float height, float startAngle,float arcAngle, SVGPaint paint) //绘制圆弧
7. public void drawCurve(float sx, float sy, float ex, float ey, float x, float y, SVGPaint paint) //绘制贝塞尔曲线
8. public void drawPath(SVGPath path, SVGPaint paint) //绘制普通path
9. public void drawShape(SVGShape shape, SVGPaint paint) //绘制shape
10. public void clip(SVGClipShape clipShape) //设置裁剪区域
11. public void drawText(String text, float x, float y, SVGPaint paint)//绘制文本
12. public void drawTextOnPath(String text, float x, float y, SVGPath path, SVGPaint paint) //绘制文本在path上
13. public void drawImage(String uri, float x, float y, float width, float height, SVGPaint paint)//绘制图片
14. public void drawCircle(float cx, float cy, float r, SVGPaint paint) //绘制圆形
transform clip 操作api
1. void clip(SVGClipShape shape) //设置clip区域,后续的绘制操作只会在clip区域上显示
2. translate scale rotate skew //变换操作,使用方法和canvas一致
3. save() save(int flags) restore() //和安卓canvas save restore一致,save后会保存当前canvas状态(transform和clip信息),restore会回退到之前状态,flags用来指示保存什么信息,不填则全部保存,SAVE_FLAG_CLIP 只保存clip信息。SAVE_FLAG_MATRIX 只保存transform信息
保存api
1. getSVGXmlString() //获取svg string
2. writeSVGXMLToStream(OutputStream outputStream) //保存svg
- SVGPaint
绘制画笔类,继承自安卓paint,主要用来添加一些安卓paint不支持的功能,以及解决无法获取渐变色和dash的问题
1. setDashArray(float[] dashArray) //设置线段dash值
2. setGradient(SVGGradient gradient) //设置渐变色
3. setFillRule(String fillRule) //设置填充规则 值意义参考svg nonzero / evenodd / inherit 默认nonzero
4. setFillColor(long color)//单独设置fill color,可以实现strokecolor和fillcolor不一样,不设置的话默认使用同一个颜色
5. setUseGradientStroke(bool useGradientStroke)//设置是否使用渐变色画线,当设置渐变填充时使用,默认为false
6. setFont(SVGFont font) setLengthAdjust(@LengthAdjust String lengthAdjust) setTextDecoration(@TextDecoration String textDecoration) setWordSpacing(float wordSpacing) //绘制文本时设置文本font以及相关信息
- SVGGradient
渐变色
1. SVGLinearGradient //线性渐变
2. SVGRadialGradient //放射渐变
- SVGShape
1. SVGPath //设置path路径
2. SVGShapeGroup //shape组,可以同时绘制多个shape 对应于 svg的g
3. SVGTextPath //文本path
4. SVGLine
5. SVGRect
6. SVGOval
7. SVGPolygon
8. SVGPolyline
- SVGClipShape
设置裁剪区域
1. public SVGClipShape(SVGShape shape, @SVGModes.POS_MODE String posMode)//posMode设置坐标空间。MODE_BOX 相对绘制元素坐标。MODE_USERSPACE绝对坐标,即相对于画布的坐标
- SVGFont
font信息,绘制文本时使用
实现思路
大家都知道,svg图片其实就是用xml格式描述的矢量图片,因此实现该库的时候我们只需要将对应的一个个画图操作转换成对应的xml描述即可,当然这里需要大家对svg格式有所了解。生成xml使用了安卓自带的dom,这里当然也可以直接自己生成字符串,不过使用dom有个好处,他会将生成的xml信息保存在内存中,后续还可以通过id快速查找和修改(目前该库没有实现该功能,后续我会增加修改已绘制图形的二次修改能力,并且提供在已有的svg图片上绘制的能力)。
关于api设计,前面也提到过,为了方便安卓程序员快速上手,整体的api设计完全参考安卓canvas,并且复用了canvas的一部分能力(比如SVGPaint继承自Paint)。