特別說明
- 當前博客平臺賬號已廢棄薪贫,如果有使用細節(jié)問題請前往我個人博客平臺 HuRuWo的技術(shù)小站進行討論交流絮短。直接在其他平臺留言可能無法及時得到回復(fù)窘问。
文章首發(fā)于個人博客HuRuWo的技術(shù)小站,如果本文非vip用戶無法完全瀏覽或者圖片無法打開臼婆,可前往個人博客文章地址查看文章并留言討論。
本篇文章個人博客文章地址自定義View--加載SVG地圖的控件MapView,點擊鏈接直接前往恰起。
更多技術(shù)文章訪問本人博客HuRuWo的技術(shù)小站修械,包括 Electron從零開發(fā) Android 逆向 app 微信數(shù)據(jù)抓取 抖音數(shù)據(jù)抓取 閑魚數(shù)據(jù)抓取 小紅書數(shù)據(jù)抓取 其他軟件爬蟲 等技術(shù)文章
前言
最近制作一款和地圖相關(guān)的軟件,需要在地圖上點擊不同的色塊進入不同的子地圖检盼。并且需要在地圖上添加標志物肯污。
顯然當前的第三方地圖sdk無法完成,所以需要自定義View梯皿。
效果展示
思路
-- 地圖
- 解析SVG獲得地圖的path
- 自定義View繪制path
- 3.點擊事件 計算點位置所落地點
- 由標記的出每個區(qū)域中心的位置
-- 標志物
需要覆蓋在view的指定位置仇箱,有可能是圖片/View或者其他東西
MapView
解析SVG圖片
首先把SVG轉(zhuǎn)化為
通過Android SVG to VectorDrawable
轉(zhuǎn)化為android格式的文件,復(fù)制后導(dǎo)入工程东羹。
編寫工具類解析svg的path
這個方法在包android.support.v4.graphics
下面
原理則是:
取出path的點剂桥,按svg圖片規(guī)則讀取成android里面的繪圖規(guī)則連線,成為一個整的path属提。
PathParser
自定義View繪制取出來的Path
這個就很簡單了,只需要使用canvas.drawPath
核心ondraw:
canvas.save();
canvas.scale(scale, scale, 0, 0); //中心縮放
for (PathItem pathItem : pathItems) {
if ((pathItems.indexOf(pathItem) > pathItems.size()/2-1)) {
} else {
pathItemDraw(canvas, pathItem);
}
}
/**
* 文字繪制
*/
points.clear();
for (PathItem pathItem : pathItems) {
if (pathItems.indexOf(pathItem) > pathItems.size()/2-1) {
pathCenterText(canvas, pathItem);
} else {
pathBoder(canvas, pathItem);
}
}
if (!ispos) {
if (onPathDrawListener != null) {
onPathDrawListener.onPathDrawFinish(getPoints());
}
ispos = !ispos;
}
canvas.restore();
其中調(diào)用的三個繪圖方法:
public void pathItemDraw(final Canvas canvas, PathItem pathItem) {
if (pathItem.getIsSelect()) {
//首先繪制選中的背景陰影
paintMap.clearShadowLayer();
paintMap.setShadowLayer(8, 0, 0, Color.BLACK);
canvas.drawPath(pathItem.getPath(), paintMap);
//繪制具體顯示的
paintMap.clearShadowLayer();
paintMap.setColor(Color.WHITE);
canvas.drawPath(pathItem.getPath(), paintMap);
} else {
//繪制具體顯示的
paintMap.clearShadowLayer();
paintMap.setColor(Color.parseColor(pathItem.getColor()));
canvas.drawPath(pathItem.getPath(), paintMap);
}
}
public void pathCenterText(Canvas canvas, PathItem pathItem) {
PathMeasure measure = new PathMeasure(pathItem.getPath(), false);
float[] pos1 = new float[2];
measure.getPosTan(measure.getLength() / 2, pos1, null);
/**
* 計算字體的寬高
*/
Rect rect = new Rect();
paintText.getTextBounds(pathItem.getTitle(), 0, pathItem.getTitle().length(), rect);
int w = rect.width();
int h = rect.height();
canvas.drawText(pathItem.getTitle(), pos1[0] - w / 2, pos1[1] + h, paintText);
/**
* 兩點求中點
*/
float[] pos2 = new float[2];
measure.getPosTan(0, pos2, null);
float[] point = new float[2];
point[0] = (pos1[0] + pos2[0]) / 2;
point[1] = (pos1[1] + pos2[1]) / 2;
points.add(point);
}
public void pathBoder(Canvas canvas, PathItem pathItem) {
canvas.drawPath(pathItem.getPath(), paintBoder);
}
path路徑的點擊事件
1.定義點擊事件回調(diào):
public interface OnPathClickListener {
void onPathClick(PathItem p);
}
2.View點擊事件輔助工具GestureDetector
mDetector = new GestureDetector(context, this);
mDetector.setOnDoubleTapListener(this);
3.View繼承接口
包括普通點擊事件以及單雙擊事件
implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener
4.方法實現(xiàn)
@Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
Log.e("觸摸點擊", "onSingleTapConfirmed" + motionEvent.getY() + "---" + motionEvent.getY());
float x = (motionEvent.getX() - has_move_x) / scale;
float y = (motionEvent.getY() - has_move_y) / scale;
for (PathItem pathItem : pathItems) {
if (BitmapUtil.isTouch((int) x, (int) y, pathItem.getPath())) {
if (onPathClickListener != null) {
onPathClickListener.onPathClick(pathItem);
}
pathItem.setIsSelect(true);
} else {
pathItem.setIsSelect(false);
}
}
invalidate();
return false;
}
@Override
public boolean onDoubleTap(MotionEvent motionEvent) {
Log.e("觸摸點擊", "onDoubleTap" + motionEvent.getY() + "---" + motionEvent.getY());
/**
* 雙擊放大
*/
scale = scale * 1.1f;
invalidate();
return false;
}
@Override
public boolean onDoubleTapEvent(MotionEvent motionEvent) {
Log.e("觸摸點擊", "onDoubleTapEvent" + motionEvent.getY() + "---" + motionEvent.getY());
return false;
}
@Override
public boolean onDown(MotionEvent motionEvent) {
Log.e("觸摸點擊", "onDown" + motionEvent.getY() + "---" + motionEvent.getY());
return true;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
Log.e("觸摸點擊", "onShowPress" + motionEvent.getY() + "---" + motionEvent.getY());
}
/**
* 這個事件 在雙擊事件也會觸發(fā) 所以為了區(qū)分 單擊事件應(yīng)該 放在onSingleTapConfirmed 中
*
* @param motionEvent
* @return
*/
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
Log.e("觸摸點擊", "onSingleTapUp" + motionEvent.getY() + "---" + motionEvent.getY());
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
Log.e("觸摸點擊", "onLongPress" + motionEvent.getY() + "---" + motionEvent.getY());
/**
* 長按恢復(fù)
*/
has_move_x = 0;
has_move_y = 0;
move_x = 0;
move_y = 0;
scale = getMinScale();
invalidate();
}
可以看到核心的單擊事件权逗,判斷是否屬于某個path區(qū)域,并回調(diào)。
if (BitmapUtil.isTouch((int) x, (int) y, pathItem.getPath())) {
if (onPathClickListener != null) {
onPathClickListener.onPathClick(pathItem);
}
pathItem.setIsSelect(true);
} else {
pathItem.setIsSelect(false);
}
5.最后記得把view觸控事件交給GestureDetector
/**
* ---------------------手勢的處理---------------
**/
@Override
public boolean onTouchEvent(MotionEvent event) {
return mDetector.onTouchEvent(event); //把手勢相關(guān)操作返回給 手勢操控類
}
以上就是繪制地圖的方法冤议,接下繪制地圖標志物斟薇。
地圖標志物
自定義ViewGroup繪制遮蓋物
MapView獲取區(qū)域中點的位置:
寫一個getMapPoint方法,獲取各個市區(qū)中心點位置恕酸,方便獲取遮蓋物堪滨。
然后在MapView的父布局添加子View
1.定義繪圖完成,計算完成回調(diào):
private OnPathDrawListener onPathDrawListener;
public interface OnPathDrawListener {
void onPathDrawFinish(List<float[]> pos);
}
計算地圖中的市區(qū)中點
float[] pos2 = new float[2];
measure.getPosTan(0, pos2, null);
float[] point = new float[2];
point[0] = (pos1[0] + pos2[0]) / 2;
point[1] = (pos1[1] + pos2[1]) / 2;
points.add(point);
if (onPathDrawListener != null) {
onPathDrawListener.onPathDrawFinish(getPoints());
}
獲得回調(diào)蕊温,繪制遮蓋View
ztemap.setOnPathDrawListener(new ZTEMapView.OnPathDrawListener() {
@Override
public void onPathDrawFinish(List<float[]> pos) {
for (float[] point : points) {
PointView PointView = new PointView(getContext());
PointView.scrollTo((int) -point[0]+width/2, (int) -point[1]+hight/2);
addView(PointView);
}
}
});
PointView為自定義的波浪View
到此為止袱箱,整個圖繪制完成了。
View加載優(yōu)化
1.優(yōu)化地圖資源加載速度
由于每次從xml資源取得path路徑的string是非常耗時的io操作义矛。所以可以使用數(shù)據(jù)庫緩存得到的path路徑資源发笔。只需要第一次加載之后,都從數(shù)據(jù)庫獲取凉翻。
具體看demo即可了讨。
2.標志物的加載需要在地圖的加載之后,所以需要設(shè)置延遲制轰,以防界面卡頓前计。具體邏輯及時監(jiān)聽地圖的
addOnGlobalLayoutListener
3.動畫繪制的優(yōu)化