1、SVG概念
SVG是一种图像文件格式,类似于PNG JPG。只不过PNG这种图片需要图像引擎加载,SVG则是由画布来加载,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形。可以让你设计无损失、高分辨率的Web图形界面。用户可以直接用代码来描述图像。

2、SVG在安卓中能做什么
1、App图标:能SDK23后,APP图标都是由SVG来表示
2、自定义控件:不规则的控件,复杂的交互,子控件重叠判断,图标等都可以用SVG来做
3、复杂动画:如根据用户滑动动态显示动画,路径动画
3、SVG语法:
<path/>路径的路径描述指令含义表:
M = moveto 相当于 android Path 里的moveTo(),用于移动起始点
L = lineto 相当于 android Path 里的lineTo(),用于画线
H = horizontal lineto 用于画水平线
V = vertical lineto 用于画竖直线
C = curveto 相当于cubicTo(),三次贝塞尔曲线
S = smooth curveto 同样三次贝塞尔曲线,更平滑
Q = quadratic Belzier curve quadTo(),二次贝塞尔曲线
T = smooth quadratic Belzier curveto 同样二次贝塞尔曲线,更平滑
A = elliptical Arc 相当于arcTo(),用于画弧
Z = closepath 相当于closeTo(),关闭path
4、Region
Region:不规则的连续区域,是绘制中的区域的意思。使用Region可以对图形有很多操作,比如区域的合并,取交集,取抑或等
设置一个连续的矩形区域
public boolean set(Region region)
设置一个path的区域与一个矩形的交集产生的连续性区域
public boolean setPath(Path path, Region clip)
区域中是否包含该坐标
public boolean contains(int x, int y)
使用实例:完成中国地图的绘制,并能正常点击省份。
效果如下图:

public class MapView extends View {
private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFFFFFFFF};
private Context context;
private List<ProviceItem> itemList;
private Paint paint;
private ProviceItem select;
private RectF totalRect;
private float scale = 1.0f;//缩放比例
public MapView(Context context) {
super(context);
}
public MapView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
this.context = context;
paint = new Paint();
paint.setAntiAlias(true);
itemList = new ArrayList<>();
loadThread.start();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取到当前控件宽高值
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (totalRect != null) {
double mapWidth = totalRect.width();
scale = (float) (width / mapWidth);//适配手机 计算缩放比
}
setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
private Thread loadThread = new Thread() {
@Override
public void run() {
final InputStream inputStream = context.getResources().openRawResource(R.raw.china);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //取得DocumentBuilderFactory实例
DocumentBuilder builder = null; //从factory获取DocumentBuilder实例
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(inputStream); //解析输入流 得到Document实例
Element rootElement = doc.getDocumentElement();
NodeList items = rootElement.getElementsByTagName("path");
float left = -1;
float right = -1;
float top = -1;
float bottom = -1;
List<ProviceItem> list = new ArrayList<>();
for (int i = 0; i < items.getLength(); i++) {
Element element = (Element) items.item(i);
String pathData = element.getAttribute("android:pathData");
@SuppressLint("RestrictedApi") Path path = PathParser.createPathFromPathData(pathData);
ProviceItem proviceItem = new ProviceItem(path);
proviceItem.setDrawColor(colorArray[i % 4]);
RectF rect = new RectF();
path.computeBounds(rect, true);
left = left == -1 ? rect.left : Math.min(left, rect.left);
right = right == -1 ? rect.right : Math.max(right, rect.right);
top = top == -1 ? rect.top : Math.min(top, rect.top);
bottom = bottom == -1 ? rect.bottom : Math.max(bottom, rect.bottom);
list.add(proviceItem);
}
itemList = list;
totalRect = new RectF(left, top, right, bottom);
//刷新界面
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
requestLayout();
invalidate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
@Override
public boolean onTouchEvent(MotionEvent event) {
handleTouch(event.getX() / scale, event.getY() / scale);
return super.onTouchEvent(event);
}
private void handleTouch(float x, float y) {
if (itemList == null) {
return;
}
ProviceItem selectItem = null;
for (ProviceItem proviceItem : itemList) {
if (proviceItem.isTouch(x, y)) {
selectItem = proviceItem;
}
}
if (selectItem != null) {
select = selectItem;
postInvalidate();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (itemList != null) {
canvas.save();
canvas.scale(scale, scale);
for (ProviceItem proviceItem : itemList) {
if (proviceItem != select) {
proviceItem.drawItem(canvas, paint, false);
} else {
select.drawItem(canvas, paint, true);
}
}
}
}
}
ProviceItem.class是对一个省份的封装,路径和颜色,是否选中,是否点击的判断。
public class ProviceItem {
private Path path;
/**
* 绘制颜色
*/
private int drawColor;
public ProviceItem(Path path) {
this.path = path;
}
public void setDrawColor(int drawColor) {
this.drawColor = drawColor;
}
void drawItem(Canvas canvas, Paint paint, boolean isSelect) {
if (isSelect) {
//绘制内部的颜色
paint.clearShadowLayer();
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.FILL);
paint.setColor(drawColor);
canvas.drawPath(path, paint);
//绘制边界
paint.setStyle(Paint.Style.STROKE);
int strokeColor = 0xFFD0E8F4;
paint.setColor(strokeColor);
canvas.drawPath(path, paint);
} else {
paint.setStrokeWidth(2);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
paint.setShadowLayer(8, 0, 0, 0xffffff);
canvas.drawPath(path, paint);
//绘制边界
paint.clearShadowLayer();
paint.setColor(drawColor);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(2);
canvas.drawPath(path, paint);
}
}
public boolean isTouch(float x, float y) {
RectF rectF = new RectF();
path.computeBounds(rectF, true);
Region region = new Region();
region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
return region.contains((int) x, (int) y);
}
}
svg地图下载地址:
https://www.amcharts.com/download/

网友评论