![](https://img.haomeiwen.com/i2916323/477523947a1a4f3b.png)
应用需求
通过触摸轨迹在屏幕上书写内容,其他功能都是围绕书写内容进行的,例如:两点拖拽,两点缩放,擦除.
画线部分代码
/**
* 在SurfaceView触摸时间OnTouchEvent()传入坐标点和Action
* 记录下坐标点生成贝塞尔曲线
* @param x 横坐标
* @param y 纵坐标
* @param action 按下&移动&抬起&多点
*/
public void setCurrent(float x, float y, int action) {
/*mPath是drawPath的容器,drawPath的作用是局部渲染的path是一小段的path,用作于书写和擦除线条的判断 */
if (!isStart()) {
super.setCurrent(x, y, action);
mPath.moveTo(x, y);
mPath.addPathPoints(new float[]{x, y});
paths.add(new SerPath());
} else {
mPath.addPathPoints(new float[]{x, y});
drawPath = new SerPath();
paths.add(drawPath);
super.setCurrent(x, y, action);
double distance = Math.sqrt(Math.pow(Math.abs(x - getLast().x), 2) + Math.pow(Math.abs(y - getLast().y), 2));
if (distance > 400) { //如果距离突变过长,判断为无效点,直接current回退到上一次纪录的last的点,并且用UP时间结束这次path draw
// super.setCurrent(getLast().x, getLast().y, MotionEvent.ACTION_UP);
return;
}
cx = getLast().x;
cy = getLast().y;
midX = (x + cx) / 2;
midY = (y + cy) / 2;
startX = getMid().x;
startY = getMid().y;
getMid().x = midX;
getMid().y = midY;
drawPath.moveTo(startX, startY);
double s = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2));
if (action == MotionEvent.ACTION_UP) {
if (s > 5) {
drawPath.lineTo(x, y);
mPath.lineTo(x, y);
}
} else {
if (s < 200) {
if (s < 30) {//1.10 //2.12 //3.15 //在一些性能较差的触摸屏会出现线条飞线的问题
drawPath.cubicTo(cx, cy, midX, midY, x, y);
mPath.cubicTo(cx, cy, midX, midY, x, y);
} else {
drawPath.quadTo(cx, cy, midX, midY);
mPath.quadTo(cx, cy, midX, midY);
}
} else {
drawPath.quadTo(cx, cy, midX, midY);
mPath.quadTo(cx, cy, midX, midY);
}
}
}
prevX = x;
prevY = y;
}
橡皮擦部分代码
/**
* 橡皮擦擦除线条核心
* @param eraserPath 橡皮擦轨迹
* @param pens 所有跟橡皮擦有交集的线条
*/
public boolean eraser(Path eraserPath, ArrayList<NewCurv> pens) {
Region eraserRegion = new Region();
RectF eraserRectF = new RectF();
eraserPath.computeBounds(eraserRectF, false);
eraserRegion.setPath(eraserPath, new Region((int) eraserRectF.left, (int) eraserRectF.top, (int)
eraserRectF.right, (int) eraserRectF.bottom));
Rect bounds = eraserRegion.getBounds();
RectF rectF = new RectF(bounds.left, bounds.top, bounds.right, bounds.bottom);
boolean split = false;
int size = this.mPath.pathPoints.size();
NewCurv pen = new NewCurv(2, cPaint);
PathMeasure pm = new PathMeasure();
if (size != 0) {
//size = 0
System.out.println("size != 0");
pen.cachePoints.add(mPath.pathPoints.get(0));
float[] smallSegPoint = new float[2];
if (mPath.pathPoints != null && !mPath.pathPoints.isEmpty()) {
for (int j = 1; j < mPath.pathPoints.size(); j++) {
SerPath path = new SerPath();
float pointPrev[] = mPath.pathPoints.get(j - 1);
float pointNow[] = mPath.pathPoints.get(j);
path.moveTo(pointPrev[0], pointPrev[1]);
path.lineTo(pointNow[0], pointNow[1]);
paths.add(path);
}
} else {
return false;
}
for (int i = 1; i <= size - 1; i++) {
if (i > paths.size() - 1) {
return false;
}
System.out.println("for");
float[] points = this.mPath.pathPoints.get(i); //todo 小板擦会挂掉,因为paths数组是空的
SerPath path = paths.get(i); /////////////////////////////
Region pathRegion = new Region();
RectF segRect = new RectF();
path.computeBounds(segRect, false);
//判断是否在触摸点范围的小矩形里面
/*当板擦直接覆盖住目标点**/
// RectF segRect = new RectF(pathRect.left, pathRect.top, pathRect.right, pathRect.bottom);
pathRegion.setPath(path, new Region((int) segRect.left, (int) segRect.top, (int) segRect.right, (int) segRect.bottom));
Rect rect = new Rect((int) segRect.left, (int) segRect.top, (int) segRect.right, (int) segRect.bottom);
if (PanelUtil.isRectIntersectRegion(segRect, eraserRegion) || bounds.intersect(rect) || eraserRegion.quickContains(rect)) {
pm.setPath(path, false);
split = true;
boolean cut = false;
for (int j = 0; j < pm.getLength(); j++) { //遍历细段
pm.getPosTan(j, smallSegPoint, null);
//细分到某段 找到擦除的点
if (eraserRegion.contains((int) smallSegPoint[0], (int) smallSegPoint[1]) || bounds.contains((int) smallSegPoint[0], (int) smallSegPoint[1])) { //此时得到的是线条1的终结点
if (!cut) {
cut = true;
if (pen.getPointCount() == 1 && j == 0 && i == 1) { //如果是擦除开头的话,重新开一跳新线条
pen = new NewCurv(2, cPaint);
} else {
pen.cachePoints.add(smallSegPoint); //线条1的终结点添加到目标线条cachePoints
/*终结并渲染该线条, 然后新开一条线条**/
pen.buildPathAll();
if (pen.getLength() >= 2)
pens.add(pen);
pen = new NewCurv(2, cPaint);
}
}
} else if (j >= pm.getLength() - 1) {
pen.cachePoints.add(points);
} else if ((cut || pen.getPointCount() == 0)) {//新开的第二条线,后面橡皮不相交的部分都塞进去
cut = false;
float[] copySeg = new float[2];
copySeg[0] = smallSegPoint[0];
copySeg[1] = smallSegPoint[1];
pen.cachePoints.add(copySeg); //起点是这个
}
}//for
} else if (pen.getPointCount() == 0) {
pen.cachePoints.add(points);
} else {
pen.cachePoints.add(points);
}
}
if (pen.getPointCount() > 1) {
pen.buildPathAll();
if (pen.getLength() >= 2)
pens.add(pen);
}
}
return split;
}
总得来说就是利用橡皮檫的Rect跟线条的drawpath进行判断,生成开始点过程点结束点,重新生成一条新的线条
![](https://img.haomeiwen.com/i2916323/e90c1cfe3d38b9e1.jpg)
简单写一下流程,细分的太多内容
网友评论