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图形文 ...
随机推荐
- Parallel Gradient Boosting Decision Trees
本文转载自:链接 Highlights Three different methods for parallel gradient boosting decision trees. My algori ...
- Python:使用异常处理来判断运行的平台
try: import termios, TERMIOS 1 except ImportError: try: import msvcrt 2 except ImportError: try: fro ...
- Spring Boot + Spring Cloud 实现权限管理系统 后端篇(七):集成 Druid 数据源
数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏 ...
- Go的方法集
方法集定义了接口的接受规则. package main import "fmt" type notifier interface { notify() } type user st ...
- CNN初探
CNN初探 版权声明:本文为博主原创文章,转载请指明转载地址 http://www.cnblogs.com/fydeblog/p/7450413.html 前言 这篇博客主要讲解卷积神经网络(CNN) ...
- MySQL Replication之主从切换
在生产环境中,我们的架构很多都是一主多从.比如一个主数据库服务器M,两个从数据库服务器S1,S2同时指向主数据库服务器M.当主服务器M因为意外情况宕机,需要将其中的一个从数据库服务器(假设选择S1)切 ...
- 面试题----实现memcpy
#include <stdio.h> void *memcpy(void *memTo,const void *memFrom,size_t size) { if(memTo == NUL ...
- 编写无Java脚本的JSP页面
在上一章中总结了Web开发中应用MVC架构模式,将Servlet 用做控制器,JSP作为视图,JavaBean作为模型,实现业务流程控制,页面逻辑和业务逻辑的分离.然而,使用前面的技术实现MVC,并不 ...
- 局域网使用visual studio配合iis调试手机app
描述:开发一款手机应用程序,服务器配置在iis,当局域网中即只有路由器无外网如何设置实时调试手机应用程序? vs配合iis调试程序的两种方式? 使用vs的debug(f5)调试网站比较常见,然而当网站 ...
- Quartz框架多个trigger任务执行出现漏执行的问题分析--转
原文地址:http://blog.csdn.net/dailywater/article/details/51470779 一.问题描述 使用Quartz配置定时任务,配置了超过10个定时任务,这些定 ...