概述
先上效果图,利用SVG中国地图实现不规则图形,点击各省的不规则图像,让省份颜色变红。
原理解析
svg矢量图是一种体积小,放大后都不会模糊的图片格式,适用于纯色或者简单的图案,svg矢量图其实也可以看成类似xml的文件,使用文本编辑器就可以打开,svg都有一个path,类似于java中的path,我们可以利用DocumentBuilderFactory对svg进行解析,从而拿到svg的path路径,然后重新绘制,但是解析是一个耗时的事情,我们开启一个子线程进行解析。
代码实现
首先是定义一个Map类继承自View,在构造方法中对画笔等进行初始化,然后开启一个线程解析svg
public Map(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
provices = new ArrayList();
paint = new Paint();
paint.setAntiAlias(true);
provices = new ArrayList<>();
new LoadThread().start();//读取数据
}
然后我们定义一个省份的JavaBean,每个省份都是一个对象,每个省份包括自己的path路径和绘制颜色,完了我们在创建一个绘制path的方法,最后关于点击,我们创建一个方法public boolean isTouch(float x, float y),传入坐标点,判断坐标是否在该省份的path范围内。
public class ProviceBean {
private Path mPath;
private int drawColor;
public ProviceBean(Path path) {
mPath = 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(Color.RED);
canvas.drawPath(mPath, paint);
// 绘制边界
paint.setStyle(Paint.Style.STROKE);
int strokeColor = 0xFFD0E8F4;
paint.setColor(strokeColor);
canvas.drawPath(mPath, paint);
} else {
// 绘制内部的颜色
paint.setStrokeWidth(2);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
paint.setShadowLayer(8, 0, 0, 0xffffff);
canvas.drawPath(mPath, paint);
// 绘制边界
paint.clearShadowLayer();
paint.setColor(drawColor);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(2);
canvas.drawPath(mPath, paint);
}
}
public boolean isTouch(float x, float y) {
RectF rectF = new RectF();
mPath.computeBounds(rectF, true);
Region region = new Region();
region.setPath(mPath, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
return region.contains((int) x, (int) y);
}
}
完了定义一个Map类继承自View ,在构造函数对paint进行初始化,并且开启一个线程解析svg矢量图,因为解析是一个耗时的过程,单独使用一个线程。
public Map(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
provices = new ArrayList();
paint = new Paint();
paint.setAntiAlias(true);
provices = new ArrayList<>();
new LoadThread().start();//读取数据
}
private class LoadThread extends Thread {
private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1};
@Override
public void run() {
super.run();
final InputStream inputStream = mContext.getResources().openRawResource(R.raw.map_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<ProviceBean> list = new ArrayList<>();
for (int i = 0; i < items.getLength(); i++) {
Element element = (Element) items.item(i);
String pathData = element.getAttribute("d");
Path path = PathParser.createPathFromPathData(pathData);
ProviceBean proviceItem = new ProviceBean(path);
proviceItem.setDrawColor(colorArray[i % 3]);
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);
}
provices = 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();
}
}
}
为了让整个地图的宽度铺满屏幕的宽度,我们在这里重写onMeasure方法,获得一个屏幕宽度与svg图计算宽度的比例系数,然后在onDraw方法中对画布进行缩放使其正好铺满屏幕的宽度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 获取到当前控件宽高值
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (totalRect != null) {
scale = width / totalRect.width();
}
setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.scale(scale, scale);
for (ProviceBean provice : provices) {
provice.drawItem(canvas, paint, select == provice ? true : false);
}
}
最后就是点击事件的处理了,这里比较简单,因为我们对画布做了缩放,因此在点击这里也应该对坐标做相应的缩放。
@Override
public boolean onTouchEvent(MotionEvent event) {
for (ProviceBean provice : provices) {
boolean isTouch = provice.isTouch(event.getX() / scale, event.getY() / scale);
if (isTouch) {
select = provice;
invalidate();
break;
}
}
return super.onTouchEvent(event);
}
Map的完整代码
public class Map extends View {
private List<ProviceBean> provices;
private Paint paint;
private ProviceBean select;
private RectF totalRect;
private float scale = 1.0f;//缩放比例,让地图在屏幕全屏显示
private Context mContext;
public Map(Context context) {
this(context, null);
}
public Map(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public Map(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
provices = new ArrayList();
paint = new Paint();
paint.setAntiAlias(true);
provices = new ArrayList<>();
new LoadThread().start();//读取数据
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 获取到当前控件宽高值
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (totalRect != null) {
scale = width / totalRect.width();
}
setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.scale(scale, scale);
for (ProviceBean provice : provices) {
provice.drawItem(canvas, paint, select == provice ? true : false);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
for (ProviceBean provice : provices) {
boolean isTouch = provice.isTouch(event.getX() / scale, event.getY() / scale);
if (isTouch) {
select = provice;
invalidate();
break;
}
}
return super.onTouchEvent(event);
}
private class LoadThread extends Thread {
private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1};
@Override
public void run() {
super.run();
final InputStream inputStream = mContext.getResources().openRawResource(R.raw.map_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<ProviceBean> list = new ArrayList<>();
for (int i = 0; i < items.getLength(); i++) {
Element element = (Element) items.item(i);
String pathData = element.getAttribute("d");
Path path = PathParser.createPathFromPathData(pathData);
ProviceBean proviceItem = new ProviceBean(path);
proviceItem.setDrawColor(colorArray[i % 3]);
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);
}
provices = 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();
}
}
}
}
Demo:https://github.com/987570437/SVGDemo
最后
以上就是复杂金针菇为你收集整理的自定义View之SVG矢量图实现不规则图像及点击事件的全部内容,希望文章能够帮你解决自定义View之SVG矢量图实现不规则图像及点击事件所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复