Android自定义控件入门到精通--Paint基础知识

《Android自定义控件入门到精通》文章索引 ☞ https://blog.csdn.net/Jhone_csdn/article/details/118146683

《Android自定义控件入门到精通》所有源码 ☞ https://gitee.com/zengjiangwen/Code

Paint基础知识

paint画笔,跟我们Ps中的画笔有点类似,比如Ps中的画笔工具和图案图章工具。

常用基础函数

我们先来系统的了解下Paint的一些基础的函数:

mPaint.setColor(Color.RED);//设置画笔颜色
mPaint.setTextAlign(Paint.Align.CENTER);//设置文本与基点的对其方式
mPaint.setAntiAlias(true);//设置是否抗锯齿
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);//添加画笔特性标识
mPaint.setDither(true);//设置是否防抖动
mPaint.setAlpha(100);//设置画笔的透明度0~255
mPaint.setARGB(55,100,100,100);//设置画笔颜色和透明度
mPaint.reset();//恢复画笔到默认状态
mPaint.getFontMetrics();//可以获取到ascent descent top bottom值
mPaint.ascent();//ascent值
mPaint.descent();//descent值
mPaint.setTextSize(24);//字体大小,通常会做sp适配
mPaint.setLetterSpacing(0.3f);//字间距,默认是0,Typical values for slight 0.05
mPaint.measureText("test");//获取文本的宽度
mPaint.setUnderlineText(true);//是否添加下划线
mPaint.setStrikeThruText(true);//是否添加删除线
mPaint.setHinting(Paint.HINTING_OFF);//设置画笔的Hint模式(没看出有啥效果)
mPaint.setFakeBoldText(true);//设置伪粗体,有些字体是没有粗体的,可以用伪粗体模拟粗体字的效果
mPaint.setTextScaleX(2f);//放大缩小文本
mPaint.setTextSkewX(2f);//倾斜文本,一般取值-0.25
mPaint.setTextLocale(Locale.CHINA);//指定语言
mPaint.getTextBounds("test",0,3,rect);//获取文本的边界

这些方法我们前面可能接触过一些,没啥难度,看注释应该就能很好的理解了,主要讲下下面这几个基础方法

mPaint.setStyle(Paint.Style.FILL);//描边,填充,描边和填充
mPaint.setStrokeWidth(10);//设置线宽,当Paint.Style.STROKE时有效
mPaint.setStrokeCap(Paint.Cap.ROUND);//设置线帽样式
mPaint.setShadowLayer(0, 0, 0, 0);//设置阴影效果
mPaint.clearShadowLayer();//清除阴影效果
mPaint.breakText();//测量
mPaint.setTypeface();//设置字体
mPaint.setStrokeJoin();//设置转折样式
mPaint.setMaskFilter();//设置滤镜

Paint.setStyle

设置画笔的样式,描边、填充、描边并填充

@Override
protected void onDraw(Canvas canvas) {
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(10);
    mPaint.setColor(Color.RED);
    canvas.drawRect(new Rect(40,20,100,60),mPaint);

    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(2);
    mPaint.setColor(Color.GREEN);
    canvas.drawRect(new Rect(40,20,100,60),mPaint);

    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawText("Paint.Style.STROKE",160,40,mPaint);

    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setStrokeWidth(10);
    mPaint.setColor(Color.RED);
    canvas.drawRect(new Rect(40,120,100,160),mPaint);

    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(2);
    mPaint.setColor(Color.GREEN);
    canvas.drawRect(new Rect(40,120,100,160),mPaint);

    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawText("Paint.Style.FILL",160,140,mPaint);

    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    mPaint.setStrokeWidth(10);
    mPaint.setColor(Color.RED);
    canvas.drawRect(new Rect(40,220,100,260),mPaint);

    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(2);
    mPaint.setColor(Color.GREEN);
    canvas.drawRect(new Rect(40,220,100,260),mPaint);

    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawText("Paint.Style.FILL_AND_STROKE",160,240,mPaint);
}

在这里插入图片描述

