【关于Path的史诗级总结】画箭头、直线,监听箭头、直线的触摸事件,以及平移
没图我说个球:
1.箭头
2.折线
3.直线
以上只能看到画出来的部分,平移的部分我没移植到这个demo下面(因为遇到了一些bug,懒得搞了),
但是下面会讲到平移部分。
简介:
该文章将涉及以下知识点:
1.path的绘制与平移(直线、箭头、可弯折直线);
2.path上每个点坐标的获取;
3.使用region获取填充型path(圆、椭圆、实心矩形等)的区域,得到该region后可判断你的path是否被点击;
PS:第3点我特地强调了“填充型path”,因为线条是无法用region获取区域的,获取到的区域永远是空(含泪实测证明)
正文:
1.绘制与平移
在主类中重写onTouchEvent方法,根据flag的值判断画哪种类型的view,然后调用该view的setPath方法进行绘制
@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mLastX = (int) event.getRawX();mLastY = (int) event.getRawY();break;case MotionEvent.ACTION_UP:mX = (int) event.getX();mY = (int) event.getY();rawX = (int) event.getRawX();rawY = (int) event.getRawY();if(!isEnd){switch (flag){case FLAG_DRAW_ARROW:arrowView.setSetTouchArea(true);break;case FLAG_DRAW_LINE:customLine.setSetTouchArea(true);break;case FLAG_DRAW_BENDABLE_LINE://do nothingbreak;}flag = FLAG_DRAW_NOTHING;isEnd = true;}break;case MotionEvent.ACTION_MOVE:mMoveX = (int) event.getRawX();mMoveY = (int) event.getRawY();if(!isEnd){switch (flag){case FLAG_DRAW_ARROW:arrowView.clear();arrowView.setPath(mLastX,mLastY,mMoveX,mMoveY);break;case FLAG_DRAW_LINE:customLine.setPath(mLastX,mLastY,mMoveX,mMoveY);break;case FLAG_DRAW_BENDABLE_LINE:bendableLine.setPath(mLastX,mLastY,mMoveX,mMoveY);break;}}break;}return true;}
直线的setPath方法以及onDraw实现:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawPath(mPath, mPaint);}public void setPath(float startX, float startY, float endX, float endY) {mPath.reset();mPath.moveTo(startX, startY);mPath.lineTo(endX, endY);invalidate();}
箭头的:
@Overrideprotected void onDraw(Canvas canvas) {if(hasOffset){canvas.drawPath(path,paint);canvas.drawPath(arrowPath,arrowPaint);return;}setArrowPath();canvas.drawPath(path, paint);canvas.drawPath(arrowPath, arrowPaint);}/*** 画箭头*/public void setArrowPath() {double H = 18; // 箭头高度double L = 13.5; // 底边的一半double angle = Math.atan(L / H); // 箭头角度double arrowLength = Math.sqrt(L * L + H * H); // 箭头的长度//箭头就是个三角形,我们已经有一个点了,根据箭头的角度和长度,确定另外2个点的位置double[] point1 = rotateVec(endX - startX, endY - startY, angle, arrowLength);double[] point2 = rotateVec(endX - startX, endY - startY, -angle, arrowLength);double point1_x = endX - point1[0];double point1_y = endY - point1[1];double point2_x = endX - point2[0];double point2_y = endY - point2[1];int x3 = (int) point1_x;int y3 = (int) point1_y;int x4 = (int) point2_x;int y4 = (int) point2_y;// 画线arrowPath.moveTo(endX, endY);arrowPath.lineTo(x3, y3);arrowPath.lineTo(x4, y4);arrowPath.close();}// 计算/*** @param diffX X的差值* @param diffY Y的差值* @param angle 箭头的角度(箭头三角形的线与直线的角度)* @param arrowLength 箭头的长度*/public double[] rotateVec(float diffX, float diffY, double angle, double arrowLength) {double arr[] = new double[2];// 下面的是公式,得出的是以滑动出的线段末点为中心点旋转angle角度后,线段起点的坐标,这个旋转后的线段也就是“变长了的箭头的三角形的一条边”//推导见注释1double x = diffX * Math.cos(angle) - diffY * Math.sin(angle);double y = diffX * Math.sin(angle) + diffY * Math.cos(angle);double d = Math.sqrt(x * x + y * y);//根据相似三角形,得出真正的箭头三角形顶点坐标,这里见注释2x = x / d * arrowLength;y = y / d * arrowLength;arr[0] = x;arr[1] = y;return arr;}public void setPath(float startX, float startY, float endX, float endY) {path.moveTo(startX,startY);path.lineTo(endX,endY);this.startX = startX;this.startY = startY;this.endX = endX;this.endY = endY;invalidate();}public void clear() {path.reset();arrowPath.reset();}
折线的:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawPaths(canvas);}public void setPath(float startX, float startY, final float endX, final float endY) {if (pathList.size() <= 0) {Path path = new Path();path.moveTo(startX, startY);path.lineTo(endX, endY);lastStartX = startX;lastStartY = startY;lastEndX = endX;lastEndY = endY;pathList.add(path);currentPath = path;} else {LogUtil.d(TAG+" lastEndx = "+lastEndX+" , lastEndY = "+lastEndY+" , endX = "+endX+" , endY = "+endY);if (Math.abs(lastEndX-endX) <=2 && Math.abs(lastEndY-endY) <= 2) {if(isTimerRunning){return;}//开启悬停计时if (mTimer == null) {mTimer = new Timer();}mTimer.schedule(new TimerTask() {@Overridepublic void run() {LogUtil.d(TAG+" do timerTask");Path path = new Path();path.moveTo(lastEndX, lastEndY);path.lineTo(endX, endY);lastStartX = lastEndX;lastStartY = lastEndY;lastEndX = endX;lastEndY = endY;pathList.add(path);currentPath = path;currentPathIndex++;LogUtil.d(TAG + " 生成新直线 : size = " + pathList.size() + " , currentIndex = " + currentPathIndex+" end timer");mHandler.sendEmptyMessage(0);isTimerRunning = false;}}, STANDBY_SECONDS);isTimerRunning = true;LogUtil.d(TAG+" run timer");} else {currentPath.reset();currentPath.moveTo(lastStartX, lastStartY);currentPath.lineTo(endX, endY);LogUtil.d(TAG + " 更新直线 line to " + endX + ", " + endY);lastEndX = endX;lastEndY = endY;pathList.set(currentPathIndex, currentPath);}}invalidate();}
平移:
平移用到的是path自带方法offset(dx,dy),参数就是x轴与y轴的偏移量。
@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (isContains(event.getRawX(),event.getRawY())) {LogUtil.i(TAG + " touch down id = " + identity);if (onLineTouchListener != null) {onLineTouchListener.onLineTouch(this);}lastX = (int) event.getRawX();lastY = (int) event.getRawY();canMove = true;}else{canMove = false;return false;}break;case MotionEvent.ACTION_MOVE:if(canMove){int tempRawX = (int) event.getRawX();int tempRawY = (int) event.getRawY();int dx = tempRawX - lastX;int dy = tempRawY - lastY;lastX = tempRawX;lastY = tempRawY;dxTemp += dx;dyTemp += dy;dxTotal += dx;dyTotal += dy;
// this.setTranslationX(dxTotal);
// this.setTranslationY(dyTotal);mPath.offset(dx,dy);invalidate();}break;case MotionEvent.ACTION_UP:if(dxTemp != 0 || dyTemp != 0){setSetTouchArea(true);dxTemp = 0;dyTemp = 0;}break;}return super.onTouchEvent(event);}
2.path上点坐标的获取
这里我用直线举个栗子,其他path的获取都是一样的方式。
先讲讲原理:
这里用到一个官方提供的类,叫PathMeasure,通过将它与path绑定,能够获取到path的长度,以及path上距离起点distance的点的坐标,然后我们可以写一个循环,获取path上所有点的坐标。
//用list存放path上的点坐标private List<float[]> pathPosList = new ArrayList<>;//绑定pathmPathMeasure.setPath(mPath, false);/*这里我每次循环完不是i++,而是i+=10,因为如果用i++会得到上千个点坐标,我不需要那么高的精度,这里根据自己需求调整*/for (int i = 0; i < mPathMeasure.getLength(); i+=10) {float[] pos = new float[2];float[] tan = new float[2];mPathMeasure.getPosTan(i, pos, tan);pathPosList.add(pos);}
3.使用Region获取path所占用的区域(对线条无用)
private void getPathRegion(){Region globalRegion = new Region(0,0,ScreenUtil.screenWidth,ScreenUtil.screenHeight);Region mPathRegion = new Region();mPathRegion.setPath(mPath,globalRegion);}
代码很简单,解释下原理,region.setPath方法,获取的是传入的path与传入的region的交集。
总结:
用path基本想画啥都能画出来,尤其是典型的几何图形,官方都封装好了相应的方法。如果想要做path画出来的非规则图形的点击,填充图形用Region,线条用点坐标来判断。
源码下载地址:https://download.csdn.net/download/yonghuming_jesse/11164357
以上。
如果有我说的不够详细,你没看懂的地方,请在下方留言,博主会尽快回复。
【关于Path的史诗级总结】画箭头、直线,监听箭头、直线的触摸事件,以及平移相关推荐
- java 接口文件夹_Java NIO.2 使用Path接口来监听文件、文件夹变化
Java7对NIO进行了大的改进,新增了许多功能: •对文件系统的访问提供了全面的支持 •提供了基于异步Channel的IO 这些新增的IO功能简称为 NIO.2,依然在java.nio包下. 早期的 ...
- wps ppt画直线总是有箭头,如何还原成只画直线
双击带箭头的直线 选直线 在设置为直线的地方右键
- 流程图虚线箭头的意思_Ai篇NO.3: 怎样画出不死板的箭头?
再次感谢大家的关注!已经有260位小伙伴关注我的专栏啦!还有一些小伙伴提出合作,建群,分享素材等要求,我觉得都很好,我会认真考虑的! 今天我们来谈谈Ai科研示意图中必不可少的元素--箭头. 如果我们想 ...
- cad怎么画坐标系箭头_cad怎么插入箭头?cad插入箭头方法
在cad制图中,我们经常会对图形进标注和注明,这里就需要一些箭头进行指向和声明.而在cad中插入箭头的方法并不是那么简单,不是直接插入就可以的.这里就给大家讲解在cad中插入各种箭头的方法,cad箭头 ...
- [html] 你能否画出一个0.5px的直线?
[html] 你能否画出一个0.5px的直线? 通过scale(0.5)来实现 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌谣一起通关 ...
- 用java的事件监听机制实现一个简单的画板应用:通过选择图形按钮和颜色按钮来画出自己想画的图形:直线、空心矩形、圆形、折线、多边形、圆角矩形、弧线、曲线、喷枪
今天做一个简单的画板,完整代码附在文章末尾处. - 功能:通过选择图形按钮和颜色按钮来画出自己想画的图形. - 界面展示: - 思路: 1.做一个可视化界面:创建JFrame对象,并设置Title.S ...
- svg实现直线带左箭头
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8" ...
- 【可视化】python-matplotlib画出普通箭头和带注释箭头
画出带注释的箭头: # 注释箭头 bbox_props = dict(boxstyle="rarrow", fc=(0.8, 0.9, 0.9), ec="b" ...
- DDA画线算法+代码详解-直线扫描算法之一
#DDA画线算法+代码详解-直线扫描算法之一 本文目录结构如下 1.直线扫描算法简介 2.DDA直线扫描算法 2.1 公式推理 1.求斜率K: 2.当|K| <= 1 时 3.当|K| > ...
最新文章
- CornerNet的配置、训练与测试
- SAP IDoc E1EDP04 Z8 数据错误之对策
- excel 某个单元格不是等于空值_excel 单元格为空与不存在
- 图解Win7下set命令使用
- python 在python的class中的,self到底是什么?
- 操作系统P,V(wait,signal原语)操作讲解,以及两个例题(答案仅供参考)
- memcpy和memmove的区别
- scala中处理json数据
- shopify二次开发教程_详细教程:如何将Shopify的Storefront API与React和Redux结合使用...
- 超级楼梯(HDU-2040)
- MySQL中的翻页优化和延迟缓存
- 关于TransactionScope出错:“与基础事务管理器的通信失败”的解决方法
- 哨兵系列卫星_传感器|英国Teledyne e2v公司为“哥白尼哨兵”卫星任务提供CO2监测传感器...
- stm32与计算机串口通信,STM32串口通信协议
- 微信公众号数据2019_微信公众号精准数据对比 让公众号运营更顺利
- 你画我猜---websocket
- ARP报文目的MAC为什么不是广播地址?
- VBA-自动筛选符合条件的数据
- 基于python的opencv图像处理对交通路口的红绿灯进行颜色检测,无人汽车驾驶第一步!
- 香港虚拟主机空间哪个好?
热门文章
- 破解计算机密码的各种方法
- day9.初始函数练习题
- Atitit s2018.5 s5 doc list on com pc.docx v2
- 被创新工场、君联和华熙集团先后翻牌子,因为它把古风二次元玩得特别溜!
- 给出市政工程研究生面试十个简述题并给出答案
- Excel导出XML和CS脚本
- 泰拉瑞亚怎么样修改服务器时间,泰拉瑞亚如何修改时间 安卓版时间修改教程...
- BAT脚本学习篇——基本语法
- [书籍精读]《JavaScript设计模式与开发实践》精读笔记分享
- 互联网大厂的微服务架构系统应对超大流量解决方案