目錄
- SVG概念
- SVG特性
- SVG在安卓中能做什么
- 標準SVG預覽
- SVG語法介紹
- SVG圖下載地址
- 巴鐵!奔跑的小恐龍?(示例開始)
- 自定義View展示SVG
SVG概念
SVG是一種影像檔案格式,類似于PNG JPG,只不過PNG需要影像引擎加載,SVG則由畫布來加載 它的英文全稱為Scalable
Vector Graphics,意思為可縮放的矢量圖形,可以設計無損失、高解析度的Web圖形頁面,用戶可以直接用代碼來描繪影像,
SVG特性
- SVG可被非常 多的工具讀取和修改(比如記事本)
- SVG與JPEG和GIF影像 比起來尺寸更小,且可縮放性更強
- SVG是可伸縮的
- SVG影像可在 任何解析度下被高質量地列印
- SVG可在影像質量不下降地情況下被放大
- SVG影像中的文本 是可以自定義的可以融入代碼片段
- SVG檔案是純粹的XML
SVG在安卓中能做什么
- APP圖示:在SDK23后,APP的圖示是由SVG來表示
- 自定義控制元件:不規則的控制元件,復雜的互動,子控制元件重疊判斷,圖表等都
可以用SVG來做 - 復雜影片:如根據用戶滑動動態顯示影片,路徑影片
是不是很抽象?沒耐心看下去,那就點擊看看這篇博客,就知道它干嘛用了,
Android中使用SVG矢量圖打造多邊形圖形框架
附張圖吧

標準SVG預覽
<svg width= 580 height=400"
xmIns= http://www.w3.org/2000/svg>
<title>L ayerl</title>
<line stroke-linecap= undefind stroke linejoin= undefind
ld=svg_1y2=119.4375x2=''412.53211"yl="119.4375"x1='77.5"
Stroke-width= 1.5 stroke- #000" fill= none" />
</svg>
SVG語法介紹
M=moveto(M X,Y):將畫筆移動到指定位置L=lineto(L X,Y): 畫直線到指定位置H=horizontal lineto(H X): 畫水平線到指定的X坐標位置V=vertical lineto(VY):畫垂直線到指定的Y坐標位置Q=quadratic Belzier curve(Q X,ENDX,ENDY):二次貝塞爾曲線C=curveto(Q X1,Y1,X2,Y2,ENDX,ENDY):三次貝塞爾曲線S=smooth curveto(S X1,Y1,ENDX,ENDY):平滑過渡Z=closepath(): 閉合路徑
SVG圖下載地址
https://www.amcharts.com/svg-maps/

這個網站里面的資源好像挺豐富的,以后有需要可以上來參考看看,
巴鐵!奔跑的小恐龍?(示例開始)
看網上有很多我們大中國的地圖示例了,我就下個巴鐵的地圖,試試能不能給咱巴鐵弄一份,
先按步驟,下載巴基斯坦的svg地圖矢量圖,



將下載完成的svg圖片放入專案的raw檔案夾下

點開svg圖片,我們就能看到預覽效果

再來看看svg檔案中的代碼
<?xml version="1.0" encoding="utf-8"?>
<!-- (c) ammap.com | SVG map of Pakistan - High -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:amcharts="http://amcharts.com/ammap"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 800 700"
version="1.1">
<defs>
<style type="text/css">
.land
{
fill: #CCCCCC;
fill-opacity: 1;
stroke:white;
stroke-opacity: 1;
stroke-width:0.5;
}
</style>
<amcharts:ammap projection="mercator" leftLongitude="60.896610" topLatitude="37.095717" rightLongitude="77.843840" bottomLatitude="23.705520"></amcharts:ammap>
<!-- All areas are listed in the line below. You can use this list in your script. -->
<!--{id:"PK-BA"},{id:"PK-GB"},{id:"PK-IS"},{id:"PK-JK"},{id:"PK-KP"},{id:"PK-PB"},{id:"PK-SD"},{id:"PK-TA"}-->
</defs>
<g>
<!--各省的path-->
<path />
<path />
<path />
...
</g>
</svg>
來個中國紅?改屬性:fill: #ff0000;

