需求:做小型地图的一个显示功能

实现思路:自定义view解析对应的svg文件,在利用canvas的画笔画出相应的地图线条。各个线条围成的区域可以表示成各个元素比如树,街道,路灯,汽车等等一切事物。然后通过view的点击事件可以给自定义控件添加点击,长按,放大缩小等等操作。

需要美工给一个svg格式的图片。

//当然这里并不止一个path路径,这里的path路径就是地图的线条

android:width="3000dp"

android:height="3000dp"

android:viewportWidth="3000"

android:viewportHeight="3000">

android:dataName="CCCC"

android:cabinId="9a43312887844fa3a2815dfac018f6ee"

android:pathData="M7,2297H157v60H7v-60Z"

android:strokeWidth="8.333"

android:fillType="evenOdd"

android:strokeColor="#000"/>

path标签内可存放一切你想要存放的信息,可以是表示一棵树,一栋房子都随意,只需要按照相应的格式解析出来存放到对象内就行了。

//控件自适应请重写onMeasure方法,我这里是直接全屏了。

public class MapView extends View {

private Paint paint;

private Context mContext;

private int[] colors = new int[]{Color.BLUE,Color.CYAN,Color.YELLOW,Color.GREEN};

private List pathItemList = new ArrayList<>();

GestureDetector gestureDetector;

ScaleGestureDetector scaleGestureDetector;

//双指操作下的中心位置

float focusX = 0,focusY = 0;

float posX = 0, posY = 0;

int viewWidth, viewHeight;

//是否初始化了

boolean hasInitViewSize;

float widthScale, heightScale;

float scaleFactor = 1.0f;

private int imgWidth = 3000,imgHeight=3000;

private MapPathItem selectPathItem;

//地图最大的矩阵

private RectF mapRectF;

private List users;

private List usersLocationInfo;

private Bitmap userLocationIcon;

private itemClickListener itemClickListener;

public MapView(Context context) {

super(context);

}

public MapView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

init(context);

}

private void init(Context context) {

this.mContext = context;

this.paint = new Paint();

users = new ArrayList<>();

usersLocationInfo = new ArrayList<>();

userLocationIcon = BitmapFactory.decodeResource(getResources(),R.drawable.avtar_icon);

gestureDetector = new GestureDetector(context, new MySimpleOnGestureDetector());

scaleGestureDetector = new ScaleGestureDetector(context, new MySimpleScaleOnGestureDetector());

thread.start();

}

//双指缩放

class MySimpleScaleOnGestureDetector extends ScaleGestureDetector.SimpleOnScaleGestureListener {

@Override

public boolean onScale(ScaleGestureDetector detector) {

scaleFactor *= detector.getScaleFactor();

scaleFactor = scaleFactor < 1 ? (float) 1 : scaleFactor > 3 ? 3 : scaleFactor;

focusX = detector.getFocusX();

focusY = detector.getFocusY();

invalidate();

return true;

}

}

//单指移动

class MySimpleOnGestureDetector extends GestureDetector.SimpleOnGestureListener {

@Override

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

posX -= distanceX;

posY -= distanceY;

invalidate();

return true;

}

@Override

public boolean onSingleTapConfirmed(MotionEvent e) {

//点击区域是舱段区域才触发listener

for (MapPathItem item:pathItemList){

if(item.isTouch(e.getX()-posX,e.getY()-posY,scaleFactor)){

onTouch(e.getX(),e.getY());

}

}

return super.onSingleTapConfirmed(e);

}

}

public void initSize() {

viewWidth = getWidth();

viewHeight = getHeight();

if (viewWidth < 0 && viewHeight < 0) {

return;

}

hasInitViewSize = true;

widthScale = viewWidth / imgWidth;

heightScale = viewHeight / imgHeight;

scaleFactor = Math.min(widthScale, heightScale);

scaleFactor = 1f;

posX = viewWidth / 2 - imgWidth / 2;

posY = viewHeight / 2 - imgHeight / 2;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

scaleGestureDetector.onTouchEvent(event); //双指缩放

gestureDetector.onTouchEvent(event); //单指移动

//点击区域事件

return true;

}

