前言

在安卓绘图中,path是一个很常用的类,使用它可以实现基本的画线功能,但是自己用path画出来的同一条线段大小是不会改变的。如果做书写类型的软件,当然想要实现更好的逼真的书写效果,在实际书写过程中,我们的笔迹通常是带有笔锋的。因此,这篇文章主要讲解一下具体的实现思路,具体代码就不放上来了,有兴趣的可以私密我交流一下。

思路

要实现笔锋,首先要获取一个变化的值,这个值能决定画线的大小。在安卓手机中,能充当这个值的目前我能想到的有压感和滑动速度,而压感在某些安卓设备是无法获取的,因此我采用滑动速度作为这个变化值。从实际测试来看,这个值是完全可以做到仿真笔锋的。速度可以通过时间和距离计算出来,然后可以根据这个速度,计算宽度的大小,速度越快,两点间的线条宽度越小,反之则越大。线条采用贝塞尔曲线来画,可以得到更为圆滑的曲线,贝塞尔曲线可以手动画点成线,也可以直接path.quadTo(),但是path.quadTo()无法控制同一条贝塞尔曲线的宽度,因此理论上无法实现大小变化。所以可以采用画点成线的方案,控制每一点的大小,优点是笔锋比较完美,缺点是运算量较大,在一些性能不是很好的安卓设备中表现特别差。另一个方案是先用path.quadTo()画一条贝塞尔曲线,然后再利用pathMeasure.getSegment()分割path,通过设定参数,把这段贝塞尔曲线的path分割为固定长度的新path,然后给新path添加不同宽度的画笔即可。这样做的优点是效率大大提高,可以适应性能低的设备,缺点是表现效果没有画点好,如果长度没有适配好,线条会出现段落感。

画点成线笔锋实现

相关变量

lastVelocity 初始速度或上一条贝塞尔曲线速度
originalWidth 初始宽度
lastWidth 上一条贝塞尔曲线宽度
time 点的触摸时间
minWidth 最小宽度
VELOCITY_WEIGHT 权重
DST_WIDTH 宽度变化范围量
velocity 当前速度

初始速度

笔迹初始速度要根据情况的不同而设置不同的值,在首次画线的时候,初始速度为0。但是如果是笔锋线段擦除后重绘,则擦除分割出来的线段,每一段的初始速度是不一样的,第一段初始速度仍然是0,而分割出的其他笔迹线段的初始速度是上一部分线段的速度。注:上一部分线段为已被擦除的线段,速度被记录为lastVelocity

初始宽度

初始宽度就是白板设置的笔迹宽度originalWidth,用于笔锋擦除。因为笔锋擦除后分割出来的笔迹的开始宽度需要根据未分割的整段笔锋笔迹来计算,因此即使笔迹的前半部分已经被擦除,有了初始宽度和初始速度,依然可以计算出线段开始宽度,这样可以保证在擦除笔迹部分线段后其他线段还能保持不变。

上一段线段速度

二阶贝塞尔曲线是两个点加一个控制点形成的曲线,因此三个点形成一条贝塞尔曲线,而一条笔迹则由N条贝塞尔曲线形成,橡皮擦擦除实际上是擦除某一条或者N条贝塞尔曲线。因此如果某一条贝塞尔曲线前面的贝塞尔曲线被擦除了,那么它就作为新笔迹的初始线段,它需要一个初始速度,也就是前面所说的初始速度lastVelocity,实际上就是上一条贝塞尔曲线的速度。

上一段线段宽度

同上一段线段速度,也是保存的上一条贝塞尔曲线的最后宽度lastWidth。

每个点的经过时间

在onTouch触摸获取点的时候,可以把每个点的触摸时间time记录下来,则用两个点就可以计算出时间和距离,从而得出两点间的速度。

最小宽度

在会议平板上,1个像素的线条在视觉上表现不好,因此可以设置一个最小宽度,即使滑动速度非常快,线条的宽度大小也限定在这个最小宽度之上,目前设置minWidth为2。

权重

计算速度的时候,会根据上一段的速度得出一个比较合理的当前速度。如下:

velocity = (VELOCITY_WEIGHT * velocity + (1-VELOCITY_WEIGHT) * lastVelocity);

