最近发现了一个好玩的东西:SVG,秉承着学以致用的原则,做了一个小demo。
先上效果图:
这是一个可以交互的中国地图.
像这类复杂的自定义空间,如果要我们自己来画,我估计会让我开始怀疑人生吧!
好了 废话不多说,下面我们就用svg的方式来实现这个自定义的地图控件:
步骤:
1:下载中国地图的svg文件
2:然后将svg文件转换成android能用的xml文件:附上链接一个:http://inloop.github.io/svg2android/
3:由于步骤2解析到的SVG是xml格式的,如果说不做交互的话,可以直接显示(可改变源文件的颜色),如果要做交互的话,就需要获取到每一个省对应的path,然后设置交互点击事件了,这样的话,就需要解析得到我们需要的path。
4:解析完之后,就需要将每一个path会知道canvas中去
5:设置点击事件,判断当前点击的是哪个省份
这里1,2步就直接略过了,我们从第三步开始:
//开启一个线程,用于解析xml文件
private Thread parseSvgThread = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = mContext.getResources().openRawResource(R.raw.china);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(inputStream);
Element rootElements = doc.getDocumentElement();
//解析path节点
NodeList items = rootElements.getElementsByTagName("path");
for(int i = 0;i<items.getLength();i++){
Element element = (Element) items.item(i);
String pathData = element.getAttribute("android:pathData");
Path path = PathParser.createPathFromPathData(pathData);
//将解析的每一条path创建一个javabean,然后放到容器中
Province province = new Province(path);
itemLists.add(province);
}
//解析完成之后,发送handler 通知解析完成并设置颜色
mHandler.sendEmptyMessage(0);
} catch (Exception e) {
e.printStackTrace();
}
}
});
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if(itemLists != null && itemLists.size() > 0){
int size = itemLists.size();
for(int i = 0;i<size;i++){
//简单设置下颜色itemLists.get(i).setDrawColor(colors[i%colors.length]);
}
//更新绘制
invalidate();
}
}
};
由于解析过程比较耗时,所以我们开启一个独立的线程来解析,解析完成之后通过handler来设置每一个省的颜色,然后更新绘制
第四步:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//1:画选择的
//2画没别选择的
if(itemLists != null && itemLists.size()>0){
canvas.save();
canvas.scale(scale,scale);
if(mSelectedItem != null){
mSelectedItem.drawableProvince(mPaint,canvas,true);
}
for(int i = 0;i<itemLists.size();i++){
if(itemLists.get(i) != mSelectedItem){
itemLists.get(i).drawableProvince(mPaint,canvas,false);
}
}
canvas.restore();
}
}
然后在province的bean中,我们来做绘画的操作:
//在画板上画
public void drawableProvince(Paint paint, Canvas canvas,boolean isSelected){
if(isSelected){
//当前被选择的省
//画阴影
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(2);
paint.setColor(Color.BLACK);
paint.setShadowLayer(8,0,0,0xffffff);
canvas.drawPath(path,paint);
//画填充部分
paint.clearShadowLayer();
paint.setColor(Color.RED);
canvas.drawPath(path,paint);
}else{
//画填充部分
paint.setColor(drawColor);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
canvas.drawPath(path,paint);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(0xFFD0E8F4);
canvas.drawPath(path,paint);
}
}
到这里,地图就可以按照我们自定义的颜色来显示了。
最后一步,设置监听,首先,我们在prvoince这个bean中,写一个方法,来判断当前点击的这个点是否在province中:
//判断某一点是否包含在该省内
public boolean isContain(int x,int 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(x,y);
}
这里使用的方法是:创建一个包含path的区域(region),然后使用region的contains方法来进行判断。
最后,我们只需要在mapview中来处理touch事件并刷新ui就可以了。