mPaint = new Paint(); //初始化
mPaint.setColor(Color.RED);// 设置颜色
mPaint.setARGB(255, 255, 255, 0); // 设置 Paint对象颜色,范围为0~255
mPaint.setAlpha(200); // 设置alpha不透明度,范围为0~255
mPaint.setAntiAlias(true); // 抗锯齿
mPaint.setStyle(Paint.Style.FILL); //描边效果
mPaint.setStrokeWidth(4);//描边宽度
mPaint.setStrokeCap(Paint.Cap.ROUND); //圆角效果
mPaint.setStrokeJoin(Paint.Join.MITER);//拐角风格
mPaint.setShader(new SweepGradient(200, 200, Color.BLUE, Color.RED)); //设置环形渲染器
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); //设置图层混合模式
mPaint.setColorFilter(new LightingColorFilter(0x00ffff, 0x000000)); //设置颜色过滤器
mPaint.setFilterBitmap(true); //设置双线性过滤,相当于对图片做平滑处理
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));//设置画笔遮罩滤镜 ,传入度数和样式
mPaint.setTextScaleX(2);// 设置文本缩放倍数
mPaint.setTextSize(38);// 设置字体大小
mPaint.setTextAlign(Paint.Align.LEFT);//对其方式
mPaint.setUnderlineText(true);// 设置下划线
String str = "Android高级工程师";
Rect rect = new Rect();
mPaint.getTextBounds(str, 0, str.length(), rect); //测量文本大小,将文本大小信息存放在rect中
mPaint.measureText(str); //获取文本的宽
mPaint.getFontMetrics(); //获取字体度量对象
StrokeCap的取值
public enum Cap {
/**
* The stroke ends with the path, and does not project beyond it.
*/
BUTT (0),
/**
* The stroke projects out as a semicircle, with the center at the
* end of the path.
*/
ROUND (1),
/**
* The stroke projects out as a square, with the center at the end
* of the path.
*/
SQUARE (2);
private Cap(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
StrokeJoin 拐角风格
public enum Join {
/**
* The outer edges of a join meet at a sharp angle
*/
MITER (0),
/**
* The outer edges of a join meet in a circular arc.
*/
ROUND (1),
/**
* The outer edges of a join meet with a straight line
*/
BEVEL (2);
private Join(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
FontMetrics 对象
1.2.1-Paint_FontMetrics.png
图形混合模式
- 需要关闭硬件加速
- 图形混合模式只作用于src源图像区域
//所绘制不会提交到画布上
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
//显示上层绘制的图像
new PorterDuffXfermode(PorterDuff.Mode.SRC),
//显示下层绘制图像
new PorterDuffXfermode(PorterDuff.Mode.DST),
//正常绘制显示,上下层绘制叠盖
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
//上下层都显示,下层居上显示
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
//取两层绘制交集,显示上层
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
//取两层绘制交集,显示下层
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
//取上层绘制非交集部分,交集部分变成透明
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
//取下层绘制非交集部分,交集部分变成透明
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
//取上层交集部分与下层非交集部分
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
//取下层交集部分与上层非交集部分
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
//去除两图层交集部分
new PorterDuffXfermode(PorterDuff.Mode.XOR),
//取两图层全部区域,交集部分颜色加深
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
//取两图层全部区域,交集部分颜色点亮
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
//取两图层交集部分,颜色叠加
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
//取两图层全部区域,交集部分滤色
new PorterDuffXfermode(PorterDuff.Mode.SCREEN),
//取两图层全部区域,交集部分饱和度相加
new PorterDuffXfermode(PorterDuff.Mode.ADD),
//取两图层全部区域,交集部分叠加
new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)
这个图片显示的混合模式的前提是:黄色区域就是圆形的区域,蓝色区域就是正方形的区域
这个图片显示的混合模式的前提是:黄色区域是整个方块的区域,除了黄色区域的圆形其他周围都是透明的,蓝色区域就是正方形的区域,除了蓝色区域的正方形其他周围都是透明的
Canavs
- translate(float sx, float sy) 平移
- scale(float sx, float sy) 缩放的同时会平移
- scale(float sx, float sy, float px, float py) px py表示以那个点为轴进行缩放
- rotate(float degrees) 旋转
- rotate(float degrees, float px, float py) px py 旋转的中心
- skew(float sx, float sy) 倾斜操作(不太明白也不常用)
- clipRect 切割
- clipOutRect 反向切割
Canvas 调用了上面的方法后,后续的操作都是基于变化后的Canvas ,都会受到影响,可以使用save saveLayer ,saveLayerAlpha ,restore ,restoreToCount 来保存和恢复状态, canavs.getSaveCount() 可以获取保存了多少次,restore 是restore最近的一次save
save 和saveLayer 的区别
状态栈: save、 restore方法来保存和还原变换操作Matrix以及Clip剪裁 也可以通过restoretoCount直接还原到对应栈的保存状态
Layer栈: saveLayer的时候都会新建一个透明的图层(离屏Bitmap-离屏缓冲),并且会将saveLayer之前的一些Canvas操作延续过来 后续的绘图操作都在新建的layer上面进行 当我们调用restore 或者 restoreToCount 时 更新到对应的图层和画布上
正因为save方法不会创建图层,所以当我们使用Xfermode,ColorFilter,Alpha时应当使用saveLayer(刮刮卡效果)因为只有新建了图层才可能使用 Xfermode,ColorFilter,Alpha 这些方法
saveLayer 会增加内存
Path 的使用
常用方法
1.moveTo(float x, float y) 移动下一次操作的起点位置
2.lineTo(float x, float y) 添加上一个点到当前点之间的直线到Path
3.close() 连接第一个点连接到最后一个点,形成一个闭合区域
4.addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo 添加(矩形, 圆角矩形, 椭圆, 圆, 路径, 圆弧) 到当前Path
4.isEmpty() 判断Path是否为空
5.isRect() 判断path是否是一个矩形
6.set(@NonNull Path src) 用新的路径替换到当前路径所有内容
7.offset(float dx, float dy)/offset(float dx, float dy, @Nullable Path dst) 对当前路径之前的操作进行偏移
8.quadTo, cubicTo 分别为二次和三次贝塞尔曲线的方法
9.rMoveTo, rLineTo, rQuadTo, rCubicTo 不带r的方法是基于原点的坐标系(偏移量),rXxx方法是基于当前点坐标系(偏移量)
10.setFillType, getFillType, isInverseFillType, toggleInverseFillType 设置,获取,判断和切换填充模式
11.op 对两个Path进行布尔运算(即取交集、并集等操作)
12.computeBounds 计算Path的边界
13.reset, rewind 清除Path中的内容(reset相当于重置到new Path阶段,rewind会保留Path的数据结构)
14.transform 矩阵变换
PathMeasure 路径测量
PathMeasure 需要关联一个创建好的Path ,forceClosed 会影响结果
getLength() 获取路径的长度,只是获取到设置的时候的值,并且是path中的最后一条,如果后面path发生变化,需要重新setPath 进行关联
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) // 截取路径 startD 距离Path起点的长度 stopD 距离Path起点的距离
这个API用于截取整个Path的片段,通过参数startD和stopD来控制截取的长度,并将截取的Path保存到dst中,最后一个参数startWithMoveTo表示起始点是否使用moveTo方法,通常为True,保证每次截取的Path片段都是正常的、完整的。
如果startWithMoveTo设置为false,通常是和dst一起使用,因为dst中保存的Path是被不断添加的,而不是每次被覆盖,设置为false,则新增的片段会从上一次Path终点开始计算,这样可以保存截取的Path片段数组连续起来。
由于硬件加速的问题,PathMeasure中的getSegment在讲Path添加到dst数组中时会被导致一些错误,需要通过mDst.lineTo(0,0)来避免这样一个Bug。
public class PathPainter extends View {
private Path mPath;
private Paint mPaint;
private PathMeasure mPathMeasure;
private float mAnimatorValue;
private Path mDst;
private float mLength;
public PathPainter(Context context) {
super(context);
}
public PathPainter(Context context, AttributeSet attrs) {
super(context, attrs);
mPathMeasure = new PathMeasure();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPath = new Path();
mPath.addCircle(400, 400, 100, Path.Direction.CW);
mPathMeasure.setPath(mPath, true);
mLength = mPathMeasure.getLength();
mDst = new Path();
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatorValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(2000);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.start();
}
public PathPainter(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDst.reset();
// 硬件加速的BUG
mDst.lineTo(0,0);
float stop = mLength * mAnimatorValue;
mPathMeasure.getSegment(0, stop, mDst, true);
canvas.drawPath(mDst, mPaint);
}
}
nextContour() 如果包含了多个路径,跳转到下一个路径上
getPosTan boolean getPosTan (float distance, float[] pos, float[] tan)
通过指定distance(0<distance<getLength),来获取坐标点和切线的坐标,并保存到pos[]和tan[]数组中 (一般使用getMatrix函数即可)
boolean getMatrix(float distance, Matrix matrix, int flags) 将pos信息和tan信息保存在 matrix中 flags 取值有 PathMeasure.POSITION_MATRIX_FLAG PathMeasure.TANGENT_MATRIX_FLAG