代码中的VELOCITY_WEIGHT则为当前采用的权重值,值越大宽度变化越明显,范围在0~1之间。

宽度变化范围

在现实画笔锋的情况中,不存在在很短的距离中出现大小变化巨大的线段,因此前一条的贝塞尔曲线大小不能和后一段贝塞尔曲线大小相差太大,需要设定一个范围值,超出范围值则强制限定在范围值内。如下:

newWidth = Math.min(newWidth, lastWidth + DST_WIDTH);
newWidth = Math.max(newWidth, lastWidth -DST_WIDTH);

代码中lastWidh+DST_WIDTH和lastWidth-DST_WIDTH就是宽度变化范围大小,DST_WIDTH就是相对上一条线段的宽度可变化量。

画贝塞尔曲线

由于两点之间范围不是很大,因此采用二阶贝塞尔曲线和三阶贝塞尔曲线在观感上差别不大,而二阶贝塞尔曲线画起来要比三阶贝塞尔曲线效率快很多,因此采用二阶贝塞尔曲线画线。二阶贝塞尔曲线的公式为:

B(t)=(1-t)²P0+2t(1-t)p1+t²p2, t∈[0,1]

其中p0代表坐标点第一点,p1代表控制点,p2代表坐标点第二点,t=i/steps,steps是总共需要补充的点的数量,i是当前补充到的第i个点的索引。根据这个公式就可以计算出当前需要补充的点和点的大小:

dWidth = endWidth - startWidth;
width = startWidth + tt * dWidth

截取path笔锋实现

第二方案在第一方案的基础上,去掉了手动计算贝塞尔曲线和画每一个点的大小的绘图方案。并且不再在笔迹对象中保存初始速度和原始宽度,而是保存当前线段大小。一条笔迹是由很多部分的贝塞尔曲线组成,那么只需要在第一次画笔迹抬手后,计算每一小段贝塞尔曲线的宽度,然后从组成贝塞尔曲线的三个点中,取第一个点的时间值,变更为当前贝塞尔曲线宽度值,则重绘的时候直接取这个宽度值作为新的宽度即可省略掉重复计算宽度(重绘的时候不需要再次计算,因此不需要原来的time值)。

相关变量

lastVelocity 初始速度或上一条贝塞尔曲线速度
originalWidth 初始宽度
lastWidth 上一条贝塞尔曲线宽度
time 点的触摸时间或当前贝塞尔曲线的宽度值
minWidth 最小宽度
VELOCITY_WEIGHT 权重

画贝塞尔曲线

画贝塞尔曲线直接使用path.quadTo()函数即可,不过需要使用PathMeasure的getLength()函数获取当前贝塞尔曲线长度,如果小于一个设定长度,则直接画在画布上,然后继续下一条贝塞尔曲线。如果大于设定长度,则使用PathMeasure的getSegment()函数截取设定长度的path,然后重新设定paint大小,画这条path,重复,直到截取完毕,继续下一条贝塞尔曲线。循环画path代码如下:

for (float i = 0, j = 1; i < pathLength; i += dl, j++) {pathMeasure.getSegment(i, i + dl, newPath, true);paint.setStrokeWidth(startWidth - dw * j);canvas.drawPath(newPath, paint);newPath.reset();
}

转载于:https://www.cnblogs.com/newbyblog/p/10112202.html