奔跑的小恐龍?
如何能轉成Android專案能使用的VectorDrawable xml檔案呢,步驟如下:
首先需要在svg檔案中加入viewBox屬性(參考上面的svg檔案代碼),然后按下圖操作:
選中svg檔案,然后確定

便能在目標目錄中看到生成好的檔案

點擊查看代碼
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="700dp"
android:viewportWidth="800"
android:viewportHeight="700">
<!--各省份的path的屬性-->
<path
android:pathData="balabala"
android:strokeWidth="0.5"
android:fillColor="#ff0000"
android:strokeColor="#ffffff"/>
<path/>
...
</vector>
這個時候你就可以指定各省的屬性了,
自定義View展示SVG
這個示例是給巴鐵的各省添加上了點擊事件,點擊的時候展示相應省份的名字,
看看效果

附上主要代碼
定義ProvinceItem對應各個省份
public class ProvinceItem {
private Path path;
private String name;
private int drawColor;//板塊顏色
private PointF clickPoint;//顯示省份資訊
public ProvinceItem(Path path) {
this.path = path;
}
public Path getPath() {
return path;
}
public void setPath(Path path) {
this.path = path;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDrawColor() {
return drawColor;
}
public void setDrawColor(int drawColor) {
this.drawColor = drawColor;
}
public PointF getClickPoint() {
return clickPoint;
}
public void setClickPoint(PointF clickPoint) {
this.clickPoint = clickPoint;
}
void drawItem(Canvas canvas, Paint paint, boolean isSelect) {
if (isSelect) {
//繪制內部顏色
paint.clearShadowLayer();
paint.setStrokeWidth(1);
paint.setColor(drawColor);
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
//繪制邊界
paint.setStyle(Paint.Style.STROKE);
paint.setColor(0xff0e8ef4);
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);
canvas.drawPath(path, paint);
}
}
/**
* 判斷點擊區域是否在當前的省份
*
* @param x
* @param y
* @return
*/
public boolean isTouch(float x, float y) {
//獲取path矩形區域
RectF recrF = new RectF();
path.computeBounds(recrF, true);
Region region = new Region();
//給定路徑
region.setPath(path, new Region((int) recrF.left, (int) recrF.top, (int) recrF.right, (int) recrF.bottom));
return region.contains((int) x, (int) y);
}
}
自定義View來展示地圖:
public class MapView extends View {
private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFF4087A3};
private List<ProvinceItem> itemList;//所有省份集合
private Paint paint;
private ProvinceItem select;//當前選中的省份
private RectF totalRect;//地圖大小資訊
private float scale = 1.0f;
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
// 第一個按下的手指的點
private PointF startPoint = new PointF();
// 兩個按下的手指的觸摸點的中點
private PointF midPoint = new PointF();
// 初始的兩個手指按下的觸摸點的距離
private float oriDis = 1f;
private boolean actionClick = true;
private float translateX;
private float translateY;
private boolean shouldShowText;//是否需要顯示省份名
public MapView(Context context) {
super(context);
init();
}
public MapView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MapView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
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) {
float mapWidth = totalRect.width();
scale = width / mapWidth;
}
setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float currentScaleCount = 0;//當前縮放系數
float currentTranslateX = 0;//當前x平移距離
float currentTranslateY = 0;//當前y平移距離
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//單點觸控
startPoint.set(event.getX(), event.getY());
mode = DRAG;
actionClick = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
//多點觸控
Log.e("多點觸控", "多點觸控");
oriDis = distance(event);
if (oriDis > 10) {
midPoint = midPoint(event);
mode = ZOOM;
}
actionClick = false;
break;
case MotionEvent.ACTION_MOVE:
//滑動
Log.e("mode", mode + " ");
if (mode == DRAG) {
//單指拖動
if (Math.abs(x - startPoint.x) > 10 || Math.abs(y - startPoint.y) > 10) {
currentTranslateX = translateX + x - startPoint.x;
currentTranslateY = translateY + y - startPoint.y;
translateX = currentTranslateX;
translateY = currentTranslateY;
startPoint.set(x, y);
actionClick = false;
invalidate();
}
} else if (mode == ZOOM) {
//兩指縮放
float newDist = distance(event);//當前兩指距離
if (Math.abs(newDist - oriDis) > 10) {
float scaleInner = newDist / oriDis;
currentScaleCount = scale + (scaleInner - 1);
if (currentScaleCount < 1) {
scale = 1;
} else {
scale = currentScaleCount;
}
oriDis = newDist;
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
//單點觸控
mode = NONE;
if (actionClick) {
handleTouch(x / scale - translateX, y / scale - translateY);
}
break;
case MotionEvent.ACTION_POINTER_UP:
//多點觸控
mode = NONE;
break;
}
return true;
}
private void handleTouch(float x, float y) {
shouldShowText = false;
if (itemList == null) {
return;
}
ProvinceItem selectItem = null;
for (ProvinceItem provinceItem : itemList) {
if (provinceItem.isTouch(x, y)) {
selectItem = provinceItem;
provinceItem.setClickPoint(new PointF(x, y));
shouldShowText = true;
}
}
if (selectItem != null) {
select = selectItem;
postInvalidate();
}
}
private Thread loadThread = new Thread() {
@Override
public void run() {
InputStream inputStream = getResources().openRawResource(R.raw.pakistan);
//獲取DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//從DocumentBuilderFactory獲取DocumentBuilder實體
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
//決議輸入流 獲取Document實體
Document document = builder.parse(inputStream);
Element rootElement = document.getDocumentElement();
//先找到Path
NodeList path = rootElement.getElementsByTagName("path");
float left = -1;
float right = -1;
float top = -1;
float bottom = -1;
List<ProvinceItem> list = new ArrayList<>();
for (int i = 0; i < path.getLength(); i++) {
Element element = (Element) path.item(i);
String pathData = element.getAttribute("d");
String name = element.getAttribute("title");
//將pathData轉成Path
@SuppressLint("RestrictedApi") Path path1 = PathParser.createPathFromPathData(pathData);
ProvinceItem provinceItem = new ProvinceItem(path1);
provinceItem.setName(name);
provinceItem.setDrawColor(colorArray[i % 4]);
RectF rectF = new RectF();
path1.computeBounds(rectF, true);
left = left == -1 ? rectF.left : Math.min(left, rectF.left);
right = right == -1 ? rectF.right : Math.max(right, rectF.right);
top = top == -1 ? rectF.top : Math.min(top, rectF.top);
bottom = bottom == -1 ? rectF.bottom : Math.max(bottom, rectF.bottom);
list.add(provinceItem);
}
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 (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (itemList != null && itemList.size() > 0) {
canvas.save();
canvas.scale(scale, scale);
canvas.translate(translateX, translateY);
for (ProvinceItem provinceItem : itemList) {
if (provinceItem != select) {
provinceItem.drawItem(canvas, paint, false);
} else {
provinceItem.drawItem(canvas, paint, true);
}
}
if (shouldShowText) {
//繪制文本
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(40);
canvas.drawText(select.getName(), select.getClickPoint().x, select.getClickPoint().y, paint);
canvas.restore();
}
}
}
/**
* 計算兩個手指頭之間的中心點的位置
* x = (x1+x2)/2;
* y = (y1+y2)/2;
*
* @param event 觸摸事件
* @return 回傳中心點的坐標
*/
private PointF midPoint(MotionEvent event) {
float x = (event.getX(0) + event.getX(1)) / 2;
float y = (event.getY(0) + event.getY(1)) / 2;
return new PointF(x, y);
}
/**
* 計算兩個手指間的距離
*
* @param event 觸摸事件
* @return 放回兩個手指之間的距離
*/
private float distance(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);//兩點間距離公式
}
}
最后參考即可:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<pers.owen.svgmap.MapView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
本文完,Demo下載地址,
About
- 阿里巴巴矢量圖示庫
UI系列文章一覽
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/267149.html
標籤:其他