Paint.setStrokeCap

设置线帽(两端)样式

@Override
protected void onDraw(Canvas canvas) {
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(20);
    mPaint.setColor(Color.RED);

    mPaint.setStrokeCap(Paint.Cap.ROUND);
    canvas.drawLine(40,40,160,40,mPaint);

    mPaint.setStrokeCap(Paint.Cap.SQUARE);
    canvas.drawLine(40,140,160,140,mPaint);

    mPaint.setStrokeCap(Paint.Cap.BUTT);
    canvas.drawLine(40,240,160,240,mPaint);

    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawText("Paint.Cap.ROUND",200,50, mPaint);
    canvas.drawText("Paint.Cap.SQUARE",200,150, mPaint);
    canvas.drawText("Paint.Cap.BUTT",200,250, mPaint);

    mPaint.setColor(Color.GREEN);
    mPaint.setStrokeWidth(2);
    canvas.drawLine(40,30,40,250,mPaint);
    canvas.drawLine(160,30,160,250,mPaint);
}

在这里插入图片描述

Paint.setTypeface

设置字体样式

Typeface构造方式:

  • Typeface create(String familyName, int style) //直接通过指定字体名来加载系统中自带的文字样式
  • Typeface create(Typeface family, int style) //通过其它Typeface变量来构建文字样式
  • Typeface createFromAsset(AssetManager mgr, String path) //通过从Asset中获取外部字体来显示字体样式
  • Typeface createFromFile(String path)//直接从路径创建
  • Typeface createFromFile(File path)//从外部路径来创建字体样式
  • Typeface defaultFromStyle(int style)//创建默认字体

自带的字体(Typeface)

    /** The default NORMAL typeface object */
    public static final Typeface DEFAULT;
    /**
     * The default BOLD typeface object. Note: this may be not actually be
     * bold, depending on what fonts are installed. Call getStyle() to know
     * for sure.
     */
    public static final Typeface DEFAULT_BOLD;
    /** The NORMAL style of the default sans serif typeface. */
    public static final Typeface SANS_SERIF;
    /** The NORMAL style of the default serif typeface. */
    public static final Typeface SERIF;
    /** The NORMAL style of the default monospace typeface. */
    public static final Typeface MONOSPACE;

Style(字体样式)的枚举值如下:

// Style
public static final int NORMAL = 0;//正常体
public static final int BOLD = 1;//粗体
public static final int ITALIC = 2; //斜体
public static final int BOLD_ITALIC = 3; //粗斜体

示例:

@Override
protected void onDraw(Canvas canvas) {
    mPaint.setColor(Color.WHITE);
    mPaint.setTextSize(30);
    mPaint.setStyle(Paint.Style.FILL);

    String text="Android字体演示";
    int destant=50;
    int position=100;

    AssetManager mgr=getContext().getAssets();
    Typeface face0= Typeface.createFromAsset(mgr, "jzk.ttf");//简中楷字体
    mPaint.setTypeface(face0);
    canvas.drawText(text,50,position, mPaint);

    Typeface face1 = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
    mPaint.setTypeface(face1);
    canvas.drawText(text,50,position+destant, mPaint);

    Typeface face2 = Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD);
    mPaint.setTypeface(face2);
    canvas.drawText(text,50,position+destant*2, mPaint);

    Typeface face3 = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
    mPaint.setTypeface(face3);
    canvas.drawText(text,50,position+destant*3, mPaint);

    Typeface face4 = Typeface.create(Typeface.SERIF, Typeface.NORMAL);
    mPaint.setTypeface(face4);
    canvas.drawText(text,50,position+destant*4, mPaint);

    Typeface face5 = Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL);
    mPaint.setTypeface(face5);
    canvas.drawText(text,50,position+destant*5, mPaint);

    Typeface face6 = Typeface.create(Typeface.MONOSPACE, Typeface.BOLD_ITALIC);
    mPaint.setTypeface(face6);
    canvas.drawText(text,50,position+destant*6, mPaint);
}

在这里插入图片描述

