Android 绘图学习
1贝塞尔曲线简介
贝塞尔曲线被发明用来进行汽车的主体设计(流线型),具体就是通过起始点和终点,以及若干控制点,通过调整控制点形成的曲线就叫做贝塞尔曲线,很多复杂一点的特效都需要贝塞尔曲线来实现,我们用的比较多的是二维和三维贝塞尔曲线。
应用:抛物线,水波纹,平滑曲线。
具体数学推导公式我也看不懂,只能给大家贴两个网上大神制作的贝塞尔曲线生成过程动画。这里列出的公式后面会用到。
二维贝塞尔曲线公式:
其中P0是开始点,P1是控制点,P2是结束点。
二维贝塞尔曲线生成示意图:
三维贝塞尔曲线公式:
其中P0是开始点,P1,P2是控制点,P3是结束点。
三维贝塞尔曲线示意图:
贝塞尔曲线在线调试器:http://cubic-bezier.com/#.16,.67,.79,.41 http://xuanfengge.com/easeing/ceaser/
2 Android中 quadTo ,rQuadTo 二阶贝塞尔曲线
/**
* Add a quadratic bezier from the last point, approaching control point
* (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
* this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the control point on a quadratic curve
* @param y1 The y-coordinate of the control point on a quadratic curve
* @param x2 The x-coordinate of the end point on a quadratic curve
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) ;
/**
* Same as quadTo, but the coordinates are considered relative to the last
* point on this contour. If there is no previous point, then a moveTo(0,0)
* is inserted automatically.
**/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
isSimplePath = false;
nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
参数讲解:
x1,y1:控制点坐标
x2,y2:终点坐标
rQuadTo简介:
rQuadTo在前一个点的基础上相对变换,例如:
// mPath.moveTo(200,700);
// mPath.quadTo(300,300,900,700);
mPath.rQuadTo(100,-400,700,0);
上面quadTo和rQuadTo是相等的,相当于从(200,700)利用rQuadTo中的值进行变换:
200+100 = 300 ,700-400 = 300,200+700=900,700+0 = 700;
函数cubicTo、rCubicTo是三阶贝赛尔曲线,暂时不讲解
简单示例:
public class ViewDemo16 extends View {
private int eventX,eventY;
private int startX,startY;
private int endX,endY;
private Paint paint;
private int screenW;
private int screenH;
public ViewDemo16(Context context) {
this(context,null);
}
public ViewDemo16(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
screenW = w;
screenH = h;
startX = screenW/2-400;
startY = screenH/2;
endX = screenW/2 + 400;
endY = screenH/2;
eventX = screenW/2;
eventY = screenH/2 - 400;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLUE);
canvas.drawCircle(startX,startY,10,paint);
canvas.drawCircle(endX,endY,10,paint);
canvas.drawCircle(eventX,eventY,10,paint);
paint.setStrokeWidth(10);
canvas.drawLine(startX,screenH/2,eventX,eventY,paint);
canvas.drawLine(endX,screenH/2,eventX,eventY,paint);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
path.moveTo(startX,startY);
path.quadTo(eventX,eventY,endX,endY);
canvas.drawPath(path,paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
eventX = (int) event.getX();
eventY = (int) event.getY();
invalidate();
break;
}
return true;
}
}
3 贝塞尔曲线的简单应用
平滑曲线
直接使用lineto函数:
public class ViewDemo17 extends View {
private Paint paint;
private Path mPath;
public ViewDemo17(Context context) {
this(context,null);
}
public ViewDemo17(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(15);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
mPath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath,paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN: {
mPath.reset();
mPath.moveTo(event.getX(), event.getY());
return true;
}
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(), event.getY());
postInvalidate();
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
可以看到直接利用lineto,会有很多锐利的转折,不平滑;
利用二维贝塞尔曲线平滑绘制
如何平滑拖动鼠标绘制的点,如果有(A,B,C)三个点,如何平滑的画出曲线,可以取A,B的中点为起始点,B,C的中点为结束点,B点作为控制点,就可以绘制出和ABC弯曲度方向一样的曲线。
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class ViewDemo17 extends View {
private Paint paint;
private Path mPath;
private float lastX;
private float lastY;
public ViewDemo17(Context context) {
this(context,null);
}
public ViewDemo17(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(15);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
mPath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath,paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN: {
mPath.reset();
lastX = event.getX();
lastY = event.getY();
mPath.moveTo(event.getX(), event.getY());
return true;
}
case MotionEvent.ACTION_MOVE:
float endx = (event.getX() +lastX)/2;
float endY = (event.getY() + lastY)/2;
mPath.quadTo(lastX,lastY,endx,endY);
lastY = event.getY();
lastX = event.getX();
postInvalidate();
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
利用贝塞尔曲线实现类似抛物线操作:
二阶贝塞尔曲线公式:
通过变化t,可以得到贝塞尔曲线的坐标,
public class ViewDemo18 extends View {
private Paint paint;
private float startX = 100;
private float startY = 100;
private float endX = 1200;
private float endY = 1990;
private float controlX = 900;
private float controlY = 600;
private float location;
private Bitmap mBitmap;
private Path mPath;
public ViewDemo18(Context context) {
this(context,null);
}
public ViewDemo18(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(15);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.jinbi);
mPath = new Path();
mPath.moveTo(startX,startY);
mPath.quadTo(controlX,controlY,endX,endY);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
location = (float) animation.getAnimatedValue();
postInvalidate();
}
});
valueAnimator.start();
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = (int) (startX * Math.pow((1 - location), 2) + 2 * controlX * location * (1 - location) + endX * Math.pow(location, 2));
int y = (int) (startY * Math.pow((1 - location), 2) + 2 * controlY * location * (1 - location) + endY * Math.pow(location, 2));
canvas.drawBitmap(mBitmap,x,y,paint);
canvas.drawPath(mPath,paint);
}
}
android绘图之Paint(1)
android绘图之Canvas基础(2)
Android绘图之Path(3)
Android绘图之drawText绘制文本相关(4)
Android绘图之Canvas概念理解(5)
Android绘图之Canvas变换(6)
Android绘图之Canvas状态保存和恢复(7)
Android绘图之PathEffect (8)
Android绘图之LinearGradient线性渐变(9)
Android绘图之SweepGradient(10)
Android绘图之RadialGradient 放射渐变(11)
Android绘制之BitmapShader(12)
Android绘图之ComposeShader,PorterDuff.mode及Xfermode(13)
Android绘图之drawText,getTextBounds,measureText,FontMetrics,基线(14)
Android绘图之贝塞尔曲线简介(15)
Android绘图之PathMeasure(16)
Android 动态修改渐变 GradientDrawable