android地图路径绘制,android:利用svg的path路径+canvas 开发自定义地图控件
需求:做小型地图的一个显示功能
实现思路:自定义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 开发自定义地图控件相关推荐
- Android 自定义组合控件小结
Android 自定义组合控件小结 引言 接触Android UI开发的这段时间以来,对自定义组合控件有了一定的了解,为此小结一下,本文小结内容主要讨论的是如何使用Android SDK提供的布局和控 ...
- Android基于高德SDK的开发——自定义地图主题样式(悬浮按钮+底部弹窗)
日常的地图使用中,平台一般只会给我们提供地图的标准样式,造成了一定程度上的审美疲劳,那么如何实现地图的自定义样式呢?本文使用Android Studio 4.1,给开发者提供了一个基于高德地图SDK进 ...
- android程序日历layout,Android使用GridLayout绘制自定义日历控件
效果图 思路:就是先设置Gridlayout的行列数,然后往里面放置一定数目的自定义日历按钮控件,最后实现日历逻辑就可以了. 步骤: 第一步:自定义日历控件(初步) 第二步:实现自定义单个日期按钮控件 ...
- SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
维基百科:贝塞尔曲线 以二次贝塞尔曲线的公式为例: js函数: //p0.p1.p2三个点,其中p0为起点,p2为终点,p1为控制点 //它们的坐标用数组表示[x,y] //t的范围是0-1 func ...
- Android Studio自定义组合控件
在Android的开发中,为了能够服用代码,会把有一定共有特点的控件组合在一起定义成一个自定义组合控件. 本文就详细讲述这一过程.虽然这样的View的组合有一个粒度的问题.粒度太大了无法复用,粒度太 ...
- android xml图片缩放,Android通过自定义ImageView控件实现图片的缩放和拖动的实现代码...
概述:通过自定义ImageView控件,在xml布局里面调用自定的组件实现图片的缩放. /** * 自定义的ImageView控制,可对图片进行多点触控缩放和拖动 * * @author qiuwan ...
- Android插件化开发指南——实践之仿酷狗音乐首页(自定义ImageView控件)
文章目录 1. 前言 2. 基础环境--实现RecyclerView的网格布局 3. 自定义ImageView 3. 后记 1. 前言 拟定实现效果部分为下图的歌单列表部分,也就是图中红线框出来的部分 ...
- android+高仿+日历,android高仿钉钉和小米的自定义日历控件(支持阴历和阳历,左右无限翻页viewpager)...
收藏 0 简介 这是一个高仿钉钉和小米的日历控件,支持快速滑动,界面缓存.想要定制化UI,使用起来非常简单,就像使用ListView一样 一些特点: 可以自定义日历控件UI 支持快速滑动 支持农历和阳 ...
- Android 手机卫士--自定义组合控件构件布局结构
由于设置中心条目中的布局都很类似,所以可以考虑使用自定义组合控件来简化实现 本文地址:http://www.cnblogs.com/wuyudong/p/5909043.html,转载请注明源地址. ...
最新文章
- 透明代理Transparent Proxy
- 创建一个没有边框的并添加自定义文字的UISegmentedControl
- 上升沿判断语句_FPGA入门系列6判断语句
- 【Git Bash】在window 下,设置打开gitbash快捷键、修改默认路径
- (三十一)java版spring cloud+spring boot+redis多租户社交电子商务平台-spring-cloud-config...
- 【计算机基础】存储器层次 Memory hierarchy
- 关于DataAccess Application block
- 将文件复制到ftp发生错误 请检查是否有权限_SE文件管理器2.8.6解锁完整功能版...
- java实心菱形_java打印出实心菱形与空心菱形
- 从数据库中导出数据库文档(新增了索引及表的描述信息)
- 问题四十一:怎么用ray tracing画任意圆柱面(generalized cylinder)
- Ajax局部刷新例子
- 使用Scipy进行函数优化
- CSS综合征病例,ChurgStrauss 综合征 (css) 变应性嗜酸性肉芽肿课件
- javascript 实现下载的几种方法
- 带你了解关系网络在反欺诈领域的常见应用
- 137、易燃固体的分级
- 手机内存不够用,蒲公英X1让U盘秒变私有云
- 《当程序员的那些狗日日子》五
- 微信客户聊天做到这四点,有效提高成单量
热门文章
- android自定义模态框,安卓开发自定义弹出框的简单方式(纯代码布局)
- ios 秒数转化为时间_iOS UTC秒数时间戳和日期的相互转换【原创】
- 吴恩达 matlab,吴恩达机器学习记录--Matlab 一些基本操作
- druid.io mysql 配置_druid.io 使用mysql存储metadata overlord启动出错
- mybatis 时间_开发工具:Mybatis.Plus.插件三种方式的逆向工程
- 深度学习和目标检测系列教程 18-300:关于yolo、voc格式标签转化问题
- 一、使用两台Centos7系统搭建Hadoop-3.1.4完全分布式集群
- 三、Go语言控制语句
- 信息抽取新SOTA!首个结构化生成式信息抽取预训练模型,一统信息抽取四大任务...
- 搞科研,从好好读论文开始:沈向洋带你读论文了