Paint.breakText

用途:给定宽度的情况下,看看能放下多少内容,多用于配合文本的截取功能

@Override
protected void onDraw(Canvas canvas) {
    mPaint.setColor(Color.RED);
    mPaint.setTextSize(16);
    mPaint.setStyle(Paint.Style.FILL);
    //需要测量的字符串
    String text="安卓程序员";
    //用来接受测量结果,测量结果回放在widths[0]
    float widths[]=new float[1];

    //给定40的宽度,看看能放下几个字,这几个字的宽度为多少?
    float maxWidth=40;
    int index=mPaint.breakText(text,true,maxWidth,widths);
    String log="给定宽度 "+maxWidth+" 时,能放下 "+index+" 个字,这 "+index+" 个字的宽度为 "+widths[0]+"  ,整个字符串的宽度为 "+mPaint.measureText(text);
    canvas.drawText(log,50,50,mPaint);

    float maxWidth1=60;
    int index1=mPaint.breakText(text,true,maxWidth1,widths);
    String log1="给定宽度 "+maxWidth1+" 时,能放下 "+index1+" 个字,这 "+index1+" 个字的宽度为 "+widths[0]+"  ,整个字符串的宽度为 "+mPaint.measureText(text);
    canvas.drawText(log1,50,100,mPaint);

    float maxWidth2=200;
    int index2=mPaint.breakText(text,true,maxWidth2,widths);
    String log2="给定宽度 "+maxWidth2+" 时,能放下 "+index2+" 个字,这 "+index2+" 个字的宽度为 "+widths[0]+"  ,整个字符串的宽度为 "+mPaint.measureText(text);
    canvas.drawText(log2,50,150,mPaint);
}

在这里插入图片描述

Paint.setStrokeJoin

设置拐角样式

@Override
protected void onDraw(Canvas canvas) {
    mPaint.setColor(Color.RED);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(20);

    //写文字的画笔
    Paint textPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
    textPaint.setStyle(Paint.Style.FILL);
    textPaint.setTextSize(14);
    textPaint.setColor(Color.WHITE);

    Path path1=new Path();
    path1.moveTo(20,20);
    path1.lineTo(100,20);
    path1.lineTo(100,60);

    mPaint.setStrokeJoin(Paint.Join.ROUND);
    canvas.drawPath(path1,mPaint);
    canvas.drawText("ROUND",120,40,textPaint);

    canvas.translate(0,60);

    mPaint.setStrokeJoin(Paint.Join.BEVEL);
    canvas.drawPath(path1,mPaint);
    canvas.drawText("BEVEL",120,40,textPaint);

    canvas.translate(0,60);

    mPaint.setStrokeJoin(Paint.Join.MITER);
    canvas.drawPath(path1,mPaint);
    canvas.drawText("MITER",120,40,textPaint);
}

在这里插入图片描述

Paint.setMaskFilter

有两个实现类:

  • BlurMaskFilter
  • EmbossMaskFilter

BlurMaskFilter

BlurMaskFilter(float radius, Blur style)

设置边界模糊效果

  • radius:模糊半径
  • style:模糊样式(NORMAL、SOLID、OUTER、INNER)

示例:

@Override
protected void onDraw(Canvas canvas) {

    setLayerType(View.LAYER_TYPE_SOFTWARE,null);//关闭硬件加速

    Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.parseColor("#ff0000"));
    mPaint.setColor(Color.RED);
    mPaint.setStyle(Paint.Style.FILL);

    //写文字的画笔
    Paint textPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
    textPaint.setStyle(Paint.Style.FILL);
    textPaint.setTextSize(14);
    textPaint.setColor(Color.WHITE);

    int radius=40;
    int blurRadius=20;

    mPaint.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.OUTER));
    canvas.drawCircle(60,60,radius,mPaint);
    canvas.drawText("Blur.OUTER",140,60,textPaint);

    canvas.translate(0,100);

    mPaint.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.INNER));
    canvas.drawCircle(60,60,radius,mPaint);
    canvas.drawText("Blur.INNER",140,60,textPaint);

    canvas.translate(0,100);

    mPaint.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.SOLID));
    canvas.drawCircle(60,60,radius,mPaint);
    canvas.drawText("Blur.SOLID",140,60,textPaint);

    canvas.translate(0,100);

    mPaint.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.NORMAL));
    canvas.drawCircle(60,60,radius,mPaint);
    canvas.drawText("Blur.NORMAL",140,60,textPaint);
}