@RequiresApi(api = Build.VERSION_CODES.N)

public void refreshUserLocation(List users){

usersLocationInfo.clear();

Log.e("users", String.valueOf(users.size()));

for (MapPathItem item:pathItemList){

users.forEach(userLocationInf -> {

if(item.getCabinId().equals(userLocationInf.getCabinId())){

usersLocationInfo.add(item);

}

});

}

Log.e("usersLocationInfosize", String.valueOf(usersLocationInfo.size()));

invalidate();

}

private void onTouch(float x, float y) {

for (MapPathItem item:pathItemList){

if(item.isTouch(x-posX,y-posY,scaleFactor)){

selectPathItem = item;

}

}

//从新绘制

if(selectPathItem!=null){

//设置点击事件

//ToastShow.showText(getContext(),"点击了item"+selectPathItem.getCabinId());

if(itemClickListener!=null){

itemClickListener.itemClick(selectPathItem);

}

Log.e("选中",selectPathItem.toString());

invalidate();

}

}

public interface itemClickListener{

void itemClick(MapPathItem selectPathItem);

}

private void checkBounds() { //检查边界

if (scaleFactor > widthScale) {

posX = Math.min(posX, (scaleFactor - 1) * (imgWidth / 2));

posX = Math.max(posX, viewWidth - imgHeight*scaleFactor - (scaleFactor - 1) * (imgWidth / 2));

} else {

posX = Math.max(posX, (scaleFactor - 1) * (imgWidth / 2));

posX = Math.min(posX, viewWidth - imgWidth*scaleFactor - (scaleFactor - 1) * (imgWidth / 2));

}

if (scaleFactor > heightScale) {

posY = Math.min(posY, (scaleFactor - 1) * (imgHeight / 2));

posY = Math.max(posY, viewHeight - imgHeight*scaleFactor - (scaleFactor - 1) * (imgHeight / 2));

} else {

posY = Math.max(posY, (scaleFactor - 1) * (imgHeight / 2));

posY = Math.min(posY, viewHeight - imgHeight*scaleFactor - (scaleFactor - 1) * (imgHeight / 2));

}

}

//子线程加载svg

private Thread thread = new Thread(new Runnable() {

@Override

public void run() {

InputStream inputStream = mContext.getResources().openRawResource(R.raw.cabins);

//数据流转化为path DocumentBuilderFactory dom解析工厂

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

try {

DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

Document document = documentBuilder.parse(inputStream);

//根节点

Element documentElement = document.getDocumentElement();

String attribute = documentElement.getAttribute("android:viewportWidth");

Log.e("viewportWidth",attribute);

//子节点

NodeList nodeList = documentElement.getElementsByTagName("path");

//svg的上下左右边界

float left = -1,top = -1,right = -1,bottom = -1;

//解析子节点属性

for(int i = 0;i

Element element = (Element)nodeList.item(i);

//拿到路径

String pathData = element.getAttribute("android:pathData");

String dataName = element.getAttribute("android:dataName");

String cabinId = element.getAttribute("android:cabinId");

Path path = PathParser.createPathFromPathData(pathData);

MapPathItem pathItem = new MapPathItem(path);

pathItem.setDataName(dataName);

pathItem.setCabinId(cabinId);

pathItem.setColor(Color.BLACK);

pathItemList.add(pathItem);

RectF rectF = new RectF();

path.computeBounds(rectF,true);

left = left ==-1?rectF.left:Math.min(rectF.left,left);

top = top ==-1?rectF.top:Math.min(rectF.top,top);

right = right ==-1?rectF.right:Math.min(rectF.right,right);

bottom = bottom ==-1?rectF.bottom:Math.min(rectF.bottom,bottom);

}

mapRectF = new RectF(left,top,right,bottom);

measure(getMeasuredWidth(),getMeasuredHeight());

handler.sendEmptyMessage(1);

} catch (Exception e) {

e.printStackTrace();

}

}

});

@SuppressLint("HandlerLeak")

private Handler handler = new Handler(){

@RequiresApi(api = Build.VERSION_CODES.N)

@Override

public void handleMessage(@NonNull Message msg) {

super.handleMessage(msg);

invalidate();

}

};

