android画板笔锋实现
前言
在安卓绘图中,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();
}
android画板笔锋实现的更多相关文章
- Android 实现图片画画板
本文主要讲述了Android 实现图片画画板 设计项目布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk ...
- android 图片画画板
canvas.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns: ...
- Android(java)学习笔记238:多媒体之图片画画板案例
1.首先我们编写布局文件activity_main.xml如下: <RelativeLayout xmlns:android="http://schemas.android.com/a ...
- Android利用canvas画画板
首先新建一个项目工程,建立文件,如下图所示
- Android绘画板(普通绘画模式和缩放平移绘画模式)
ScaleSketchPadDemo 项目地址: demo apk体验下载 demo2 apk体验下载 用法: 进入项目根目录:https://github.com/ShaunSheep/ScaleS ...
- Android 开源可缩放平移的绘画板
ScaleSketchPadDemo 此项目包含两个模块 app1 为普通绘画板 app2 为可所发的绘画板 方便各位Android 开发者理解和使用 用法: 进入项目根目录:https://gith ...
- Android简单开发的画画板
Android开发画画板要考虑得几个问题如下: 1 屏幕画板.画笔如何绘制问题 2 用户手指触摸屏幕画板监听事件,以及对应的几种状态处理问题 3 保存图片到SD卡,以及在系统相册打开时自动加载刚才的 ...
- Android简易实战教程--第二十四话《画画板》
今天完成一个画画板. 首先来个布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android ...
- 【Android】21.1 画板资源
分类:C#.Android.VS2015: 创建日期:2016-03-19 一.简介 画板资源(Drawable Resources)是用XML描述/Resources/drawable中的2D图形文 ...
随机推荐
- Maven内置属性,pom属性
内置属性(Maven预定义,用户可以直接使用) ${basedir}表示项目根目录,即包含pom.xml文件的目录; ${version}表示项目版本; ${project.basedir}同${ba ...
- 持续集成 TeamCity 的配置与使用
环境:实现自动编译与自动化测试,发布到远程服务器,环境 VS2015 +WIN2008R2 什么是TeamCity TeamCity是由Jetbrains开发的一款功能强大的持续集成(Continue ...
- Chapter 3 Phenomenon——19
His unfriendliness intimidated me. 他的不友好恐吓到了我. My words came out with less severity than I'd intende ...
- 轻量级web富文本框——wangEditor使用手册(2)——扩展一个“缩进”功能 demo
最新版wangEditor: 配置说明:http://www.wangeditor.com/doc.html demo演示:http://www.wangeditor.com/wangEditor/d ...
- C++ 运算符的重载
#include <iostream> #include <stdio.h> using namespace std; class Complex //复数类 { public ...
- C++ 各种继承
博客园首页博问闪存新随笔联系订阅 管理随笔- 文章- 评论- C++继承:公有,私有,保护 公有继承(public).私有继承(private).保护继承(protected)是常用的三种继承方式. ...
- 查看Linux 版本
如何得知自己正在使用的linux是什么版本呢,下面的几种方法将给你带来答案! 1. 查看内核版本命令: 1) [root@q1test01 ~]# cat /proc/version Linux v ...
- JVM内存初学 堆、栈、方法区
转自: http://www.open-open.com/lib/view/open1432200119489.html 这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解J ...
- 程序员必知的8大排序(一)-------直接插入排序,希尔排序(java实现)
http://blog.csdn.net/pzhtpf/article/details/7559896 程序员必知的8大排序(一)-------直接插入排序,希尔排序(java实现) 程序员必知的8大 ...
- 设置tabBar、使用第三方插件和自定义组件使用简单实例
创建小程序项目进入时填写,因需要用上第三方插件,所以要填上开发者的APPID,前往微信公众平台去注册一个账号获取APPID,在设置=>开发设置可以查看相关appid信息 简单思路 底部导航添加三 ...