Paint(画笔),Android中绘制界面最常见的一个类,它的设计思路其实也遵从现实中画笔的定义:在画布(Canvas)上绘制内容的对象。
我们通过设置Paint相关属性,就可以在画布上绘制不同样式的图案。
1、常用API
函数 | 解释 |
---|---|
void setColor(int color) | 设置画笔颜色 |
void setStyle(Style style) | 描边效果。 可选值包括 FILL 、FILL_AND_STROKE 、STROKE
|
void setStrokeWidth(float width) | 设置画笔宽度,单位是px。 只在Style为 STROKE 和FILL_AND_STROKE 时有效 |
void setAntiAlias(boolean aa) | 抗锯齿,绘制不规则图形使用,如果绘制矩形、位图,就不需要打开 |
void setDither(boolean dither) | 抗抖动 |
setStrokeMiter(float miter) | |
void setStrokeCap(Cap cap) | 线帽风格。Cap.ROUND (圆形),Cap.SQUARE (方形),Cap.BUTT (无线帽)。 |
void setStrokeJoin(Join join) | 拐角风格。Join.MITER (锐角),Join.ROUND (圆弧),Join.BEVEL (直线) |
void setFilterBitmap(boolean filter) | 双线性滤波 |
注意:在Android系统在,颜色一般由一个int值表示:int color = (alpha<<24) | (red<<16) | (green<<8) | blue
2、文字相关API
2.1、文字相关函数
函数 | 解释 |
---|---|
void setTextSize(float textSize) | 设置字体大小 |
void setTextAlign(Align align) | 设置文字对齐方式,包括LEFT 、CENTER 、RIGHT
|
void setFakeBoldText(boolean) | 设置是否为粗体 |
void setUnderlineText(boolean) | 设置下划线 |
void setTextSkewX(float) | 设置字体水平倾斜度,普通斜体字设为-0.25 |
void setStrikeThruText(boolean) | 设置带有删除线效果 |
void setTextScaleX(2) | 水平缩放 |
2.2、字体相关函数
1、设置字体样式
Typeface setTypeface(Typeface typeface)
typeface取值:Typeface.SANS_SERIF
、Typeface.MONOSPACE
、Typeface.SERIF
,但对中文支持不好,一般都不使用。
2、根据字体样式获取对应的默认字体
Typeface defaultFromStyle(int style)
直接通过指定字体名来加载系统中自带的字体样式,如果字体样式不存在,则返回系统样式。style
取值如下:
Typeface.NORMAL
: 正常字体。
Typeface.BOLD
: 粗体。
Typeface.ITALIC
: 斜体。
Typeface.BOLD_ITALIC
: 粗斜体。
3、创建字体
3.1、获取系统自带的字体
Typeface create(String familyName, int style)
3.2、获取应用Asset目录下的字体
Typeface createFromAsset(AssetManager mgr, String path)
3.3、从字体文件中获得字体
Typeface createFromFile(String path)
Typeface createFromFile(File path)
2.3、文字的测量
1、文字的外接矩形
void getTextBounds(String text, int start, int end, Rect bounds)
注意,这个矩形是根据基线位置为(0,0)得到的
2、测量文字宽度
float measureText(char[] text, int index, int count)
float measureText(String text, int start, int end)
float measureText(String text)
float measureText(CharSequence text, int start, int end)
3、文字的基线
文字的绘制位置,不是由文字左上角位置所决定的,而是由文字的TextAlign
和baseline
所控制的:
- 当TextAlign为
LEFT时
(默认),绘制点为(x=文字开始位置,y=baseline)
- 当TextAlign为
CENTER
时,绘制点为(x=文字中心位置,y=baseline)
- 当TextAlign为
RIGHT
时,绘制点为(x=文字结束位置,y=baseline)
由此可见,绘制文字的关键,在于找到baseline
,这时就要用到Paint.getFontMetrics()
函数了,该函数返回一个FontMetrics
对象,其中包含以下变量:
变量 | 含义 |
---|---|
ascent | 字体最佳绘制区域顶部到baseline的距离 |
top | 字体最大绘制区域顶部到baseline的距离 |
descent | 字体最佳绘制区域底部到baseline的距离 |
bottom | 字体最大绘制区域底部到baseline的距离 |
leading | 上一行字符的descent到下一行的ascent之间的距离 |
它们与baseline
的位置关系如图所示:
我们通过FontMetrics
,能够很快的计算出baseLine
。注意,由于坐标系的关系,FontMetrics的bottom为正数,top为负数。
3、高级
3.1、setShadowLayer
如果需要给TextView的文字添加阴影,有两种方式:
1、xml布局中设置
shadowRadius
、shadowDx
、shadowDy
、shadowColor
这4个属性
2、调用TextView的setShadowLayer方法。
这里我们介绍第二种,setShadowLayer
可以在当前图层下方绘制一个阴影图层,并具有指定的偏移量和颜色以及模糊半径。
void setShadowLayer(float radius, float dx, float dy, int shadowColor)
参数含义:
radius
:阴影模糊半径
dx
和dy
:阴影位置偏移
shadowColor
:阴影颜色
注意:
设置setShadowLayer后,绘制Bitmap的阴影效果会有所不同(也跟系统版本
相关):
1、如果Bitmap是彩色不透明图像,如由jpg图片获取的Bitmap
阴影由Bitmap模糊后加上shadowColor的alpha得到。
2、如果Bitmap是单色不透明图像,如由jpg图片获取的Bitmap,并调用Bitmap.extractAlpha()
阴影由shadowColor模糊而来。
3、如果Bitmap是彩色透明图像,如由png图片获取的Bitmap
1、阴影不会被模糊
2、阴影由Bitmap和shadowColor的alpha得到。
4、如果Bitmap是单色透明图像,如由png图片获取的Bitmap,并调用Bitmap.extractAlpha()
1、阴影不会被模糊
2、阴影颜色是shadowColor。
总结:
1、如果图片带有透明通道,阴影将不会被模糊,并受shadowColor的alpha值影响。
2、如果图片是单色,阴影将由shadowColor决定。
当不再需要阴影时,可以通过将radius设置为0,或者调用clearShadowLayer
来清除阴影。
3.2、setMaskFilter
MaskFilter setMaskFilter(MaskFilter maskfilter)
MaskFilter有2个子类:
- BlurMaskFilter:模糊效果
- EmbossMaskFilter:浮雕效果
其中BlurMaskFilter构造函数
public BlurMaskFilter(float radius, Blur style)
需要指定模糊半径,和模糊类型:
模糊类型 | 含义 |
---|---|
NORMAL | 内外都模糊 |
SOLID | 内部直接绘制,外部模糊 |
OUTER | 内部不绘制,外部模糊 |
INNER | 模糊内部,外部不绘制 |
注意
:使用BlurMaskFilter
模糊Bitmap时,也有跟setShadowLayer
一样的限制,只有当Bitmap不透明,才会有模糊效果。那如何生成一个带透明度的纯色阴影图片呢?
可通过设置BlurMaskFilter,再调用Bitmap extractAlpha(Paint paint, int[] offsetXY)
方法即可。
public class ShadowView extends View {
private Paint mPaint = new Paint();
private Bitmap mAlphaBitmap;
public ShadowView(Context context) {
super(context);
setLayerType(LAYER_TYPE_SOFTWARE, null);
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(40);
// mPaint.setShadowLayer(10, 10, 10, Color.argb(255, 255, 0, 0));
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.cat_dog);
mAlphaBitmap = Bitmap.createScaledBitmap(bitmap, 400, 400, true).extractAlpha(mPaint, new int[]{10, 10});
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.drawText("青云", 140, 200, mPaint);
canvas.drawCircle(150, 400, 50, mPaint);
canvas.saveLayerAlpha(new RectF(400, 50, 400 + mAlphaBitmap.getWidth(), 50 + +mAlphaBitmap.getHeight()), 100);
canvas.drawBitmap(mAlphaBitmap, 400, 50, mPaint);
canvas.restore();
}
}
3.3、setPathEffect
setPathEffect(PathEffect effect)
public class EffectView extends View {
private PathEffect[] mEffect = new PathEffect[6];
private Path mPath = new Path();
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public EffectView(Context context) {
super(context);
for (int i = 0; i <= 30; i++) {
mPath.lineTo(i * 35, (float) (Math.random() * 100));
}
mEffect[0] = null;
mEffect[1] = new CornerPathEffect(30);
mEffect[2] = new DiscretePathEffect(3.0F, 5.0F);
mEffect[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);
Path path = new Path();
path.addRect(0, 0, 8, 8, Path.Direction.CCW);
mEffect[4] = new PathDashPathEffect(path, 12, 0, PathDashPathEffect.Style.ROTATE);
mEffect[5] = new ComposePathEffect(mEffect[3], mEffect[1]);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
for (PathEffect pathEffect : mEffect) {
mPaint.setPathEffect(pathEffect);
canvas.drawPath(mPath, mPaint);
canvas.translate(0, 150);
}
}
}
3.4、setShader
Shader setShader(Shader shader)
参数:Shader 着色器对象,一般使用系统所提供的几个子类:
LinearGradient
:线性渲染(霓虹灯文字,倒影图片)
RadialGradient
:环形渲染(水波纹效果)
SweepGradient
:扫描渲染(雷达扫描效果)
BitmapShader
:位图渲染
ComposeShader
:组合渲染,例如LinearGradient+BitmapShader
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY)
TileMode取值如下:
TileMode取值 | 含义 |
---|---|
TileMode.CLAMP | 用边缘像素来填充多余空间 |
TileMode.REPEAT | 重复原图像来填充多余空间 |
TileMode.MIRROR | 使用镜像图像来填充多余空间 |
注意:填充顺序为先竖向填充,再横向填充,并且绘制是从左上角进行绘制,与画笔位置无关。
所以这会照成一些问题,如:
1、单个图像不能充满整个屏幕
2、单个图像不能在控件中部位置完整显示
解决方案为:Shader.setLocalMatrix
3.5、setColorFilter
ColorFilter setColorFilter(ColorFilter filter)
设置颜色过滤,一般使用ColorFilter的三个子类
LightingColorFilter:光照效果
PorterDuffColorFilter:指定一个颜色和一种PorterDuff.Mode与绘制对象进行组合
ColorMatrixColorFilter:使用一个ColorMatrix来对颜色进行处理
ColorMatrix类
ColorMatrix.setScale //色度调节
ColorMatrix.setSaturation //饱和度调节,0-无色彩,1-默认效果,>1饱和度加强
ColorMatrix.setRotate //色调调节
常见效果:
- 平移运算---加法
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 100,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
});
- 反相效果 -- 底片效果
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
-1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0,
});
- 缩放运算---乘法 -- 颜色增强
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1.2f, 0, 0, 0, 0,
0, 1.2f, 0, 0, 0,
0, 0, 1.2f, 0, 0,
0, 0, 0, 1.2f, 0,
});
- 黑白照片
将三通道变为单通道的灰度模式
原理:只要把R G B 三通道的色彩信息设置成一样,那么图像就会变成灰色,同时为了保证图像亮度不变,同一个通道里的R+G+B =1
ColorMatrix colorMartrix = 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,
});
- 发色效果---(比如红色和绿色交换)
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 0,
0, 0, 1, 0, 0,
0, 1, 0, 0, 0,
0, 0, 0, 0.5F, 0,
});
- 复古效果
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1 / 2f, 1 / 2f, 1 / 2f, 0, 0,
1 / 3f, 1 / 3f, 1 / 3f, 0, 0,
1 / 4f, 1 / 4f, 1 / 4f, 0, 0,
0, 0, 0, 1, 0,
});
3.6、setXfermode
混合模式是Paint绘图中最难的部分,它能够将两张图片无缝结合,实现类似Photoshop中的两张图片融合效果。
Xfermode setXfermode(Xfermode xfermode)
Xfermode是一个空类,PorterDuffXfermode
是其唯一的子类。
在介绍Xfermode之前,我们先介绍一下PorterDuff.Mode
和离屏渲染
相关知识。