SVG矢量圖打造不規(guī)則自定義控件拜英,可點擊的中國地圖

1静汤、SVG概念:

SVG是一種圖像文件格式,類似PNG居凶,JPG虫给。只不過PNG這種圖片需要圖像引擎加載,SVG則是由畫布來加載侠碧,它的英文全稱為Scalable Vector Graphics抹估,意思為可縮放的矢量圖形,可讓你設計無損失弄兜、高分辨率的Web圖形頁面药蜻,用戶可以直接使用代碼來描繪圖像;


2替饿、SVG圖像在Android中的使用

app圖標:sdk23以后语泽,app的圖標都是由svg圖像來表示

自定義控件:如不規(guī)則控件、復雜的交互控件视卢、子控件重疊判斷踱卵、圖標等,都可以使用SVG圖像實現

復雜動畫:如根據用戶滑動手勢動態(tài)顯示動畫据过,路徑動畫等

3惋砂、實現中國地圖的繪制,并且能正常點擊省份

效果展示:


中國地圖svg圖像下載地址:http://www.amcharts.com/download/


點擊第二個download按鈕即可下載所有國家svg圖像绳锅;

此下載比較費勁西饵,我分享一個網盤地址:

鏈接 https://pan.baidu.com/s/1zbtejuTYhSL2ino8soOgRw? 提取碼:mung

下載成功后將文件導入工程res/raw/china.svg

自定義view代碼:



package com.xxx.uidemo;

import android.annotation.SuppressLint;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.RectF;

import android.os.Handler;

import android.os.Looper;

import android.support.v4.graphics.PathParser;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.NodeList;

import java.io.InputStream;

import java.util.ArrayList;

import java.util.List;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

public class ChinaMapView extends View {

? ? private int[] colorArrays = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFFFFFF00};

? ? private List<ProvinceItem> provinceItems = new ArrayList<>();// 所有省份

? ? private Paint paint;

? ? private ProvinceItem selectItem;// 點擊選中的省份

? ? private RectF totalRectF;// 地圖矩形

? ? private float scale = 1.0f;// 畫布縮放系數

? ? public ChinaMapView(Context context) {

? ? ? ? this(context, null);

? ? }

? ? public ChinaMapView(Context context, AttributeSet attrs) {

? ? ? ? this(context, attrs, 0);

? ? }

? ? public ChinaMapView(Context context, AttributeSet attrs, int defStyleAttr) {

? ? ? ? super(context, attrs, defStyleAttr);

? ? ? ? init();

? ? ? ? parseSVG(context);

? ? }