@RequiresApi(api = Build.VERSION_CODES.N)

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (!hasInitViewSize) {

initSize();

}

checkBounds();

canvas.translate(posX,posY);

//canvas.scale(scaleFactor, scaleFactor, focusX-posX, focusY-posY);

canvas.scale(scaleFactor,scaleFactor);

for (MapPathItem pathItem : pathItemList) {

pathItem.drawItem(canvas,paint,false);

}

if(selectPathItem!=null){

Log.e("sadasd","重绘");

selectPathItem.drawItem(canvas,paint,true);

}

if(usersLocationInfo.size()!= 0 ){

usersLocationInfo.forEach(user->{

user.drawUserIcon(canvas,paint,userLocationIcon);

});

}

}

public void setItemClickListener(MapView.itemClickListener itemClickListener) {

this.itemClickListener = itemClickListener;

}

}

public class MapPathItem {

//画笔路径

private Path path;

private int color;

private String dataName;

private String cabinId;

public MapPathItem(Path path) {

this.path = path;

}

public void setColor(int color) {

this.color = color;

}

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

if(isSelect){

//画边框

paint.setStrokeWidth(4);

paint.setStyle(Paint.Style.STROKE);

paint.setShadowLayer(6,0,0,Color.RED);

paint.setColor(Color.BLACK);

canvas.drawPath(path, paint);

//填充色

paint.setColor(Color.RED);

paint.setStyle(Paint.Style.FILL);

paint.setStrokeWidth(4);

}else {

//清除阴影

paint.clearShadowLayer();

//画边框

paint.setStrokeWidth(2);

paint.setStyle(Paint.Style.STROKE);

paint.setColor(Color.BLACK);

canvas.drawPath(path, paint);

//填充色

paint.setColor(Color.WHITE);

paint.setStyle(Paint.Style.FILL);

paint.setStrokeWidth(2);

}

canvas.drawPath(path, paint);

}

public void drawUserIcon(Canvas canvas, Paint paint, Bitmap b) {

RectF rectF = new RectF();

path.computeBounds(rectF,true);

Rect src=new Rect(0,0,b.getWidth(),b.getHeight());

canvas.drawBitmap(b,src,rectF,paint);

}

/**

* 判断当前区域是否选中

* @param x

* @param y

* @param scaleFactor 缩放倍数

* @return

*/

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

RectF rectF = new RectF();

path.computeBounds(rectF,true);

//区域

Region region = new Region();

region.set((int)(rectF.left*scaleFactor),(int)(rectF.top*scaleFactor),(int)(rectF.right*scaleFactor),(int)(rectF.bottom*scaleFactor));

return region.contains((int)x,(int)y);

}

public String getDataName() {

return dataName;

}

public void setDataName(String dataName) {

this.dataName = dataName;

}

public String getCabinId() {

return cabinId;

}

public void setCabinId(String cabinId) {

this.cabinId = cabinId;

}

}

