Paint类解析
在自定义组件中,Paint类是一个很重要的类,主要包含颜色、文本、图形样式、位图模式、滤镜等几个方面。Paint类的相关方法如下: 1、颜色是指绘图时使用的颜色,在 Android 中颜色可以指定透明度,使用 16 进制来表示颜色 时,格式通常为#AARRGGBB,其中,AA 表示透明度、RR 表示红色、GG 表示绿色、BB 表示蓝色, Color 类定义了颜色信息,内置了常用颜色的 int 型常量,比如 Color.RED 是红色,Color.BLUE 是 蓝色……如果您习惯了 16 进制的颜色, Color 类的静态方法 parseColor(String colorString)可以将 16进制颜色转换成 Color 类型。需要注意的是,Android 的颜色都是 int 类型的,Color 类只负责颜色的管理而不是代表某种颜色。 Paint 类中与颜色相关的方法有: public native void setColor(int color);//设置颜色 public native void setAlpha(int a);//设置透明度,a 的范围取 0~255 之间的整数 public void setARGB(int a,int r,int g,int b);//指定透明度、红、绿、蓝定义一种颜色 2、绘制文本时,可以指定文本的大小、对齐方式、文本样式等属性,文本样式主要是为文本指定粗体、下划线、删除线等修饰性属性,主要说明下设置字体大小这个: public native void setTextSize(float textSize);// 设置文本大小,单位是 px,这个和我们平时使用的字体单位 sp 不同,所以最好进行转换,这个要注意。 比较麻烦的是绘制文字时,我们还要考虑字体的基本结构,字体的信息使用 Paint.FontMetrics 类来表示,该类源码如下: public static class FontMetrics { public float ascent; public float bottom; public float descent; public float leading; public float top; } 从文字上理解可能比较晦涩,看下示意图也许很容易找到答案: 简单来说,常用字符的高度是 ascent 和 descent 的和,但是,一些特殊字符比如拼音的音调等则会延伸到 top 的位置。 baseline:基准点(绿色虚线所指); ascent:baseline 之上至字符最高处的距离; descent:baseline 之下至字符最低处的距离; top:字符可达最高处到 baseline 的值,即 ascent 的最大值; bottom:字符可达最低处到 baseline 的值,即 descent 的最大值。 leading:行间距,表示上一行字符的descent到该行字符的ascent之间的距离 根据世界范围内已入案的使用语言中能够标注在字符上方或者下方的除了类似的符号肯定是数不胜数的,一般情况下我们极少使用到类似的符号所以往往会忽略掉这些符号的存在,但是Android依然会在绘制文本的时候在文本外层留出一定的边距,这就是为什么 top和bottom总会比ascent和descent大一点的原因。而在TextView中我们可以通过xml设置其属性 android:includeFontPadding="false"去掉一定的边距值但是不能完全去掉。 要获取 FontMetrics 对象,调用 Paint 类的 getFontMetrics()即可,而在 drawText()方法中,参数 y 就是 baseline 的值,因为 FontMetrics 类并没有声明 baseline 属性,所以,我们需要通过下面的公式计算出来: int baseline=(int) (viewHeight - fontMetrics.descent - fontMetrics.ascent) /2; 其中,viewHeight 是文字所在区域的高度。 下面自定义一个文本绘制的View public class TextView1 extends View { private static final String TEXT = "Android自定义"; private Paint paint; public TextView1(Context context) { super(context); } public TextView1(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.RED); FontMetrics fontMetrics = paint.getFontMetrics(); Log.d("xmr", "ascent:" + fontMetrics.ascent); Log.d("xmr", "top:" + fontMetrics.top); Log.d("xmr", "leading:" + fontMetrics.leading); Log.d("xmr", "descent:" + fontMetrics.descent); Log.d("xmr", "bottom:" + fontMetrics.bottom); } public TextView1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 将文字放在正中间 Rect textRect = this.getTextRect(); int viewWidth = getMeasuredWidth(); int viewHeight = getMeasuredHeight(); Paint.FontMetrics fontMetrics = paint.getFontMetrics(); int x = (viewWidth - textRect.width()) / 2; int y = (int) (viewHeight - fontMetrics.descent - fontMetrics.ascent) / 2; canvas.drawText(TEXT, x, y, paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Rect rect = getTextRect(); int textWidth = rect.width(); int textHeight = rect.height(); int width = measureWidth(widthMeasureSpec, textWidth); int height = measureHeight(heightMeasureSpec, textHeight); setMeasuredDimension(width, height); } /** * 获取文字所占的尺寸 * * @return */ private Rect getTextRect() { // 根据 Paint 设置的绘制参数计算文字所占的宽度 Rect rect = new Rect(); // 文字所占的区域大小保存在 rect 中 paint.getTextBounds(TEXT, 0, TEXT.length(), rect); return rect; } /** * 测量组件宽度 * * @param widthMeasureSpec * @param textWidth * 文字所占宽度 * @return */ private int measureWidth(int widthMeasureSpec, int textWidth) { int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); int width = 0; if (mode == MeasureSpec.EXACTLY) { // 宽度为 match_parent 和具体值时,直接将 size 作为组件的宽度 width = size; } else if (mode == MeasureSpec.AT_MOST) { // 宽度为 wrap_content,宽度需要计算,此处为文字宽度 width = textWidth; } return width; } /** * 测量组件高度 * * @param heightMeasureSpec * @param textHeight * 文字所占高度 * @return */ private int measureHeight(int heightMeasureSpec, int textHeight) { int mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); int height = 0; if (mode == MeasureSpec.EXACTLY) { // 宽度为 match_parent 和具体值时,直接将 size 作为组件的高度 height = size; } else if (mode == MeasureSpec.AT_MOST) { // 高度为 wrap_content,高度需要计算,此处为文字高度 height = textHeight; } return height; } } 效果如下: logcat输出如下: 如图我们得到了top,ascent,descent,bottom和leading的值,因为只有一行文本所以leading恒为0。 从代码中可以发现在我们绘制文本之前我们便可以获取文本的FontMetrics属性值,也就是说我们FontMetrics的这些值跟我们要绘制什么文本是无关的,而仅与绘制文本Paint的size和typeface有关,我们来分别更改这两个值看看效果: paint.setTextSize(36); 如图所示所有值都改变了,我们再为Paint设置一个typeface: paint.setTypeface(Typeface.defaultFromStyle(Typeface.ITALIC)); 3、图形样式包含绘制的图形是空心样式还是实心样式,同时还能指定落笔和收笔时的笔触效 果。绘制直线或折线时,图形样式能影响到一些绘制细节。 Paint 类与图形样式相关的方法有: public void setStyle(Paint.Style style);//设置绘制的图形是空心样式还是实心样式,默认为实心样式,style 的可选值有: public static enum Style{ FILL, FILL_AND_STROKE, STROKE } 其中,FILL 表示实心样式,对于闭合图形来说,会用指定的颜色进行填充;STROKE 表 示空心样式,绘制时只有线条而无填充效果;FILL_AND_STROKE 表示同时使用实心样 式和空心样式。 public void setStrokeJoin(Paint.Join join) 当绘图样式为 STROKE 时,该方法用于指定线条连接处的拐角样式,能使绘制的图形 更加平滑。可选值如下: public static enum Join { BEVEL, MITER, ROUND } 我们通过下图来区别上面三个枚举值(比较右上角即可) ,默认值为 MITER: public void setStrokeCap(Paint.Cap cap) 该方法用于设置落笔时的样式,控制我们的画笔在离开画板时留下的最后一点图形,可选值如下: public static enum Cap { BUTT, ROUND, SQUARE } 我们通过下图来区别上面三个枚举值,默认值为 BUTT: public native void setStrokeWidth(float width) 设置线条的宽度,注意是 float 类型,在 Android 中最细的线条不是 1,可以比 1 更小更细。 参考:http://blog.csdn.net/abcdef314159/article/details/51720686