二阶贝塞尔曲线实现绘画板效果
如左图,使用一阶贝塞尔曲线绘制,图线不光滑,会有明显折线效果;
如右图,使用二阶贝塞尔曲线,图线光滑圆润。
1. 什么是二阶贝塞尔曲线
2. 曲线函数 quadTo()
-
函数
//二阶贝济埃曲线 public void quadTo(float xl , float yl, float x2 , float y2);
-
参数
(xl , y1) 是控制点 P1 坐标,(x2,y2)是终点 P2 坐标。
起始点 P0 是通过 Path.moveTo(x,y)函数来指定的;
如果连续调用quadTo() 函数,那么前一个 quadTo() 函数的终点就是下一个 quadTo() 函数的起始点。
3. 原理分析
-
捕捉手势轨迹
在自定义控件中拦截 OnTouchEvent,根据手指的移动轨迹来绘制 Path;
-
实现方法:
最简单的方法就是直接调用 Path.lineTo() 函数把各个点连接起来。
效果如首图左侧所示,存在明显的折线效果。
4. 代码实现
4.1 自定义控件
-
在构造函数中初始化相关的参数;
public class NormalGestureTrackView extends View { private Paint mPaint; private Path mPath; //起始点坐标 private float mPreX; private float mPreY; //结束点坐标 private float mEndX; private float mEndY; public NormalGestureTrackView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.RED); mPaint.setStrokeWidth(5); mPaint.setStyle(Paint.Style.STROKE); mPath = new Path(); } @Override public boolean onTouchEvent(MotionEvent event) { …… return super.onTouchEvent(event); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); } }
4.2 重写 OnTouchEvent() 函数
- 调用 Path 的 moveTo() 和 lineTo() 函数将手势经过的点连接起来。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPreX = event.getX();
mPreY = event.getY();
mPath.moveTo(mPreX, mPreY);
return true;
case MotionEvent.ACTION_MOVE:
if (GestureTrackActivity.isLineTo) {
mEndX = event.getX();
mEndY = event.getY();
mPath.lineTo(mEndX, mEndY);
} else {
mEndX = (event.getX() + mPreX) / 2;
mEndY = (event.getY() + mPreY) / 2;
mPath.quadTo(mPreX, mPreY, mEndX, mEndY);
mPreX = event.getX();
mPreY = event.getY();
}
invalidate();
break;
default:
break;
}
return super.onTouchEvent(event);
}
-
注意:
这里我在调用该 View 的 activity 中定义了静态布尔值常量 isLineTo ,默认为 true,默认使用 lineTo() 连接,当点击 btn 时取反;
当 isLineTo 为 true 时,使用一个方法,为 false 时,使用二阶贝塞尔曲线;
-
关于 EndX 取值,请参考如图理解;
4.3 重写 onDraw() 方法
-
画出轨迹
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); }
4.4 注意
-
在 MotionEvent.ACTION_DOWN 中 返回 true;并且没有 break;表示当前控件已经消费了下按动作,之后的ACTION_MOVE 、ACTION_UP 动作也会继续传递到当前/控件中:
-
重绘控件使用的是 postlnvalidate() 函数,也可以使用 Inval idate() 函数。这两个函数都可以重绘控件,
区别是 Invalidate() 函数一定要在主线程中执行, 否则会报错
postlnvalidate() 函数可以在任何线程中执行;
5. rQuadTo() 函数
publiC void rQuadTo(float dxl , float dyl, float dx2, float dy2)
具体实现与详解,请看:小动画之"波浪动画";
声明:本文整理自《《Android自定义控件开发入门与实战》_启舰》;