android地图路径绘制,android:利用svg的path路径+canvas 开发自定义地图控件相关推荐

  1. Android 自定义组合控件小结

    Android 自定义组合控件小结 引言 接触Android UI开发的这段时间以来,对自定义组合控件有了一定的了解,为此小结一下,本文小结内容主要讨论的是如何使用Android SDK提供的布局和控 ...

  2. Android基于高德SDK的开发——自定义地图主题样式(悬浮按钮+底部弹窗)

    日常的地图使用中,平台一般只会给我们提供地图的标准样式,造成了一定程度上的审美疲劳,那么如何实现地图的自定义样式呢?本文使用Android Studio 4.1,给开发者提供了一个基于高德地图SDK进 ...

  3. android程序日历layout,Android使用GridLayout绘制自定义日历控件

    效果图 思路:就是先设置Gridlayout的行列数,然后往里面放置一定数目的自定义日历按钮控件,最后实现日历逻辑就可以了. 步骤: 第一步:自定义日历控件(初步) 第二步:实现自定义单个日期按钮控件 ...

  4. SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)

    维基百科:贝塞尔曲线 以二次贝塞尔曲线的公式为例: js函数: //p0.p1.p2三个点,其中p0为起点,p2为终点,p1为控制点 //它们的坐标用数组表示[x,y] //t的范围是0-1 func ...

  5. Android Studio自定义组合控件

    在Android的开发中,为了能够服用代码,会把有一定共有特点的控件组合在一起定义成一个自定义组合控件.  本文就详细讲述这一过程.虽然这样的View的组合有一个粒度的问题.粒度太大了无法复用,粒度太 ...

  6. android xml图片缩放,Android通过自定义ImageView控件实现图片的缩放和拖动的实现代码...

    概述:通过自定义ImageView控件,在xml布局里面调用自定的组件实现图片的缩放. /** * 自定义的ImageView控制,可对图片进行多点触控缩放和拖动 * * @author qiuwan ...

  7. Android插件化开发指南——实践之仿酷狗音乐首页(自定义ImageView控件)

    文章目录 1. 前言 2. 基础环境--实现RecyclerView的网格布局 3. 自定义ImageView 3. 后记 1. 前言 拟定实现效果部分为下图的歌单列表部分,也就是图中红线框出来的部分 ...

  8. android+高仿+日历,android高仿钉钉和小米的自定义日历控件(支持阴历和阳历,左右无限翻页viewpager)...

    收藏 0 简介 这是一个高仿钉钉和小米的日历控件,支持快速滑动,界面缓存.想要定制化UI,使用起来非常简单,就像使用ListView一样 一些特点: 可以自定义日历控件UI 支持快速滑动 支持农历和阳 ...

  9. Android 手机卫士--自定义组合控件构件布局结构

    由于设置中心条目中的布局都很类似,所以可以考虑使用自定义组合控件来简化实现 本文地址:http://www.cnblogs.com/wuyudong/p/5909043.html,转载请注明源地址. ...

最新文章

  1. 透明代理Transparent Proxy
  2. 创建一个没有边框的并添加自定义文字的UISegmentedControl
  3. 上升沿判断语句_FPGA入门系列6判断语句
  4. 【Git Bash】在window 下,设置打开gitbash快捷键、修改默认路径
  5. (三十一)java版spring cloud+spring boot+redis多租户社交电子商务平台-spring-cloud-config...
  6. 【计算机基础】存储器层次 Memory hierarchy
  7. 关于DataAccess Application block
  8. 将文件复制到ftp发生错误 请检查是否有权限_SE文件管理器2.8.6解锁完整功能版...
  9. java实心菱形_java打印出实心菱形与空心菱形
  10. 从数据库中导出数据库文档(新增了索引及表的描述信息)
  11. 问题四十一:怎么用ray tracing画任意圆柱面(generalized cylinder)
  12. Ajax局部刷新例子
  13. 使用Scipy进行函数优化
  14. CSS综合征病例,ChurgStrauss 综合征 (css) 变应性嗜酸性肉芽肿课件
  15. javascript 实现下载的几种方法
  16. 带你了解关系网络在反欺诈领域的常见应用
  17. 137、易燃固体的分级
  18. 手机内存不够用,蒲公英X1让U盘秒变私有云
  19. 《当程序员的那些狗日日子》五
  20. 微信客户聊天做到这四点,有效提高成单量

热门文章

  1. android自定义模态框,安卓开发自定义弹出框的简单方式(纯代码布局)
  2. ios 秒数转化为时间_iOS UTC秒数时间戳和日期的相互转换【原创】
  3. 吴恩达 matlab,吴恩达机器学习记录--Matlab 一些基本操作
  4. druid.io mysql 配置_druid.io 使用mysql存储metadata overlord启动出错
  5. mybatis 时间_开发工具:Mybatis.Plus.插件三种方式的逆向工程
  6. 深度学习和目标检测系列教程 18-300:关于yolo、voc格式标签转化问题
  7. 一、使用两台Centos7系统搭建Hadoop-3.1.4完全分布式集群
  8. 三、Go语言控制语句
  9. 信息抽取新SOTA!首个结构化生成式信息抽取预训练模型,一统信息抽取四大任务...
  10. 搞科研,从好好读论文开始:沈向洋带你读论文了