SVG文件本就是XML格式,因此可以直接進行XML解析,大家的方案都是使用工具(Android Studio 或者在線解析)將SVG轉換為Android可識別的Drawable資源文件,但這樣就沒法去根據需要實時修改了,因此只能從SVG中的path標簽入手。
- 一般SVG文件使用記事本打開后是這些內容:
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
<path fill="#1565C0" d="M32,5c7.7,0,10,10.6,10,19c0,8.1-3.1,15-8,15s-6-4.4-6-8c0-3.8,1-7.8,1-11c0-4.8-3-6-3-9S28.1,5,32,5z"/>
<path fill="#E39F00" d="M31,16.9l-1.7,1.1c4,5.8,12.1,7.8,12.4,7.9l0.5-1.9C42.1,24,34.5,22.1,31,16.9z"/>
<path fill="#FFC107" d="M41.7,16.3c-0.5-0.3-1.1-0.1-1.4,0.4c0,0.1-3.9,7.3-10.7,8.3c-0.5,0.1-0.9,0.6-0.8,1.1 c0.1,0.5,0.5,0.8,1,0.8c0,0,0.1,0,0.2,0c7.7-1.2,11.9-9.1,12.1-9.4C42.3,17.2,42.2,16.6,41.7,16.3z"/>
<path fill="#90CAF9" d="M32,4c-4.5,0-7,3.6-7,7c0,3,3,5.2,3,9c0,4.8-1,7.3-1,11c0,6.3,2.6,9,7,9c5.8,0,9-7.8,9-16 C43,14.3,40.1,4,32,4z M41,23.1C40.9,28,38.9,30,36.2,30c-6.9,0-4.2-6.8-4.2-8.9c0-5.9-3-8.6-3-11.5c0-1.6,1.1-3.6,3.9-3.6 C40,7,40.9,18.9,41,23.1z"/>
<path fill="#1565C0" d="M22,14c0,3-3,4.2-3,9c0,3.2,1,7.2,1,11c0,3.6-1.1,8-6,8s-8-6.9-8-15C6,18.6,8.3,8,16,8C19.9,8,22,11,22,14z"/>
<path fill="#E39F00" d="M17,19.9l1.7,1.1c-4,5.8-12.1,7.8-12.4,7.9L5.8,27C5.9,27,13.5,25.1,17,19.9z"/>
<path fill="#FFC107" d="M6.3,19.3c0.5-0.3,1.1-0.1,1.4,0.4c0,0.1,3.9,7.3,10.7,8.3c0.5,0.1,0.9,0.6,0.8,1.1c-0.1,0.5-0.5,0.8-1,0.8 c0,0-0.1,0-0.2,0C10.3,28.9,6.1,21,5.9,20.7C5.7,20.2,5.8,19.6,6.3,19.3z"/>
<path fill="#90CAF9" d="M5,27c0,8.2,3.2,16,9,16c4.4,0,7-2.8,7-9c0-3.6-1-6.2-1-11c0-3.8,3-6,3-9c0-3.4-2.5-7-7-7C7.9,7,5,17.3,5,27 z M15.1,9.1c2.7,0,3.9,2,3.9,3.6c0,2.9-3,5.6-3,11.5c0,2,2.7,8.9-4.2,8.9c-2.6,0-4.7-2-4.8-6.9C7.1,21.9,8,10,15.1,9.1z"/>
</svg>
- 其中path標簽的內容就是我們需要的數據,我們使用 DocumentBuilder 和 XPath 來解析此XML,從而得到path對象。
File file = new File("/storage/emulated/0/Tencent/QQfile_recv/more.svg");
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = builder.parse(file);
String xpathExpression = "http://@*"; // 選擇所有節點
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
XPathExpression expression = xpath.compile(xpathExpression);
// 得到整個xml的每個節點
NodeList svgPaths = (NodeList) expression.evaluate(document, XPathConstants.NODESET);
ArrayList<Data> pathList = new ArrayList<>();
for (int i = 0; i < svgPaths.getLength(); i++) {
// 對應path標簽中的d節點
if (svgPaths.item(i).getNodeName().equals("d")) {
Data data = new Data();
data.mPath = PathParser.createPathFromPathData(svgPaths.item(i).getTextContent());
// 此d節點前面應該是fill節點,fill節點是此路徑繪制的顏色
if (i > 0 && svgPaths.item(i - 1).getNodeName().equals("fill")) {
data.mFillColor = svgPaths.item(i - 1).getTextContent();
} else if (i > 0){
// d節點的前一個不是fill節點,可能是其他節點,此時往上查找,直到找到fill節點或者再次
// 遇到d節點(證明沒有fill節點,默認為黑色)為止
for (int j = i -1; j >= 0 ; j--) {
if (svgPaths.item(j).getNodeName().equals("fill")) {
data.mFillColor = svgPaths.item(j).getTextContent();
break;
} else if (svgPaths.item(j).getNodeName().equals("d")) {
// 如果找到了上一個path中的d標簽都沒有找到fill標簽,則代表此path沒有fill,為黑色
data.mFillColor = "#000000";
break;
}
}
}
pathList.add(data);
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (XPathExpressionException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
class Data {
String mFillColor;
Path mPath;
}
- 當然你也可以自己解析d標簽的內容,m代表移動,對應path中的moveTo,l代表畫線等具體可以查詢path的相關方法,但Android自己便可以從Drawable下的XML解析成VectorDrawable,那官方應該有相關的解析方法,全局搜索有關path的信息,果然找到了一個PathParser類。
- 關于PathParser,這個類原本是在android.support.graphics.drawable下的類,無法訪問到,因此直接拷貝出來,使用createPathFromPathData方法便能將d標簽下的數據轉換為path對象。
通過以上步驟,得到了一個SVG文件的所有path對象,然后根據pathList便可以將矢量圖繪制出來。
- 這里使用一個自定義的IamgeView展示矢量圖,在onDraw方法中將path繪制出來即可。
@Override
public void onDraw(Canvas canvas) {
canvas.save();
for (int i = 0; i < pathList.size(); i++) {
Paint paint = new Paint();
paint.setColor(Color.parseColor(pathList.get(i).mFillColor));
canvas.drawPath(pathList.get(i).mPath, paint);
}
canvas.restore();
}
- 如此便將矢量圖繪制到了自定義的IamgeView上,當然也可以直接新建Canvas便可將矢量圖按需求進行繪制。
- 也可以通過此方式將兩個或者幾個矢量圖疊加在一起,每一張SVG最終都生成一個pathList,矢量圖的疊加其實也就是path的疊加。
這里對這些path進行一些簡單的操作,比如放大,和平移,當然旋轉也可以,只要是Matrix擁有的效果均能實現
查看Path這個類,發現有個一addPath(Path src, Matrix matrix)方法,這就完美了,直接可以傳入Matrix,再也不用手動計算值了
這里,我重寫了ImageView的onTouchEvent方法,當點擊時,將SVG圖片放大1.1倍,代碼如下所示:
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
Matrix matrix = new Matrix();
matrix.postScale(1.1f,1.1f);
// matrix.postTranslate(5f,5f);
for (int i = 0; i < pathList.size(); i++) {
Path path = new Path();
path.addPath(pathList.get(i).mPath, matrix);
pathList.get(i).mPath = path;
}
break;
}
}
return true;
}
-
演示效果如下圖:
加載本地SVG圖片并放大.gif