Android UI SVG使用
1.
SVG是什么?
- 可缩放矢量图形(Scalable Vector Graphics)
- SVG于2003 年1月14日成为 W3C 推荐标准
2.
SVG特性
- SVG可被很多工具读取和使用
- SVG跟PNG,JPG,GIF图像比起来,尺寸更小,可压缩性更强
- SVG是可伸缩的
- SVG适配性好,任意放大缩小不失真
3.
SVG使用场景
- Android SDK23 APP的图标都是由SVG来制作的
- 不规则控件,复杂的交互,子控件重叠判断,图标等都可以用SVG来做
- 复杂路径动画等
4.
SVG使用
自己制作SVG:https://editor.method.ac/ (在线SVG编辑器)
SVG文件下载:https://www.amcharts.com/download/ (比如中国地图等)
5.
SVG常用方法
接下来利用SVG画一个中国地图,并且实现各个省份有可点击事件
6.
涉及到知识点
- 自定义View (MapView.java)
public class MapView extends View {
private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFFFFFFFF};
private Context context;
private List<ProvinceItem> itemList;
private Paint paint;
private ProvinceItem select;
private RectF totalRect;
private float scale = 1.0f;
public MapView(Context context) {
super(context);
}
public MapView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
this.context = context;
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) {
double mapWidth = totalRect.width();
// SVG缩放适配
scale = (float) (width / mapWidth);
}
setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
private Thread loadThread = new Thread() {
@Override
public void run() {
final InputStream inputStream = context.getResources().openRawResource(R.raw.china);
// 取得DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//从factory获取DocumentBuilder实例
DocumentBuilder builder = null;
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<ProvinceItem> list = new ArrayList<>();
// 解析所有省份SVG数据,然后将省份所对应字符串转换为Path绘制数据
for (int i = 0; i < items.getLength(); i++) {
// Dom解析xml
Element element = (Element) items.item(i);
String pathData = element.getAttribute("android:pathData");
@SuppressLint("RestrictedApi")
Path path = PathParser.createPathFromPathData(pathData);
ProvinceItem proviceItem = new ProvinceItem(path);
proviceItem.setDrawColor(colorArray[i % 4]);
// 通过计算地图所占最大矩形区域(左,上,右,下位置,来计算缩放scale)
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);
}
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 (Exception e) {
e.printStackTrace();
}
}
};
@Override
public boolean onTouchEvent(MotionEvent event) {
// 适配点击事件区域X,Y
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();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (itemList != null) {
canvas.save();
canvas.scale(scale, scale);
for (ProvinceItem proviceItem : itemList) {
if (proviceItem != select) {
proviceItem.drawItem(canvas, paint, false);
} else {
select.drawItem(canvas, paint, true);
}
}
}
}
}
- 封装每个省份信息对象
public class ProvinceItem {
private Path path;
// 绘制颜色
private int drawColor;
public ProvinceItem(Path path) {
this.path = 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(drawColor);
canvas.drawPath(path, paint);
// 绘制省份边界
paint.setStyle(Paint.Style.STROKE);
int strokeColor = 0xFFD0E8F4;
paint.setColor(strokeColor);
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);
paint.setStrokeWidth(2);
canvas.drawPath(path, paint);
}
}
// 判断手指点击区域所在省份
// Android Region 区域点击
public boolean isTouch(float x, float 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((int) x, (int) y);
}
}
- 最后需要一个 中国地图SVG,可从上面提供网站下载
7.
参考