? ? private void parseSVG(final Context context) {

? ? ? ? final InputStream inputStream = context.getResources().openRawResource(R.raw.china);

? ? ? ? new Thread(new Runnable() {

? ? ? ? ? ? @Override

? ? ? ? ? ? public void run() {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

? ? ? ? ? ? ? ? ? ? DocumentBuilder documentBuilder = factory.newDocumentBuilder();

? ? ? ? ? ? ? ? ? ? Document parse = documentBuilder.parse(inputStream);

? ? ? ? ? ? ? ? ? ? Element documentElement = parse.getDocumentElement();

? ? ? ? ? ? ? ? ? ? NodeList g = documentElement.getElementsByTagName("path");

? ? ? ? ? ? ? ? ? ? float left = -1;

? ? ? ? ? ? ? ? ? ? float right = -1;

? ? ? ? ? ? ? ? ? ? float top = -1;

? ? ? ? ? ? ? ? ? ? float bottom = -1;

? ? ? ? ? ? ? ? ? ? ArrayList<ProvinceItem> list = new ArrayList<>();

? ? ? ? ? ? ? ? ? ? for (int i = 0; i < g.getLength(); i++) {

? ? ? ? ? ? ? ? ? ? ? ? Element element = (Element) g.item(i);

? ? ? ? ? ? ? ? ? ? ? ? String pathData = element.getAttribute("d");

? ? ? ? ? ? ? ? ? ? ? ? @SuppressLint("RestrictedApi") Path path = PathParser.createPathFromPathData(pathData);

? ? ? ? ? ? ? ? ? ? ? ? ProvinceItem provinceItem = new ProvinceItem(path);

? ? ? ? ? ? ? ? ? ? ? ? provinceItem.setColor(colorArrays[i % 4]);

? ? ? ? ? ? ? ? ? ? ? ? // 這里循環(huán)遍歷每個省份path的邊界,并求得最左邊榨呆、右邊、頂部庸队、底部path的邊界

? ? ? ? ? ? ? ? ? ? ? ? RectF rectF = new RectF();

? ? ? ? ? ? ? ? ? ? ? ? path.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);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? provinceItems = list;// 避免priviceItems集合并發(fā)操作积蜻,先使用臨時集合,然后再重新賦值

? ? ? ? ? ? ? ? ? ? totalRectF = new RectF(left, top, right, bottom);//保存實際地圖大小

? ? ? ? ? ? ? ? ? ? // 通知刷新界面

? ? ? ? ? ? ? ? ? ? Handler handler = new Handler(Looper.getMainLooper());

? ? ? ? ? ? ? ? ? ? handler.post(new Runnable() {

? ? ? ? ? ? ? ? ? ? ? ? @Override

? ? ? ? ? ? ? ? ? ? ? ? public void run() {

? ? ? ? ? ? ? ? ? ? ? ? ? ? requestLayout();

? ? ? ? ? ? ? ? ? ? ? ? ? ? invalidate();

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? ? ? //postInvalidate();

? ? ? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }).start();

? ? }

? ? private void init() {

? ? ? ? paint = new Paint();

? ? ? ? paint.setColor(Color.BLACK);

? ? ? ? paint.setAntiAlias(true);

? ? }

? ? @Override

? ? protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

? ? ? ? super.onMeasure(widthMeasureSpec, heightMeasureSpec);

? ? ? ? // 獲取畫布原始大小

? ? ? ? int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);

? ? ? ? int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);

? ? ? ? // 計算縮放系數

? ? ? ? if (totalRectF != null) {

? ? ? ? ? ? float width = totalRectF.width();

? ? ? ? ? ? scale = measuredWidth / width;

? ? ? ? }

? ? }

? ? @Override

? ? protected void onDraw(Canvas canvas) {

? ? ? ? super.onDraw(canvas);

? ? ? ? if (provinceItems != null && provinceItems.size() > 0) {

? ? ? ? ? ? canvas.save();

? ? ? ? ? ? //按照縮放系數將畫布進行縮放

? ? ? ? ? ? canvas.scale(scale, scale);

? ? ? ? ? ? for (ProvinceItem provinceItem : provinceItems) {

? ? ? ? ? ? ? ? if (provinceItem == selectItem) {

? ? ? ? ? ? ? ? ? ? provinceItem.DrawItem(canvas, paint, true);

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? provinceItem.DrawItem(canvas, paint, false);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? @Override

? ? public boolean onTouchEvent(MotionEvent event) {

? ? ? ? handleTouch(event.getX() / scale, event.getY() / scale);// 點擊觸摸時也需要將事件位置進行縮放彻消,不然點擊事件會受影響

? ? ? ? return super.onTouchEvent(event);

? ? }

? ? private void handleTouch(float x, float y) {

? ? ? ? if (provinceItems == null) {

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? ProvinceItem select = null;

? ? ? ? for (ProvinceItem provinceItem : provinceItems) {

? ? ? ? ? ? if (provinceItem.isTouch(x, y)) {

? ? ? ? ? ? ? ? select = provinceItem;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if (select != null) {

? ? ? ? ? ? selectItem = select;

? ? ? ? ? ? postInvalidate();

? ? ? ? }

? ? }

}



package com.xxx.uidemo;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.RectF;

import android.graphics.Region;

/**

* 省份

*/

public class ProvinceItem {

? ? private Path path;

? ? private int color;

? ? public ProvinceItem(Path path) {

? ? ? ? this.path = path;

? ? }

? ? public void setColor(int color) {

? ? ? ? this.color = color;

? ? }

? ? /**

? ? * 繪制自己

? ? *

? ? * @param canvas

? ? * @param paint

? ? * @param isSelect

? ? */

? ? public void DrawItem(Canvas canvas, Paint paint, boolean isSelect) {

? ? ? ? if (isSelect) {

? ? ? ? ? ? // 繪制內部的顏色

? ? ? ? ? ? paint.clearShadowLayer();

? ? ? ? ? ? paint.setStrokeWidth(1);

? ? ? ? ? ? paint.setStyle(Paint.Style.FILL);

? ? ? ? ? ? paint.setColor(color);

? ? ? ? ? ? canvas.drawPath(path, paint);

? ? ? ? ? ? // 繪制邊界

? ? ? ? ? ? paint.setColor(0xFFD0E8F4);

? ? ? ? ? ? paint.setStyle(Paint.Style.STROKE);

? ? ? ? ? ? canvas.drawPath(path, paint);

? ? ? ? } else {

? ? ? ? ? ? // 繪制內部的顏色

? ? ? ? ? ? paint.clearShadowLayer();

? ? ? ? ? ? paint.setStrokeWidth(2);

? ? ? ? ? ? paint.setStyle(Paint.Style.FILL);

? ? ? ? ? ? paint.setColor(Color.BLACK);

? ? ? ? ? ? paint.setShadowLayer(8, 0, 0, 0xFFFFFF);

? ? ? ? ? ? canvas.drawPath(path, paint);

? ? ? ? ? ? // 繪制邊界

? ? ? ? ? ? paint.setColor(color);

? ? ? ? ? ? paint.setStyle(Paint.Style.FILL);

? ? ? ? ? ? paint.setStrokeWidth(2);

? ? ? ? ? ? canvas.drawPath(path, paint);

? ? ? ? }

? ? }

? ? /**

? ? * 按下坐標坐標是否在path軌跡和path對應的矩形軌跡的交集中

? ? *

? ? * @param x

? ? * @param y

? ? * @return

? ? */

? ? public boolean isTouch(float x, float y) {

? ? ? ? RectF rectF = new RectF();

? ? ? ? path.computeBounds(rectF, true);//計算path軌跡對應的矩形界限

? ? ? ? Region region = new Region();

? ? ? ? // 計算path對應的軌跡和path軌跡對應的矩形界限的范圍交集

? ? ? ? region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));

? ? ? ? return region.contains((int) x, (int) y);// 坐標值是否包含在此范圍中

? ? }

}



使用布局加載地圖

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

? ? xmlns:app="http://schemas.android.com/apk/res-auto"

? ? android:layout_width="match_parent"

? ? android:layout_height="match_parent">

? ? <com.xxx.uidemo.ChinaMapView

? ? ? ? android:layout_width="match_parent"

? ? ? ? android:layout_height="match_parent" />

</RelativeLayout>



里面有詳細備注竿拆,基本思路也就是先解析svg文件,然后依據解析結果畫出地圖宾尚;



關鍵步驟:

1丙笋、解析svg谢澈;

2、繪制自己

3御板、判斷點擊坐標是否屬于此path軌跡范圍內锥忿;

4、解析過程中獲取地圖實際大械±摺敬鬓;

5、計算畫布和地圖 縮放比并按比例縮放畫布笙各;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末钉答,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子杈抢,更是在濱河造成了極大的恐慌数尿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惶楼,死亡現場離奇詭異右蹦,居然都是意外死亡,警方通過查閱死者的電腦和手機鲫懒,發(fā)現死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門嫩实,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窥岩,你說我怎么就攤上這事甲献〕谒担” “怎么了勒虾?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵粗合,是天一觀的道長馋缅。 經常有香客問我辨泳,道長顾翼,這世上最難降的妖魔是什么合愈? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任颅崩,我火速辦了婚禮呻疹,結果婚禮上吃引,老公的妹妹穿的比我還像新娘。我一直安慰自己刽锤,他們只是感情好镊尺,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著并思,像睡著了一般庐氮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宋彼,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天弄砍,我揣著相機與錄音仙畦,去河邊找鬼。 笑死音婶,一個胖子當著我的面吹牛慨畸,可吹牛的內容都是我干的。 我是一名探鬼主播桃熄,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼先口,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞳收?” 一聲冷哼從身側響起碉京,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎螟深,沒想到半個月后谐宙,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡界弧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年凡蜻,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垢箕。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡划栓,死狀恐怖,靈堂內的尸體忽然破棺而出条获,到底是詐尸還是另有隱情忠荞,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布帅掘,位于F島的核電站委煤,受9級特大地震影響,放射性物質發(fā)生泄漏修档。R本人自食惡果不足惜碧绞,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吱窝。 院中可真熱鬧讥邻,春花似錦、人聲如沸院峡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撕予。三九已至鲫惶,卻和暖如春蜈首,著一層夾襖步出監(jiān)牢的瞬間实抡,已是汗流浹背欠母。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吆寨,地道東北人赏淌。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像啄清,于是被迫代替她去往敵國和親六水。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容