三、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、设置字体样式

java 复制代码
Typeface setTypeface(Typeface typeface)

typeface取值:Typeface.SANS_SERIFTypeface.MONOSPACETypeface.SERIF,但对中文支持不好,一般都不使用。

2、根据字体样式获取对应的默认字体

java 复制代码
Typeface defaultFromStyle(int style)

直接通过指定字体名来加载系统中自带的字体样式,如果字体样式不存在,则返回系统样式。

style取值如下:

  • Typeface.NORMAL: 正常字体。
  • Typeface.BOLD: 粗体。
  • Typeface.ITALIC: 斜体。
  • Typeface.BOLD_ITALIC: 粗斜体。

3、创建字体

3.1、获取系统自带的字体

java 复制代码
Typeface create(String familyName, int style)

3.2、获取应用Asset目录下的字体

java 复制代码
Typeface createFromAsset(AssetManager mgr, String path)

3.3、从字体文件中获得字体

java 复制代码
Typeface createFromFile(String path)
Typeface createFromFile(File path)

2.3、文字的测量

1、文字的外接矩形

java 复制代码
void getTextBounds(String text, int start, int end, Rect bounds)

注意,这个矩形是根据基线位置为(0,0)得到的

2、测量文字宽度

java 复制代码
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的位置关系如图所示:

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


3、高级

3.1、setShadowLayer

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

1、xml布局中设置shadowRadiusshadowDxshadowDyshadowColor这4个属性

2、调用TextView的setShadowLayer方法。

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

java 复制代码
void setShadowLayer(float radius, float dx, float dy, int shadowColor)

参数含义:

  • radius:阴影模糊半径
  • dxdy:阴影位置偏移
  • 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

java 复制代码
MaskFilter setMaskFilter(MaskFilter maskfilter)

MaskFilter有2个子类:

  • BlurMaskFilter:模糊效果
  • EmbossMaskFilter:浮雕效果

其中BlurMaskFilter构造函数

java 复制代码
public BlurMaskFilter(float radius, Blur style)

需要指定模糊半径,和模糊类型:

模糊类型 含义
NORMAL 内外都模糊
SOLID 内部直接绘制,外部模糊
OUTER 内部不绘制,外部模糊
INNER 模糊内部,外部不绘制

注意:使用BlurMaskFilter模糊Bitmap时,也有跟setShadowLayer一样的限制,只有当Bitmap不透明,才会有模糊效果。那如何生成一个带透明度的纯色阴影图片呢?

可通过设置BlurMaskFilter,再调用

java 复制代码
Bitmap extractAlpha(Paint paint, int[] offsetXY)

方法即可。

scss 复制代码
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

java 复制代码
setPathEffect(PathEffect effect)
java 复制代码
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

java 复制代码
Shader setShader(Shader shader)

参数:Shader 着色器对象,一般使用系统所提供的几个子类:

  • LinearGradient:线性渲染(霓虹灯文字,倒影图片)
  • RadialGradient:环形渲染(水波纹效果)
  • SweepGradient:扫描渲染(雷达扫描效果)
  • BitmapShader:位图渲染
  • ComposeShader:组合渲染,例如LinearGradient+BitmapShader
java 复制代码
public BitmapShader(Bitmap bitmap, TileMode tileX,  TileMode tileY)

TileMode取值如下:

TileMode取值 含义
TileMode.CLAMP 用边缘像素来填充多余空间
TileMode.REPEAT 重复原图像来填充多余空间
TileMode.MIRROR 使用镜像图像来填充多余空间

注意:填充顺序为先竖向填充,再横向填充,并且绘制是从左上角进行绘制,与画笔位置无关。

所以这会照成一些问题,如:

  • 1、单个图像不能充满整个屏幕
  • 2、单个图像不能在控件中部位置完整显示

解决方案为:Shader.setLocalMatrix


3.5、setColorFilter

java 复制代码
ColorFilter setColorFilter(ColorFilter filter)`

设置颜色过滤,一般使用ColorFilter的三个子类

arduino 复制代码
LightingColorFilter:光照效果
PorterDuffColorFilter:指定一个颜色和一种PorterDuff.Mode与绘制对象进行组合
ColorMatrixColorFilter:使用一个ColorMatrix来对颜色进行处理
    ColorMatrix类
        ColorMatrix.setScale //色度调节
        ColorMatrix.setSaturation //饱和度调节,0-无色彩,1-默认效果,>1饱和度加强
        ColorMatrix.setRotate //色调调节

常见效果:

  • 平移运算---加法
java 复制代码
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,
});
  • 反相效果 -- 底片效果
java 复制代码
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,
});
  • 缩放运算---乘法 -- 颜色增强
java 复制代码
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
java 复制代码
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,
});
  • 发色效果---(比如红色和绿色交换)
java 复制代码
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,
});
  • 复古效果
java 复制代码
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中的两张图片融合效果。

java 复制代码
Xfermode setXfermode(Xfermode xfermode)

Xfermode是一个空类,PorterDuffXfermode是其唯一的子类。

在介绍Xfermode之前,我们先介绍一下PorterDuff.Mode离屏渲染相关知识。

相关推荐
Estar.Lee4 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
温辉_xh5 小时前
uiautomator案例
android
工业甲酰苯胺6 小时前
MySQL 主从复制之多线程复制
android·mysql·adb
少说多做3436 小时前
Android 不同情况下使用 runOnUiThread
android·java
Estar.Lee7 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯8 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey9 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!11 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟12 小时前
Android音频采集
android·音视频
小白也想学C13 小时前
Android 功耗分析(底层篇)
android·功耗