Android Paint,Path,Canvas
1. Paint
Paint(画笔),保存了绘制几何图形、文本和位图的样式和颜色信息
关键词:color,alpha,stroke,solid,线条圆角效果,拐角风格,xfermode,渲染器,TileMode
1. 线性渲染2. 环形渲染3. 扫描渲染4. 位图渲染5. 组合渲染
2. 图层混合模式
1. 离屏绘制2. 刮刮卡效果实现
3. 滤镜
1. 颜色矩阵(胶片效果等)2. 饱和度3. 亮度值
2. Canvas
Canvas(画布),通过画笔绘制几何图形,文本,路径和位图等
1. drawLine,drawBitmap,drawPath,drawText (绘制几何图形,文本,位图等)2. scale(缩放),rotate,translate(平移) (位置形状变换)3. clipPath4. Matrix5. Canvas调用了translate,scale等变换后,后续的操作都是基于变换后的Canvas,都会受到影响;所有我们采用canvas.save(),restore等方法来保存画布各个阶段状态,避免混乱6. 离子爆炸效果
3. Path、PathMeasure
Path(路径),可用于绘制直线,曲线构成的几何路径,还可根据路径绘制文字;常用API:移动,连线,闭合,添加图形等
一阶贝塞尔曲线(法):数据点和控制点、rLineTo、Rect、类QQ消息数拉拽效果
public class PkLeftView extends View { private static final String TAG = "GradientLayout"; private int mW, mH; // 控件,宽高 Path stampPath = new Path(); public int startX1, startY1; public int startX2, startY2; public int startX3, startY3; public int startX4, startY4; public RectF arcRect; private Shader mShader; private Bitmap mBitmap; private Paint mPaint; private Paint mPaintLine; private int lineH = PixelDpUtils.dp2px(1); // 画线宽度1/2 public int xDown = 40; // 折角宽度 X public PkLeftView(Context context) { this(context, null); } public PkLeftView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PkLeftView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaintLine = new Paint(); //初始化 mPaintLine.setColor(Color.BLACK);// 设置颜色 mPaintLine.setStyle(Paint.Style.STROKE); //描边效果 mPaintLine.setAntiAlias(true); // 抗锯齿 mPaintLine.setStyle(Paint.Style.STROKE); //描边效果 mPaintLine.setStrokeWidth(lineH * 2);//描边宽度 mPaint = new Paint(); //初始化 mPaint.setAntiAlias(true); // 抗锯齿 mPaint.setStyle(Paint.Style.FILL); //描边效果 mPaint.setStrokeWidth(lineH * 2);//描边宽度 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mW = getMeasuredWidth(); mH = getMeasuredHeight(); Log.i(TAG, "mW: " + mW); Log.i(TAG, "mW: " + mH); xDown = mW / 10; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Log.i(TAG, "left: " + left); Log.i(TAG, "top: " + top); Log.i(TAG, "right: " + right); Log.i(TAG, "bottom: " + bottom); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float[]{0.f, 0.7f, 1}, Shader.TileMode.REPEAT); mPaint.setShader(mShader); // 1. 首先移动到起始点 startX1 = mH / 2; startY1 = lineH; stampPath.moveTo(startX1, startY1); // 2. 画水平线 startX2 = mW - lineH; startY2 = lineH; stampPath.lineTo(startX2, startY2); // 3. 画倒角 startX3 = startX2 - xDown; startY3 = mH - lineH; stampPath.lineTo(startX3, startY3); // 4. 画下边 startX4 = mH / 2; startY4 = startY3; stampPath.lineTo(startX4, startY4); // // 5. 画圆弧 if (arcRect == null) { arcRect = new RectF(lineH, lineH, mH - lineH, mH - lineH); } stampPath.addArc(arcRect, 90, 180); // 6. 将Path利用Paint绘制出来 canvas.drawPath(stampPath, mPaint); canvas.save(); // 7. canvas.drawPath(stampPath, mPaintLine); } }
public class GradientLayout extends View { private Paint mPaint; private Shader mShader; private Bitmap mBitmap; public GradientLayout(Context context) { this(context, null); } public GradientLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GradientLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.beauty); 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(); //获取字体度量对象 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * 1.线性渲染,LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) * (x0,y0):渐变起始点坐标 * (x1,y1):渐变结束点坐标 * color0:渐变开始点颜色,16进制的颜色表示,必须要带有透明度 * color1:渐变结束颜色 * colors:渐变数组 * positions:位置数组,position的取值范围[0,1],作用是指定某个位置的颜色值,如果传null,渐变就线性变化。 * tile:用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法 */ // mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float[]{0.f,0.7f,1}, Shader.TileMode.REPEAT); // mPaint.setShader(mShader); // canvas.drawCircle(250, 250, 250, mPaint); // canvas.drawRect(0,0,1000,1000, mPaint); /** * 环形渲染,RadialGradient(float centerX, float centerY, float radius, @ColorInt int colors[], @Nullable float stops[], TileMode tileMode) * centerX ,centerY:shader的中心坐标,开始渐变的坐标 * radius:渐变的半径 * centerColor,edgeColor:中心点渐变颜色,边界的渐变颜色 * colors:渐变颜色数组 * stoops:渐变位置数组,类似扫描渐变的positions数组,取值[0,1],中心点为0,半径到达位置为1.0f * tileMode:shader未覆盖以外的填充模式。 */ // mShader = new RadialGradient(250, 250, 250, new int[]{Color.GREEN, Color.YELLOW, Color.RED}, null, Shader.TileMode.CLAMP); // mPaint.setShader(mShader); // canvas.drawCircle(250, 250, 250, mPaint); /** * 扫描渲染,SweepGradient(float cx, float cy, @ColorInt int color0,int color1) * cx,cy 渐变中心坐标 * color0,color1:渐变开始结束颜色 * colors,positions:类似LinearGradient,用于多颜色渐变,positions为null时,根据颜色线性渐变 */ // mShader = new SweepGradient(250, 250, Color.RED, Color.GREEN); // mPaint.setShader(mShader); // canvas.drawCircle(250, 250, 250, mPaint); /** * 位图渲染,BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) * Bitmap:构造shader使用的bitmap * tileX:X轴方向的TileMode * tileY:Y轴方向的TileMode REPEAT, 绘制区域超过渲染区域的部分,重复排版 CLAMP, 绘制区域超过渲染区域的部分,会以最后一个像素拉伸排版 MIRROR, 绘制区域超过渲染区域的部分,镜像翻转排版 */ // mShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR); // mPaint.setShader(mShader); // canvas.drawRect(0,0,500, 500, mPaint); // canvas.drawCircle(250, 250, 250, mPaint); /** * 组合渲染, * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, Xfermode mode) * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, PorterDuff.Mode mode) * shaderA,shaderB:要混合的两种shader * Xfermode mode: 组合两种shader颜色的模式 * PorterDuff.Mode mode: 组合两种shader颜色的模式 */ BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); LinearGradient linearGradient = new LinearGradient(0, 0, 1000, 1600, new int[]{Color.RED, Color.GREEN, Color.BLUE}, null, Shader.TileMode.CLAMP); mShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY); mPaint.setShader(mShader); canvas.drawCircle(250, 250, 250, mPaint); } }
1. 划线两端圆角效果
mPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角效果
https://www.codercto.com/a/43619.html
Paint:
一、setAntiAlias()
一般用于绘制不规则图形的时候,使用抗锯齿,比如圆形、文字等。对于规则的图形,是不需要打开抗锯齿功能的,比如矩形。
二、setStyle()
设置填充的样式,主要用于控制几何图形
- Paint.Style.FILL 填充内部
- Paint.Style.FILL_AND_STROKE 填充内部和描边
- Paint.Style.STROKE 仅描边
三、setStrokeWidth()
设置画笔的宽度,单位是px.对画笔的STYLE设置成STROKE和FILL_AND_STROKE时有效。但画支线时无论style设置什么值,均有效。
注意事项:
当设置较大stroke画圆,并且绘画的范围占满(基本占满)画布或rect时,stroke会超出绘画范围。此时需要将原来的圆半径减去stroke一半得到的长度作为新圆的半径。stroke超出的长度其实为stroke宽度的一半。
对于椭圆也差不多:
四、Paint.Cap
Cap指定了描边线和路径的开始和结束的处理。默认为BUTT。
- Paint.Cap.BUTT 无线帽
- Paint.Cap.ROUND 圆形线帽
- Paint.Cap.SQUARE 方形线帽
五、Paint.Join
Join指定线条和曲线段在描边路径上连接的处理。默认为MITER。
- Paint.Join.BEVEL 连接的外边缘以直线相交
- Paint.Join.MITER 连接的外边缘以锐角相交
- Paint.Join.ROUND 连接的外边缘以圆弧相交
当画笔style设置为STROKE或者FILL_AND_STROKE时,绘画图形或绘画路径,会根据画笔的Join值对线与线之间的结合处进行处理。
Canvas:
一、画背景
canvas提供3种方法可以用于绘制画布的背景
void drawColor(int color)
void drawARGB(int a,int r,int g,int b)
void drawRGB(int r,int g,int b)
复制代码
二、画直线
- startX: 起始点X坐标
- startY: 起始点Y坐标
- stopX: 终点X坐标
- stopY: 终点Y坐标
void drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
复制代码
三、画矩形
矩形的范围可以使用两个矩形 工具 类进行设置:Rect 和 RectF,两者的主要区别就是Rect存储的上下左右均为Int类型,而RectF存储的上下左右均为Float类型。
画圆角矩形:
- rect 矩形的范围
- rx 生成圆角的x轴半径
- ry 生成圆角的y轴半径
但drawRoundRect()方法只能生成四个圆角都是一致的矩形:
如果想生成圆角不一致的圆角,可使用shape标签:
四、画弧
- RectF 圆弧绘画的矩形范围。
- startAngle 开始的角度。
- sweepAngle 整条弧跨越的弧度。
- useCenter 如果为true,则在弧形中包含椭圆的中心并闭合它。
画笔不同的style和drawArc()useCenter的值可绘画出不同的圆弧或弧线:
注意事项:
canvas画弧时,0度的起始位置在x轴正方向上。
五、画文字
- text 绘画的文本
- start 第一个字符的索引
- end 文本的最后一个字符的索引
- x 文本绘画的起始x轴位置
- y 基线(BaseLine)
前面几个参数都很好理解,无非就是文本、索引值和绘画起始的x轴位置,但基线是什么,怎么求呢? Baseline是文字绘制时所参照的基准线,观察下图可看见基线是大部分英文字母的底部沿线。只有确定Baseline的位置,才能将文字准确的绘制在我们想要的位置上。
需要先理清楚一些概念:
1、基线并不是中心线
2、FontMetricsInt.top表示BaseLine到顶部的距离
3、FontMetricsInt.bottom表示BaseLine到底部的距离
4、dy表示BaseLine到中心线的距离
5、BaseLine = 中心线 + dy
6、dy = 中心线 - FontMetricsInt.bottom。因为FontMetricsInt.bottom表示BaseLine到底部的距离。
7、FontMetricsInt.top是一个负值,FontMetricsInt.bottom是一个正数。所以求改文字的中心线时,等于(FontMetricsInt.bottom - FontMetricsInt.top)/ 2
中心线是按文字的所在的位置决定的。以联系人中的字母索引列表为例,每个字母的所需要绘画的高都不一样,需要计算每个字母可占据的高度,再除以一半求出中心线的位置。
六、裁剪
剪切是对canvas的裁剪操作,并不影响原来的之前已经画好的图形。
以clipRect()裁剪变色字体为例:
七、save()和restore()
每次调用save(),都会先保存当前画布的状态,然后将其放入到特定的栈中。
每次调用restore(),都会把栈顶的画布取出来,并按照这个状态恢复当前的画布,然后在这个画布上作画。
clipXX系列的函数对画布的操作是不可逆的,除非调用了save()和restore()对画布进行保存和恢复。
Path:
path代表路径,在canvas中使用drawPath(Path,Paint)进行绘画路径。
一、画直线路径
void moveTo(float x1,float y1)
void lineTo(float x2,float y2)
void close()
复制代码
(x1,y1)是直线的起始点,(x2,y2)是直线的终点,又是下一个绘制直线路径的起始点,lineTo可以无限用。
调用了moveTo()后,调用lineTo()画了N条直线,还没有形成闭环的话,可以调用close()将路径的首尾连接起来。
二、画弧形路径
使用3参的arcTo()方法画出的弧会和起始点相连,因为默认情况下路径都是连贯的,除非以下两种情况:
1、调用addXX系列函数,直接添加固定形状的路径
2、moveTo()改变起始位置
调用4参的arcTo()方法,将第4个参数forceMoveTo设置为true,会使弧的起始点作为绘制的起始点。
三、addXXX系列函数
路径默认是连贯的,但addXXX()系列函数可以直接添加一些固定形状的路径,不必考虑连贯性。
1、添加矩形路径
Path.Direction 用于指定如何封闭的形状(如矩形、椭圆形)。
- Path.Direction.CCW: 指创建逆时针方向的矩形路径。
- Path.Direction.CW: 指创建顺时针方向的矩形路径。
目前顺时针和逆时针的生成对矩形图形并无影响,但对于根据路径方向绘画文字等,则会起到关键作用。
2、添加圆角矩形路径
绘画圆角矩形路径对比绘画圆角矩形多了两个重载方法,其中float[] radii参数时用于定义4个角的椭圆各自的x轴半径和y轴半径,换句话说,即使用addRoundRect()也可以实现4个圆角不同的矩形。但圆角的顺序并不因为Path.Direction值的不同而有所改变。
3、添加圆形路径
具体参数和canvas绘制圆一样, x 为圆心X轴坐标, y 为圆心y轴坐标,radius 为圆的半径。
4、添加椭圆路径
具体参数和canvas绘制椭圆一样
5、添加圆弧路径
具体参数和canvas绘制圆弧一样
一般使用方案二进行对文本宽高的测量,最精准。
本文来源:码农网
本文链接:https://www.codercto.com/a/43619.html
refs:
https://blog.csdn.net/zmm911zmm/article/details/89784920
https://www.codercto.com/a/43619.html
https://juejin.cn/post/6844903487570968584 | HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解 - 掘金
https://juejin.cn/post/6844903488460177416 | HenCoder Android 开发进阶:自定义 View 1-3 文字的绘制 - 掘金
https://juejin.cn/post/6844903489789755406 | HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助 - 掘金
https://juejin.cn/post/6844903491031269383 | HenCoder Android 自定义 View 1-5: 绘制顺序 - 掘金
https://juejin.cn/post/6844903494256689165 | HenCoder Android 自定义 View 1-6: 属性动画(上手篇) - 掘金
https://juejin.cn/post/6844903494940360711 | 【HenCoder Android 开发进阶】自定义 View 1-7:属性动画(进阶篇) - 掘金
https://juejin.cn/post/6844903496064434183 | # HenCoder Android 自定义 View 1-8 硬件加速 - 掘金
https://rengwuxian.com/ui-2-1/ | HenCoder Android 自定义 View 2-1 布局基础
https://rengwuxian.com/ui-2-2/ | HenCoder Android 自定义 View 2-2 全新定义 View 的尺寸
https://rengwuxian.com/ui-2-3/ | HenCoder Android 自定义 View 2-3 定制 Layout 的内部布局
https://rengwuxian.com/ui-3-1/ | HenCoder 自定义 View 3-1 触摸反馈,以及 HenCoder Plus

浙公网安备 33010602011771号