android画板笔锋实现相关推荐

  1. android画板需求分析,基于Android的画板的设计与实现论文.doc

    基于Android的画板的设计与实现论文.doc 职场大变样社区( . zcdby. com):下载毕业设计成品全套资料,全部50元以下 毕业设计(论文)任务书 毕业设计(论文)题目: 基于Andro ...

  2. Android画板开发(二) 橡皮擦实现

    Android画板开发(一) 基本画笔的实现 Android画板开发(二) 橡皮擦实现 Android画板开发(三) 撤销反撤销功能实现 Android画板开发(四) 添加背景和保存画板内容为图片 A ...

  3. Android画板开发(四) 添加背景和保存画板内容为图片

    Android画板开发(一) 基本画笔的实现 Android画板开发(二) 橡皮擦实现 Android画板开发(三) 撤销反撤销功能实现 Android画板开发(四) 添加背景和保存画板内容为图片 A ...

  4. Android画板开发(一) 基本画笔的实现

    Android画板开发(一) 基本画笔的实现 Android画板开发(二) 橡皮擦实现 Android画板开发(三) 撤销反撤销功能实现 Android画板开发(四) 添加背景和保存画板内容为图片 A ...

  5. android画板控件,GitHub - imaiya/PainterView: Android画板控件,可以写字画画并生成图片...

    PainterView Android画板控件,可以写字画画并生成图片 引用 最新版本号 Gradle Project.gradle allprojects { repositories { jcen ...

  6. Android画板控件,可以写字,签名,画画并生成图片

    1效果图 实现步骤 1.添加画板控件module 画板控件module下载:https://download.csdn.net/download/meixi_android/10774781 2.xm ...

  7. 学习android 画板源代码,Android实现画画板案例

    郑州app开发画画板案例.布局代码是三个button和一个imagesview下面是图片. 布局代码就不展示了.下面是java代码. package cn.xhhkj.image; import an ...

  8. android画板需求分析,Android编程实现画板功能的方法总结【附源码下载】

    本文实例讲述了Android编程实现画板功能的方法.分享给大家供大家参考,具体如下: Android实现画板主要有2种方式,一种是用自定义View实现,另一种是通过Canvas类实现.当然自定义Vie ...

  9. Android画板,橡皮擦为黑色痕迹的问题

    本人小菜鸟一枚,最近想做画板(用的疯狂Android讲义中,双缓冲技术那种),遇到一个橡皮擦为黑色痕迹的问题,网上搜索资料,基础太差,实在看不懂,于是写下自己的解决办法,帮助跟我同样的小白. 1.画线 ...

  10. android 画板(选择图片作为背景并保存)

    我看了一下网上画板的写法有很多种,这篇文章的写法就是简单便捷,个人觉得不错,大家可以参考一下, http://blog.csdn.net/qq_31530015/article/details/511 ...

最新文章

  1. OKR能带来哪些价值?
  2. bzoj2597: [Wc2007]剪刀石头布
  3. 过程计算机控制综合课程设计,自动化10本 12升《计算机控制综合课程设计》任务书...
  4. Netbackup 一次备份失败原因的总结
  5. Quartus II中通过调用IP核实现RS编解码
  6. 计算机软件登记委托开发合同,软件委托开发合同
  7. Linux C/C++ 服务器/后端开发/后台开发学习路线
  8. 【转】问答 - 挑灯看剑 的最新日记
  9. 什么是DirectX?什么是OpenGL?API的作用
  10. 电加热炉温度控制系统的研究与设计
  11. aftershokz蓝牙搜不到_硬核!小程序时怎么控制蓝牙设备的?
  12. 地铁中计算机网络专业,计算机网络技术在地铁AFC系统中的应用原稿(全文完整版)...
  13. 全国计算机等级考试证书电子,全国计算机等级考试证书效力
  14. 北大AI公开课第五课--深度学习处理器by寒武纪陈云霁
  15. 泽塔云制胜秘诀:场景化破局超融合云计算市场
  16. Eclipse---Refreshing /.org.eclipse.jdt.core.external.folders/.link0
  17. 杂谈:区块链是否值得投资
  18. java 发送客服消息,Java调用微信客服消息实现发货通知的方法详解
  19. 互联网 Java 工程师进阶知识完全扫盲
  20. java使用scanner.next方式接收键盘输入

热门文章

  1. word 代码块_Python+Excel+Word一秒制作百份合同
  2. 2022届秋招笔试题小结:图
  3. 微软sql服务器双机热备,office2014-SQL Server2014 R2 双机热备,保护你的数据
  4. 【CF-gym101889:J】Jumping frog(圆上跳----思维)
  5. NOWCODER暑期多校第四场F:Beautiful Garden(签到题)题解
  6. android实现标题栏弹框,Android:Dialog对话框、Builder、showDialog、模板方法设计模式...
  7. 深入理解机器学习中的信息熵、KL散度、交叉熵
  8. 机器人学(机构学)笔记
  9. python立方体类_python学习12类
  10. php 继承 父类使用子类,PHP父类调用子类方法实例