发布于2021-03-07 21:59 阅读(1586) 评论(0) 点赞(23) 收藏(3)
SVG是一种图像文件格式,类似于PNG JPG。只不过PNG需要图像引擎加载,SVG则由画布来加载 它的英文全称为Scalable
Vector Graphics,意思为可缩放的矢量图形。可以设计无损失、高分辨率的Web图形页面。用户可以直接用代码来描绘图像。
是不是很抽象?没耐心看下去,那就点击看看这篇博客,就知道它干嘛用了。
附张图吧
<svg width= 580 height=400"
xmIns= http://www.w3.org/2000/svg>
<title>L ayerl</title>
<line stroke-linecap= undefind stroke linejoin= undefind
ld=svg_1y2=119.4375x2=''412.53211"yl="119.4375"x1='77.5"
Stroke-width= 1.5 stroke- #000" fill= none" />
</svg>
M=moveto(M X,Y)
:将画笔移动到指定位置L=lineto(L X,Y)
: 画直线到指定位置H=horizontal lineto(H X)
: 画水平线到指定的X坐标位置V=vertical lineto(VY)
:画垂直线到指定的Y坐标位置Q=quadratic Belzier curve(Q X,ENDX,ENDY)
:二次贝塞尔曲线C=curveto(Q X1,Y1,X2,Y2,ENDX,ENDY)
:三次贝塞尔曲线S=smooth curveto(S X1,Y1,ENDX,ENDY)
:平滑过渡Z=closepath()
: 闭合路径https://www.amcharts.com/svg-maps/
这个网站里面的资源好像挺丰富的,以后有需要可以上来参考看看。
看网上有很多我们大中国的地图示例了,我就下个巴铁的地图,试试能不能给咱巴铁弄一份。
先按步骤,下载巴基斯坦的svg地图矢量图。
将下载完成的svg图片放入项目的raw文件夹下
点开svg图片,我们就能看到预览效果
再来看看svg文件中的代码
<?xml version="1.0" encoding="utf-8"?>
<!-- (c) ammap.com | SVG map of Pakistan - High -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:amcharts="http://amcharts.com/ammap"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 800 700"
version="1.1">
<defs>
<style type="text/css">
.land
{
fill: #CCCCCC;
fill-opacity: 1;
stroke:white;
stroke-opacity: 1;
stroke-width:0.5;
}
</style>
<amcharts:ammap projection="mercator" leftLongitude="60.896610" topLatitude="37.095717" rightLongitude="77.843840" bottomLatitude="23.705520"></amcharts:ammap>
<!-- All areas are listed in the line below. You can use this list in your script. -->
<!--{id:"PK-BA"},{id:"PK-GB"},{id:"PK-IS"},{id:"PK-JK"},{id:"PK-KP"},{id:"PK-PB"},{id:"PK-SD"},{id:"PK-TA"}-->
</defs>
<g>
<!--各省的path-->
<path />
<path />
<path />
...
</g>
</svg>
来个中国红?改属性:fill: #ff0000
;
奔跑的小恐龙?
如何能转成Android项目能使用的VectorDrawable xml文件呢,步骤如下:
首先需要在svg文件中加入viewBox
属性(参考上面的svg文件代码),然后按下图操作:
选中svg文件,然后确定
便能在目标目录中看到生成好的文件
点击查看代码
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="700dp"
android:viewportWidth="800"
android:viewportHeight="700">
<!--各省份的path的属性-->
<path
android:pathData="balabala"
android:strokeWidth="0.5"
android:fillColor="#ff0000"
android:strokeColor="#ffffff"/>
<path/>
...
</vector>
这个时候你就可以指定各省的属性了。
这个示例是给巴铁的各省添加上了点击事件,点击的时候展示相应省份的名字。
看看效果
附上主要代码
定义ProvinceItem
对应各个省份
public class ProvinceItem {
private Path path;
private String name;
private int drawColor;//板块颜色
private PointF clickPoint;//显示省份信息
public ProvinceItem(Path path) {
this.path = path;
}
public Path getPath() {
return path;
}
public void setPath(Path path) {
this.path = path;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDrawColor() {
return drawColor;
}
public void setDrawColor(int drawColor) {
this.drawColor = drawColor;
}
public PointF getClickPoint() {
return clickPoint;
}
public void setClickPoint(PointF clickPoint) {
this.clickPoint = clickPoint;
}
void drawItem(Canvas canvas, Paint paint, boolean isSelect) {
if (isSelect) {
//绘制内部颜色
paint.clearShadowLayer();
paint.setStrokeWidth(1);
paint.setColor(drawColor);
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
//绘制边界
paint.setStyle(Paint.Style.STROKE);
paint.setColor(0xff0e8ef4);
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);
canvas.drawPath(path, paint);
}
}
/**
* 判断点击区域是否在当前的省份
*
* @param x
* @param y
* @return
*/
public boolean isTouch(float x, float y) {
//获取path矩形区域
RectF recrF = new RectF();
path.computeBounds(recrF, true);
Region region = new Region();
//给定路径
region.setPath(path, new Region((int) recrF.left, (int) recrF.top, (int) recrF.right, (int) recrF.bottom));
return region.contains((int) x, (int) y);
}
}
自定义View来展示地图:
public class MapView extends View {
private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFF4087A3};
private List<ProvinceItem> itemList;//所有省份集合
private Paint paint;
private ProvinceItem select;//当前选中的省份
private RectF totalRect;//地图大小信息
private float scale = 1.0f;
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
// 第一个按下的手指的点
private PointF startPoint = new PointF();
// 两个按下的手指的触摸点的中点
private PointF midPoint = new PointF();
// 初始的两个手指按下的触摸点的距离
private float oriDis = 1f;
private boolean actionClick = true;
private float translateX;
private float translateY;
private boolean shouldShowText;//是否需要显示省份名
public MapView(Context context) {
super(context);
init();
}
public MapView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MapView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
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) {
float mapWidth = totalRect.width();
scale = width / mapWidth;
}
setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float currentScaleCount = 0;//当前缩放系数
float currentTranslateX = 0;//当前x平移距离
float currentTranslateY = 0;//当前y平移距离
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//单点触控
startPoint.set(event.getX(), event.getY());
mode = DRAG;
actionClick = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
//多点触控
Log.e("多点触控", "多点触控");
oriDis = distance(event);
if (oriDis > 10) {
midPoint = midPoint(event);
mode = ZOOM;
}
actionClick = false;
break;
case MotionEvent.ACTION_MOVE:
//滑动
Log.e("mode", mode + " ");
if (mode == DRAG) {
//单指拖动
if (Math.abs(x - startPoint.x) > 10 || Math.abs(y - startPoint.y) > 10) {
currentTranslateX = translateX + x - startPoint.x;
currentTranslateY = translateY + y - startPoint.y;
translateX = currentTranslateX;
translateY = currentTranslateY;
startPoint.set(x, y);
actionClick = false;
invalidate();
}
} else if (mode == ZOOM) {
//两指缩放
float newDist = distance(event);//当前两指距离
if (Math.abs(newDist - oriDis) > 10) {
float scaleInner = newDist / oriDis;
currentScaleCount = scale + (scaleInner - 1);
if (currentScaleCount < 1) {
scale = 1;
} else {
scale = currentScaleCount;
}
oriDis = newDist;
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
//单点触控
mode = NONE;
if (actionClick) {
handleTouch(x / scale - translateX, y / scale - translateY);
}
break;
case MotionEvent.ACTION_POINTER_UP:
//多点触控
mode = NONE;
break;
}
return true;
}
private void handleTouch(float x, float y) {
shouldShowText = false;
if (itemList == null) {
return;
}
ProvinceItem selectItem = null;
for (ProvinceItem provinceItem : itemList) {
if (provinceItem.isTouch(x, y)) {
selectItem = provinceItem;
provinceItem.setClickPoint(new PointF(x, y));
shouldShowText = true;
}
}
if (selectItem != null) {
select = selectItem;
postInvalidate();
}
}
private Thread loadThread = new Thread() {
@Override
public void run() {
InputStream inputStream = getResources().openRawResource(R.raw.pakistan);
//获取DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//从DocumentBuilderFactory获取DocumentBuilder实例
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
//解析输入流 获取Document实例
Document document = builder.parse(inputStream);
Element rootElement = document.getDocumentElement();
//先找到Path
NodeList path = rootElement.getElementsByTagName("path");
float left = -1;
float right = -1;
float top = -1;
float bottom = -1;
List<ProvinceItem> list = new ArrayList<>();
for (int i = 0; i < path.getLength(); i++) {
Element element = (Element) path.item(i);
String pathData = element.getAttribute("d");
String name = element.getAttribute("title");
//将pathData转成Path
@SuppressLint("RestrictedApi") Path path1 = PathParser.createPathFromPathData(pathData);
ProvinceItem provinceItem = new ProvinceItem(path1);
provinceItem.setName(name);
provinceItem.setDrawColor(colorArray[i % 4]);
RectF rectF = new RectF();
path1.computeBounds(rectF, true);
left = left == -1 ? rectF.left : Math.min(left, rectF.left);
right = right == -1 ? rectF.right : Math.max(right, rectF.right);
top = top == -1 ? rectF.top : Math.min(top, rectF.top);
bottom = bottom == -1 ? rectF.bottom : Math.max(bottom, rectF.bottom);
list.add(provinceItem);
}
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 (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (itemList != null && itemList.size() > 0) {
canvas.save();
canvas.scale(scale, scale);
canvas.translate(translateX, translateY);
for (ProvinceItem provinceItem : itemList) {
if (provinceItem != select) {
provinceItem.drawItem(canvas, paint, false);
} else {
provinceItem.drawItem(canvas, paint, true);
}
}
if (shouldShowText) {
//绘制文本
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(40);
canvas.drawText(select.getName(), select.getClickPoint().x, select.getClickPoint().y, paint);
canvas.restore();
}
}
}
/**
* 计算两个手指头之间的中心点的位置
* x = (x1+x2)/2;
* y = (y1+y2)/2;
*
* @param event 触摸事件
* @return 返回中心点的坐标
*/
private PointF midPoint(MotionEvent event) {
float x = (event.getX(0) + event.getX(1)) / 2;
float y = (event.getY(0) + event.getY(1)) / 2;
return new PointF(x, y);
}
/**
* 计算两个手指间的距离
*
* @param event 触摸事件
* @return 放回两个手指之间的距离
*/
private float distance(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);//两点间距离公式
}
}
最后引用即可:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<pers.owen.svgmap.MapView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
本文完,Demo下载地址。
About
作者:前端霸主
链接:http://www.qianduanheidong.com/blog/article/33519/53950e604ffe8f218dfe/
来源:前端黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 前端黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-3
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!