效果如下:
点击相应地图位置,判断选中的区域
技术分析:
1、首先通过svg,画出图像,更加美工提供的svg文件,来到http://inloop.github.io/svg2android/
转出
拿到转出的文件,进行xml解析,得到path标签的pathData属性,(其实就是多条path)
Path指令解析如下所示:
M = moveto(M X,Y) :将画笔移动到指定的坐标位置,相当于 android Path 里的moveTo()
L = lineto(L X,Y) :画直线到指定的坐标位置,相当于 android Path 里的lineTo()
H = horizontal lineto(H X):画水平线到指定的X坐标位置
V = vertical lineto(V Y):画垂直线到指定的Y坐标位置
C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝赛曲线
S = smooth curveto(S X2,Y2,ENDX,ENDY) 同样三次贝塞尔曲线,更平滑
Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次贝赛曲线
T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射 同样二次贝塞尔曲线,更平滑
A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线 ,相当于arcTo()
Z = closepath():关闭路径(会自动绘制链接起点和终点)
利用工具类将pathData转成一个path对象,拿到path就可以直接绘制了。
public class TaiWanSVG extends View {
private Handler handler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
}
};
private List areaPathItemList = new ArrayList<>();
private AreaPathItem selectAreaPath;
private Paint paint;
private float Scale = 0.8f;
public TaiWanSVG(Context context) {
this(context, null);
}
public TaiWanSVG(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public void init() {
paint = new Paint();
paint.setAntiAlias(true);
//开启线程,读取svg中的pathdata数据
new Thread() {
@Override
public void run() {
try {
InputStream ips = getResources().openRawResource(R.raw.taiwan);
//解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
//解析xml文件
Document document = builder.parse(ips);
//得到所有path节点
NodeList nl = document.getElementsByTagName("path");
for (int i = 0; i < nl.getLength(); i++) {
String pathData = ((Element) nl.item(i)).getAttribute("android:pathData");
areaPathItemList.add(new AreaPathItem(PathParser.createPathFromPathData(pathData)));
}
handler.sendEmptyMessage(1);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if(null!=areaPathItemList&&areaPathItemList.size()>0){
canvas.scale(Scale,Scale);
for(AreaPathItem areaPathItem:areaPathItemList){
if(areaPathItem!=selectAreaPath){
areaPathItem.draw(canvas,paint,false);
}else{
}
}
if(selectAreaPath!=null){
selectAreaPath.draw(canvas,paint,true);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if(null!=areaPathItemList&&areaPathItemList.size()>0){
for(AreaPathItem areaPathItem:areaPathItemList){
if(areaPathItem.isContain(event.getX()/Scale,event.getY()/Scale)){
selectAreaPath = areaPathItem;
postInvalidate();
break;
}
}
}
break;
}
return super.onTouchEvent(event);
}
}
2.通过Region判断点击的点,是否在path的区域中
要判断一个点是否在矩形范围很好判断,但是要判断一个点是否在一个不规则的范围中,则很难判断
Region这个类,可以将一个不规则的path,在一个矩形区域内,分割成很多个小的矩形区域,类似微积分,从而实现判断点是否包含在path,矩形区域是否包含或者相交于path等功能
/**
* Set the region to the area described by the path and clip.
* Return true if the resulting region is non-empty. This produces a region
* that is identical to the pixels that would be drawn by the path
* (with no antialiasing).
*/
public boolean setPath(Path path, Region clip) {
return nativeSetPath(mNativeRegion, path.readOnlyNI(), clip.mNativeRegion);
}
public class AreaPathItem {
private Path path;
public AreaPathItem(Path path) {
this.path = path;
}
public void draw(Canvas canvas, Paint paint, boolean isSelect) {
if (!isSelect) {
//绘制区域
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GRAY);
canvas.drawPath(path, paint);
//绘制区域边界
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, paint);
} else {
//绘制选中背景
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setShadowLayer(8,0,0,Color.BLUE);
canvas.drawPath(path, paint);
paint.clearShadowLayer();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
canvas.drawPath(path, paint);
}
}
/**
* @param x
* @param y
* @return path区域是否包含 x,y坐标点
*/
public boolean isContain(float x, float y) {
RectF rectF = new RectF();
path.computeBounds(rectF,false);
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);
}
}
源码下载