在这里插入图片描述

一定要关闭硬件加速,否则无效果

硬件加速

硬件加速的主要原理,就是通过底层软件代码,将CPU不擅长的图形计算转换成GPU专用指令,由GPU完成。

  • Android3.0(API level 11)开始,2D渲染管道支持硬件加速,也就是说绘制操作可以使用GPU绘制在View的canvas上。使用硬件加速需要更多的资源,所以app会消耗更多内存。

  • 硬件加速在Target API >= 14时是默认开启的。

  • 硬件加速还不支持所有的2D绘图命令,开启后可能会影响自定义View和绘图操作。异常通常是不可见元素、运行异常、或者错误的像素点。

  • 如果只使用系统的View和Drawable,则没有任何副作用。

  • 如果app里有自定义的绘图操作,需要在开启硬件加速的设备上测试来发现问题。

总之一句话,不是所有的绘图方法都支持硬件加速,Android默认开启使用了硬件加速,当某些绘图方法不支持,就要关闭硬件加速功能,才能实现效果

关闭打开硬件加速有四种方法:

全局级别关闭(可打开可关闭)

<application
...
android:hardwareAccelerated="false"
...
>

Activity级别关闭(可打开可关闭)

<activity
    android:name=".MainActivity"
    android:hardwareAccelerated="false">

Window级别(只能打开硬件加速)

getWindow().setFlags(
  WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
  WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

View级别(只能关闭硬件加速)

view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

一般我们只在View级别关闭即可

EmbossMaskFilter

类似浮雕效果

EmbossMaskFilter(float[] direction,float ambient, float specular, float blurRadius)

  • direction:指定光源的方向,由3个量构成的[x,y,z]设置
  • ambient:背景光的亮度,取值区间[0,1],决定背景的明暗程度
  • specular:高光系数,值越小反射越强,那么亮度也相对偏亮
  • blurRadius:阴影延伸半径(模糊半径)

示例:

@Override
protected void onDraw(Canvas canvas) {

    setLayerType(View.LAYER_TYPE_SOFTWARE,null);//关闭硬件加速

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(Color.RED);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(30);
    paint.setTextSize(200);
    float[] direction = new float[] { 1, 1, 0 };
    // 环境光亮度
    float light = 0.3f;
    // 反射等级
    float specular = 5;
    //模糊
    float blur = 8;
    EmbossMaskFilter embossMaskFilter = new EmbossMaskFilter(direction, light, specular, blur);

    paint.setMaskFilter(embossMaskFilter);

    canvas.drawText("好",100,200,paint);
}

在这里插入图片描述

direction光源方向,[x,y,z]:

  • x为正,光源在水平方向的左边,反之,x为负,光源在右边
  • y为正,光源在垂直方向的上边,反之,y为负,光源在下边
  • z为正,光源在z轴方向屏幕外边,反正,z为负,光源在屏幕里边

Paint.setShadowLayer

设置阴影效果

Paint.setShadowLayer(radius,dx,dy, int shadowColor)

  • radius 阴影模糊半径,越大越模糊
  • dx 阴影在X轴上的偏移量
  • dy 阴影在Y轴上的偏移量
  • shadowColor 阴影颜色

示例:

@Override
protected void onDraw(Canvas canvas) {
    setLayerType(View.LAYER_TYPE_SOFTWARE,null);
    Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(Color.RED);
    paint.setTextSize(80);
    paint.setShadowLayer(10,10,5, Color.BLACK);
    canvas.drawText("看到阴影效果了吗?",20,120,paint);
}

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一鱼浅游

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值