android中自定义view的过程中,需要了解的绘制知识。
1.画笔paint:
画笔设置:
<span style="font-size:14px;"> paint.setAntiAlias(true);//抗锯齿功能 paint.setColor(Color.RED); //设置画笔颜色 paint.setStyle(Style.FILL);//设置填充样式 paint.setStrokeWidth(30);//设置画笔宽度 paint.setShadowLayer(10, 15, 15, Color.GREEN);//设置阴影 Paint.Style.FILL :填充内部 Paint.Style.FILL_AND_STROKE :填充内部和描边 Paint.Style.STROKE :仅描边</span>
我们在画笔canvas上绘制就要用到。
2.画布canvas:
如何获取我们的画布呢?
第一种方法是:在绘制View控件时,需要重写onDraw()函数,在绘制ViewGroup时,需要重写dispatchDraw()函数。可以从ondraw()或者dispatchDraw()中获取到canvas对象;第二种方法是:Bitmap bmp = Bitmap.createBitmap(width ,height Bitmap.Config.ARGB_8888); 或者 Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.wave_bg,null); Canvas c = new Canvas(bitmap); 之后用c画的信息都在bitmap上,如果要显示在view上,我们要在ondraw中使用系统的canvas.drawbitmap(bmp)。saveLayer会创建一个全新透明的bitmap,大小与指定保存的区域一致,其后的绘图操作都放在这个bitmap上进行。在绘制结束后,会直接盖在上一层的Bitmap上显示。画布的变换;平移(translate) void translate(float dx, float dy) 只是平移坐标系的原点的位置,之前画的东西不变,之后画的东西依照新的原点画 旋转(Rotate) void rotate (float degrees, float px, float py) 旋转的是坐标系 缩放(scale ) public void scale (float sx, float sy) 扭曲(skew) void skew (float sx, float sy) float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值, float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值, 改变的是坐标系 裁剪画布(clip系列函数) 得到新的画布 boolean clipRect(float left, float top, float right, float bottom, Region.Op op) boolean clipRect(Rect rect) 画布的保存与恢复(save()、restore()) protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.RED); //保存的画布大小为全屏幕大小 canvas.save(); canvas.clipRect(new Rect(100, 100, 800, 800)); canvas.drawColor(Color.GREEN); //保存画布大小为Rect(100, 100, 800, 800) canvas.save(); canvas.clipRect(new Rect(200, 200, 700, 700)); canvas.drawColor(Color.BLUE); //保存画布大小为Rect(200, 200, 700, 700) canvas.save(); canvas.clipRect(new Rect(300, 300, 600, 600)); canvas.drawColor(Color.BLACK); //保存画布大小为Rect(300, 300, 600, 600) canvas.save(); canvas.clipRect(new Rect(400, 400, 500, 500)); canvas.drawColor(Color.WHITE); //连续出栈三次,将最后一次出栈的Canvas状态作为当前画布,并画成黄色背景 canvas.restore(); canvas.restore(); canvas.restore(); canvas.drawColor(Color.YELLOW); }3.绘制各种线,点,图形,文字,图片;绘制多条直线:pts的组织方式为{x1,y1,x2,y2,x3,y3,……} 两两连接,(x1,y1)-(x2,y2)相连;(x3,y3)-(x3,y4)相连 void drawLines (float[] pts, Paint paint) void drawLines (float[] pts, int offset, int count, Paint paint)绘制多个点:
void drawPoints (float[] pts, Paint paint) void drawPoints (float[] pts, int offset, int count, Paint paint) float[] pts:点的合集,与上面直线一直,样式为{x1,y1,x2,y2,x3,y3,……} int offset:集合中跳过的数值个数,注意不是点的个数!一个点是两个数值; count:参与绘制的数值的个数,指pts[]里人数值个数,而不是点的个数,因为一个点是两个数值绘制几何图形:
<pre name="code" class="html"> Rect() Rect(int left, int top, int right, int bottom) Rect(Rect r) void drawRoundRect (RectF rect, float rx, float ry, Paint paint) void drawCircle (float cx, float cy, float radius, Paint paint) void drawOval (RectF oval, Paint paint) void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 参数:弧是椭圆的一部分,而椭圆是根据外接矩形来生成的 RectF oval:生成椭圆的矩形 float startAngle:弧开始的角度,以X轴正方向为0度 float sweepAngle:弧持续的角度 boolean useCenter:是否有弧的两边,True,还两边,False,只有一条弧绘制路径:
void drawPath (Path path, Paint paint) 直线路径 *void moveTo (float x1, float y1):直线的开始点;即将直线路径的绘制点定在(x1,y1)的位置; void lineTo (float x2, float y2):直线的结束点,又是下一次绘制直线路径的开始点;lineTo()可以一直用; void close ():如果连续画了几条直线,但没有形成闭环,调用Close()会将路径首尾点连接起来,形成闭环; * *矩形路径 *void addRect (float left, float top, float right, float bottom, Path.Direction dir) *Path.Direction.CCW:是counter-clockwise缩写,指创建逆时针方向的矩形路径; Path.Direction.CW:是clockwise的缩写,指创建顺时针方向的矩形路径; * *圆角矩形路径 *void addRoundRect (RectF rect, float[] radii, Path.Direction dir) *float[] radii:必须传入8个数值,分四组,分别对应每个角所使用的椭圆的横轴半径和纵轴半径 *void addRoundRect (RectF rect, float rx, float ry, Path.Direction dir) *只能构建统一圆角大小,长轴和短轴的半径 * *圆形路径 *void addCircle (float x, float y, float radius, Path.Direction dir) 椭圆路径 void addOval (RectF oval, Path.Direction dir) 弧形路径 void addArc (RectF oval, float startAngle, float sweepAngle)为绘制的路径添加特效:reset() 重置画笔 setStrokeCap(Paint.Cap cap) 设置线冒样式,取值有Cap.ROUND(圆形线冒)、Cap.SQUARE(方形线冒)、Paint.Cap.BUTT(无线冒) .默认是无线冒的 ,加线冒即使在始末加上一个帽子 setStrokeJoin(Paint.Join join) 设置线段连接处样式,取值有:Join.MITER(结合处为锐角)、Join.Round(结合处为圆弧)、Join.BEVEL(结合处为直线) setPathEffect(PathEffect effect) 设置路径样式;取值类型是所有派生自PathEffect的子类: CornerPathEffect, —圆形拐角效果 public CornerPathEffect(float radius) DashPathEffect, —虚线效果 public DashPathEffect(float intervals[], float phase) //intervals[] intervals[]:表示组成虚线的各个线段的长度;整条虚线就是由intervals[]中这些基本线段循环组成的。比如,我们定义new float[] {20,10};那这个虚线段就是基线就是由两段线段组成的,第一个可见的线段长为20,每二个线段不可见,长度为10; 长度必须大于等于2;因为必须有一个实线段和一个空线段来组成虚线。 个数必须为偶数,如果是基数,最后一个数字将被忽略;这个很好理解,因为一组虚线的组成必然是一个实线和一个空线成对组成的。 DiscretePathEffect, 离散路径效果 public DiscretePathEffect(float segmentLength, float deviation) segmentLength为将path分割成多少很多段后每段的长度。deviation每条段的偏移量 PathDashPathEffect, 印章路径效果 public PathDashPathEffect(Path shape, float advance, float phase,Style style) Path shape:表示印章路径,比如我们下面示例中的三角形加右上角一个点; float advance:表示两个印章路径间的距离,很容易理解,印章间距离越大,间距就越大。 float phase:路径绘制偏移距离,与上面DashPathEffect中的float phase参数意义相同 Style style:表示在遇到转角时,如何操作印章以使转角平滑过渡,取值有:Style.ROTATE,Style.MORPH,Style.TRANSLATE;Style.ROTATE表示通过旋转印章来过渡转角; Style.MORPH表示通过变形印章来过渡转角;Style.TRANSLATE表示通过位移来过渡转角。 SumPathEffect, * ComposePathEffect, * 这两个都是做特效合并的。具体做法不同 * 假设这里有原始路径A. 特效路径B, C * ComposePathEffect是public ComposePathEffect(PathEffect B, PathEffect C) ;先对A加上C,再加上B * SumPathEffect是public SumPathEffect(PathEffect B, PathEffect C) ;分别对A做B、C特效生成D、E.然后将D,E合并得到最终特效绘制路径还涉及到了一个曲线叫做贝塞尔曲线,常用的2介和3介曲线。
绘制文字:
文字画笔的设置;//普通设置 paint.setStrokeWidth (5);//设置画笔宽度 paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢 paint.setStyle(Paint.Style.FILL);//绘图样式,对于设文字和几何图形都有效 paint.setTextAlign(Align.CENTER);//设置文字对齐方式,取值:align.CENTER、align.LEFT或align.RIGHT paint.setTextSize(12);//设置文字大小 //样式设置 paint.setFakeBoldText(true);//设置是否为粗体文字 paint.setUnderlineText(true);//设置下划线 paint.setTextSkewX((float) -0.25);//设置字体水平倾斜度,普通斜体字是-0.25 paint.setStrikeThruText(true);//设置带有删除线效果 //其它设置 paint.setTextScaleX(2);//只会将水平方向拉伸,高度不会变绘制文字的方法有这些:普通水平绘制 void drawText (String text, float x, float y, Paint paint) void drawText (CharSequence text, int start, int end, float x, float y, Paint paint) CharSequence类型,可以实现带图片的文字绘制。功能最强大 指定个个文字位置 void drawPosText (String text, float[] pos, Paint paint) float[] pos:每个字体的位置,同样两两一组,如{x1,y1,x2,y2,x3,y3……} 沿路径绘制 void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) float hOffset : 与路径起始点的水平偏移距离 float vOffset : 与路径中心的垂直偏移量绘制文字过程中需要注意的地方:
* mPaint.setTextAlign(Align.CENTER); canvas.drawText("aafdADF", x, y, * mPaint); Align.CENTER, 绘制的时候保证整体文字的中间在(x,y)处 Align.LEFT, * 绘制的时候保证整体文字最左边位于(x,y)处 Align.Right, 绘制的时候保证整体文字最右边位于(x,y)处 * * * FontMetrics fontMetrics = mPaint.getFontMetrics(); * fontMetrics.ascent;可绘制的最顶部 * fontMetrics.descent; 可绘制的最底部 * fontMetrics.top;物理最顶部,相当于电视剧的屏幕一样 * fontMetrics.bottom; 物理最底部 * * 我们的文字是显示在ascent与descent之间的 这4个值都是相当于baseline而言的。所以fontMetrics.ascent= * ascent线-baseline线 得到的结果是负的 * * 从而可以推出; * ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent; * descent线Y坐标 =baseline线的y坐标 + fontMetric.descent; * top线Y坐标 = baseline线的y坐标 +fontMetric.top; * bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom; * * * 所绘文字宽度int width = paint.measureText(String text); * 高度 * Paint.FontMetricsInt fm = paint.getFontMetricsInt(); * int top = baseLineY+ fm.top; * int bottom = baseLineY + fm.bottom; * int height = fm.bottom -fm.top; */ /** * * @param textPaint * @return * fontMetrics.top,fontMetrics.ascent,fontMetrics.descent,fontMetrics.bottom */ private static float[] getMuxLineOffset(Paint textPaint) { FontMetrics fontMetrics = textPaint.getFontMetrics(); return new float[] { fontMetrics.top, fontMetrics.ascent, fontMetrics.descent, fontMetrics.bottom }; } //获取文字宽度 public static float getTextWidth(Paint textPaint, String text) { return textPaint.measureText(text); } // 物理尺寸的高度,最大高度,我们用来适配文字的大小 public static float getTextMaxHeight(Paint textPaint) { Paint.FontMetrics fm = textPaint.getFontMetrics(); return fm.bottom - fm.top; } /** * 返回基线位置,已知top点Y坐标 * * @param textPaint * @return */ public static float getBaseLineYFromTopY(Paint textPaint, float textTopY) { float baseLineY = textTopY - getMuxLineOffset(textPaint)[0]; return baseLineY; } /** * 返回基线位置,已知center点Y坐标 * @param textPaint * @param textCenterY * @return */ public static float getBaseLineYFromCenterY(Paint textPaint, float textCenterY) { float baseLineY = textCenterY + getTextMaxHeight(textPaint) / 2 - getMuxLineOffset(textPaint)[3]; return baseLineY; }绘制区域:
public Region(int left, int top, int right, int bottom) //创建一个矩形的区域 Region boolean setPath (Path path, Region clip)构造不规则区域 Path path:用来构造的区域的路径 Region clip:与前面的path所构成的路径取交集,并将两交集设置为最终的区域 矩形集枚举区域——RegionIterator类,对于特定的区域,我们都可以使用多个矩形来表示其大致形状 RegionIterator(Region region) //根据区域构建对应的矩形集 boolean next(Rect r) //获取下一个矩形,结果保存在参数Rect r 中 Canvas中并没有画Region的函数,我们可以借助 private void drawRegion(Canvas canvas,Region rgn,Paint paint) { RegionIterator iter = new RegionIterator(rgn); Rect r = new Rect(); while (iter.next(r)) { canvas.drawRect(r, paint); } } 区域的合并、交叉等操作 public boolean op(Region region, Op op) 假设用region1 去组合region2 public enum Op { DIFFERENCE(0), //最终区域为region1 与 region2不同的区域 INTERSECT(1), // 最终区域为region1 与 region2相交的区域 UNION(2), //最终区域为region1 与 region2组合一起的区域 XOR(3), //最终区域为region1 与 region2相交之外的区域 REVERSE_DIFFERENCE(4), //最终区域为region2 与 region1不同的区域 REPLACE(5); //最终区域为为region2的区域 } //构造两个Region Region region = new Region(rect1); Region region2= new Region(rect2); //取两个区域的交集 region.op(region2, Op.INTERSECT); 取了op之后的结果在region4.其他知识:
使用颜色矩阵,完成对图片的各种特效处理;/** 色彩矩阵 Bitmap由32位表示,A,R,G,B每一个由8位表示。 色彩矩阵 { R 0,0,0,0 0,G,0,0,0 0 0,B,0,0 0 0,0,A,0 } 第5列表示对应R,G,B,A上的增量。 第1到4列表示对R,G,B,A做线性变化 。 都可以用来过滤色彩和增加某一个色彩的饱和度 生成色彩矩阵 ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0.5, 0, }); mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)) 整体调整色彩饱和度 ColorMatrix colorMatrix = new ColorMatrix(); //整体增强颜色饱和度,即同时增强R,G,B的色彩饱和度 public void setSaturation(float sat) setScale——色彩缩放 public void setScale(float rScale, float gScale, float bScale,float aScale) setRotate——色彩旋转 public void setRotate(int axis, float degrees); ColorMatrics相乘 public void setConcat(ColorMatrix matA, ColorMatrix matB) public void preConcat(ColorMatrix prematrix) public void postConcat(ColorMatrix postmatrix) getArray()获取当前矩阵数组 */ /** * 色彩反转矩阵 * 颜色值77,反转后255-77 * @return */ public static ColorMatrix createColorReverse(){ float[] src = { -1, 0, 0, 0, 255, 0, -1, 0, 0, 255, 0, 0, -1, 0, 255, 0, 0, 0, 1, 0, }; return new ColorMatrix(src); } /** * 调节亮度的矩阵 * @param r * @param g * @param b * @param a * @return */ public static ColorMatrix createLightnessAdjust(float r,float g,float b,float a){ float[] src = { r, 0, 0, 0, 0, 0, g, 0, 0, 0, 0, 0, b, 0, 0, 0, 0, 0, a, 0, }; return new ColorMatrix(src); } /** * 过滤颜色通道输出。 * @param hasR * @param hasG * @param hasB * @return */ public static ColorMatrix createColorChannel(boolean hasR,boolean hasG,boolean hasB){ int r = hasR?1:0; int g = hasG?1:0; int b = hasB?1:0; float[] src = { r, 0, 0, 0, 0, 0, g, 0, 0, 0, 0, 0, b, 0, 0, 0, 0, 0, 1, 0, }; return new ColorMatrix(src); } /** * 增加饱和度 * @param dR * @param dG * @param dB * @return */ public static ColorMatrix createIncreaseSaturation(int dR,int dG,int dB){ float[] src = { 1, 0, 0, 0, dR, 0, 1, 0, 0, dG, 0, 0, 1, 0, dB, 0, 0, 0, 1, 0, }; return new ColorMatrix(src); } /** * * @param type 0,1,2 -->R,G,B * @param angle 旋转角度-180,180 * @return */ public static ColorMatrix createHueAdjust(int type,int angle){ float cosAngle = (float) Math.cos(Math.toRadians(angle)); float sinAngle = (float) Math.sin(Math.toRadians(angle)); if(type==0){ //红色色相调节 float[] src = { 1, 0, 0, 0, 0, 0, cosAngle, sinAngle, 0, 0, 0, -sinAngle, cosAngle, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0 }; return new ColorMatrix(src); }else if(type==1){ //绿色色相调节 float[] src = { cosAngle, 0, -sinAngle, 0, 0, 0, 1, 0, 0, 0, sinAngle, 0, cosAngle, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }; return new ColorMatrix(src); }else if(type==2){ //蓝色色相调节 float[] src = { cosAngle, sinAngle,0, 0, 0, -sinAngle, cosAngle, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }; return new ColorMatrix(src); } return null; } /** * 产生一个黑白矩阵 * @return */ public static ColorMatrix createBlackWhite(){ ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 0.213f, 0.715f, 0.072f, 0, 0, 0.213f, 0.715f, 0.072f, 0, 0, 0.213f, 0.715f, 0.072f, 0, 0, 0, 0, 0, 1, 0, }); return colorMatrix; } /** * 红绿反转 * @return */ public static ColorMatrix createColorsRGInvert(){ ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 0,1,0,0,0, 1,0,0,0,0, 0,0,1,0,0, 0,0,0,1,0 }); return colorMatrix; }虽然我们可以通过矩阵的方式来实现各种效果,但是不免有些繁琐。android提供了一些常见的方法来完成上面的效果;
paint.setColorFilter(new ColorFilter());实际过程中我们使用的是ColorFilter的3个子类 ColorMatrixColorFilter new ColorMatrixColorFilter(colorMatrix) 该方法是最强大的,只要构造合适的矩阵都可以实现下面的功能 LightingColorFilter 光照颜色过滤 LightingColorFilter(int mul, int add) 可以用来过滤颜色,比如只留下红色mul=0xff0000 add=0x000000 增强某一颜色 红色 mul=0xffffff add=0x330000 PorterDuffColorFilter PorterDuffColorFilter(int srcColor, PorterDuff.Mode mode) int srcColor:0xAARRGGBB类型的颜色值。 PorterDuff.Mode mode:表示混合模式,枚举值有18个,表示各种图形混合模式,有: Mode.ADD(饱和度相加), Mode.DARKEN(变暗), Mode.LIGHTEN(变亮), Mode.MULTIPLY(正片叠底), Mode.OVERLAY(叠加), Mode.SCREEN(滤色) Mode.CLEAR和Mode.XOR他们在这里的效果是完成一致的,就是把图像清空使用setXfermode;
* 在API 11以后,在程序集中加入了对GPU加速的支持,在API 14之后, * 硬件加速是默认开启的! * 也就是说在API 11——API 13虽然是支持硬件加速的,但是默认是关闭的。 * *setXfermode * Xfermode的有 * AvoidXfermode, * PixelXorXfermode, * PorterDuffXfermode; 禁用GPU加速,方式。 <application android:hardwareAccelerated="true" ...> 或 <activity android:hardwareAccelerated="false" /> 或Window层级不支持关闭硬件加速 getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); 或view 层级上不支持开启硬件加速 setLayerType(View.LAYER_TYPE_SOFTWARE, null); 在使用Xfermode时,为保险我们要 关闭硬件加速,setLayerType(View.LAYER_TYPE_SOFTWARE, null); 使用离屏绘制, //新建图层 int layerID = canvas.saveLayer(0,0,width,height,mPaint,Canvas.ALL_SAVE_FLAG); //TODO 核心绘制代码 //还原图层 canvas.restoreToCount(layerID); public AvoidXfermode(int opColor, int tolerance, Mode mode) 第一个参数opColor:一个16进制的AARRGGBB的颜色值; 第二个参数tolerance:表示容差,这个概念我们后面再细讲 第三个参数mode:取值有两个Mode.TARGET和Mode.AVOID; Mode.TARGET表示将指定的颜色替换掉 Mode.AVOID表示除去白色之外的其他颜色替换掉 public PorterDuffXfermode(PorterDuff.Mode mode)
时间: 2024-10-07 13:16:23