三、Android绘制知识总结(画笔篇)

Paint(画笔),Android中绘制界面最常见的一个类,它的设计思路其实也遵从现实中画笔的定义:在画布(Canvas)上绘制内容的对象。
我们通过设置Paint相关属性,就可以在画布上绘制不同样式的图案。

1、常用API

函数 解释
void setColor(int color) 设置画笔颜色
void setStyle(Style style) 描边效果。
可选值包括FILLFILL_AND_STROKESTROKE
void setStrokeWidth(float width) 设置画笔宽度,单位是px。
只在Style为STROKEFILL_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) 设置文字对齐方式,包括LEFTCENTERRIGHT
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_SERIFTypeface.MONOSPACETypeface.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、文字的基线
文字的绘制位置,不是由文字左上角位置所决定的,而是由文字的TextAlignbaseline所控制的:

  • 当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的位置关系如图所示:

image.png

我们通过FontMetrics,能够很快的计算出baseLine注意,由于坐标系的关系,FontMetrics的bottom为正数,top为负数。


3、高级

3.1、setShadowLayer

如果需要给TextView的文字添加阴影,有两种方式:

1、xml布局中设置shadowRadiusshadowDxshadowDyshadowColor这4个属性
2、调用TextView的setShadowLayer方法。

这里我们介绍第二种,setShadowLayer可以在当前图层下方绘制一个阴影图层,并具有指定的偏移量和颜色以及模糊半径。

void setShadowLayer(float radius, float dx, float dy, int shadowColor)
参数含义:
radius:阴影模糊半径
dxdy:阴影位置偏移
shadowColor:阴影颜色

注意:设置setShadowLayer后,绘制Bitmap的阴影效果会有所不同(也跟系统版本相关):
1、如果Bitmap是彩色不透明图像,如由jpg图片获取的Bitmap

阴影由Bitmap模糊后加上shadowColor的alpha得到。

image.png

2、如果Bitmap是单色不透明图像,如由jpg图片获取的Bitmap,并调用Bitmap.extractAlpha()

阴影由shadowColor模糊而来。

image.png

3、如果Bitmap是彩色透明图像,如由png图片获取的Bitmap

1、阴影不会被模糊
2、阴影由Bitmap和shadowColor的alpha得到。

image.png

4、如果Bitmap是单色透明图像,如由png图片获取的Bitmap,并调用Bitmap.extractAlpha()

1、阴影不会被模糊
2、阴影颜色是shadowColor。

image.png

总结:

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 模糊内部,外部不绘制
image.png

注意:使用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);
        }
    }
}
image.png

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离屏渲染相关知识。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,312评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,578评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,337评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,134评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,161评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,303评论 1 280
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,761评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,421评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,609评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,450评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,504评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,194评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,760评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,836评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,066评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,612评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,178评论 2 341

推荐阅读更多精彩内容

  • 常用方法 构造方法 Paint mPaint = new Paint(); 重置画笔 mPaint.reset()...
    cain07阅读 472评论 0 7
  • 其实这边并不需要你记住太多,能有知道有这样的作用,让后不会使用的时候当工具来查看就看。主要是需要能灵活运用这些。下...
    帝王鲨kingcp阅读 682评论 0 2
  • 常用方法 构造方法 Paint mPaint = new Paint(); 重置画笔 mPaint.reset()...
    孤独的根号十二阅读 11,308评论 0 17
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,520评论 28 53
  • 信任包括信任自己和信任他人 很多时候,很多事情,失败、遗憾、错过,源于不自信,不信任他人 觉得自己做不成,别人做不...
    吴氵晃阅读 6,173评论 4 8