一、问题分析
1、需要下载含有中国地图的 SVG文件
下载地址:https://www.amcharts.com/dl/javascript-maps/ ,这里包含世界各个国家的SVG文件以及省份地图的SVG
2、需要SVG资源转换xml文件
转换可以用http://inloop.github.io/svg2android/ 网站
中国地图svg格式如下:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="-1dp"
android:height="-1dp"
android:viewportWidth="-1"
android:viewportHeight="-1">
<path
android:fillColor="#CCCCCC"
android:strokeColor="#ffffff"
android:strokeWidth="0.5"
android:pathData="M541.02,336.29L541.71,336.09L54...."/>
<path ... />
....
</vector>
每个path都代表一个省份
pathData是绘制的命令和对于的坐标值,一个命令后面是两个具体坐标值
例如: M541.02,336.29L541.71,336.09
M是移动命令,后面两个数(541.02,336.29)值是移动的具体坐标
L是绘直线命令,从当前值绘制到L后面的坐标(541.71,336.09)
3、如何将SVG文件绘制成图形?
第一步需要解析xml文件获取path值,可通过dom解析获取xml文件中的值
获取到String类型的Path值后,还需要将String创建Path对象,可以通过PathParser来创建。
Path path = PathParser.createPathFromPathData(strPath);
//Dom 解析 SVG文件
InputStream inputStream = mContext.getResources().openRawResource(R.raw.china);
DocumentBuilderFactory facotory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = facotory.newDocumentBuilder();
Document doc = builder.parse(inputStream);
Element rootElement = doc.getDocumentElement();
NodeList items = rootElement.getElementsByTagName("path");
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);
ProvinceItem item = new ProvinceItem(path);
list.add(new ProvinceItem(path));
}
}catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
获取的Path后,就可以直接通过Canvas绘制了。
canvas.drawPath(path,paint);
4、如何判断点击的坐标在哪个省份?
点击的时候,我们可得到一个(x、y),而我们的地图是有很多的省份的Path绘制而成。那么我们可以讲Path转换成Region(区域),再通过Region的contains判断点击的坐标是否在这个区域内 ,这样我们就知道点击在哪个省份了。
public boolean isTouch(float x, float y) {
Region region = new Region();
//将Path转化为RectF矩形
RectF rectF = new RectF();
path.computeBounds(rectF,true);
region.setPath(path,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));
return region.contains((int)x,(int)y);
}
二、完整代码
MapView类:
package com.metre.svg_demo;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.support.v4.graphics.PathParser;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
public class MapView extends View {
public MapView(Context context) {
this(context,null);
}
public MapView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private Context mContext;
private Paint mPaint;
private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFFFFFFFF};
private void init(Context context){
mContext = context;
loadSVGThread.start();
mPaint = new Paint();
mPaint.setAntiAlias(true);
//colorArray = new int[]{R.color.color_838B8B, R.color.color_607B8B, R.color.color_8FBC8F, R.color.color_8DB6CD};
}
private List<ProvinceItem> itemList = new ArrayList<>();
private Thread loadSVGThread = new Thread(){
@Override
public void run() {
super.run();
//Dom 解析 SVG文件
InputStream inputStream = mContext.getResources().openRawResource(R.raw.china);
DocumentBuilderFactory facotory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = facotory.newDocumentBuilder();
Document doc = builder.parse(inputStream);
Element rootElement = doc.getDocumentElement();
NodeList items = rootElement.getElementsByTagName("path");
List<ProvinceItem> list = new ArrayList<>();
float left = -1;
float top = -1;
float right = -1;
float bottom = -1;
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);
//将Path转化成矩形
RectF rectF = new RectF();
path.computeBounds(rectF,true);
left = left == -1?rectF.left:Math.min(left,rectF.left);
top = top == -1?rectF.top:Math.min(top,rectF.top);
right = right == -1?rectF.right:Math.max(right,rectF.right);
bottom = bottom == -1?rectF.bottom:Math.max(bottom,rectF.bottom);
ProvinceItem item = new ProvinceItem(path);
item.setColor(getResources().getColor(colorArray[i%4]));//随机颜色
list.add(new ProvinceItem(path));
}
//float left, float top, float right, float bottom
totalRect = new RectF(left,top,right,bottom);
itemList = list;
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();
}
}
};
private ProvinceItem select;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(itemList == null)return;
canvas.save();
canvas.scale(scale,scale);
for (ProvinceItem provinceItem : itemList) {
if(select == provinceItem){
provinceItem.drawItem(canvas, mPaint, true);
}else {
provinceItem.drawItem(canvas, mPaint, false);
}
}
}
private RectF totalRect;
private float scale = 1.0f;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//当前控件高宽度
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if(totalRect != null){
float mapWidth = totalRect.width();
scale = width/mapWidth;
}
}
@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;
ProvinceItem selectItem = null;
for (ProvinceItem proviceItem : itemList) {
if(proviceItem.isTouch(x,y)){
selectItem = proviceItem;
}
}
if(selectItem != null){
select = selectItem;
postInvalidate();
}
}
}
ProvinceItem省份类:
package com.metre.svg_demo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.Log;
public class ProvinceItem {
private Path path;
private int color;
public ProvinceItem(Path path) {
this.path = path;
}
public ProvinceItem(Path path, int color) {
this.path = path;
this.color = color;
}
public Path getPath() {
return path;
}
public void setPath(Path path) {
this.path = path;
}
public int getColor() {
return color;
}
public void setColor(int color) {
Log.e("ProvinceItem","setColor -->"+color);
this.color = color;
}
public void drawItem(Canvas canvas, Paint paint, boolean isSelect) {
if(isSelect){
paint.clearShadowLayer();
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.FILL);
paint.setColor(color);
canvas.drawPath(path,paint);
paint.clearShadowLayer();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(0xFFD0E8F4);
canvas.drawPath(path,paint);
}else{
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLACK);
paint.setShadowLayer(8,0,0,0xffffff);
canvas.drawPath(path,paint);
paint.clearShadowLayer();
paint.setStyle(Paint.Style.FILL);
paint.setColor(color);
paint.setStrokeWidth(2);
canvas.drawPath(path,paint);
}
}
public boolean isTouch(float x, float y) {
Region region = new Region();
//将Path转化为RectF矩形
RectF rectF = new RectF();
path.computeBounds(rectF,true);
region.setPath(path,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));
return region.contains((int)x,(int)y);
}
}
网友评论