自定义控件点滴3(字体相关)

笔对于我们来说第一印象一定是能写字对吧,而Android给我们的这支Paint当然也不例外,它也定义了大量关于“写字”的功能,这些方法总数接近Paint的一半!可见Android对Paint写字功能的重视,在讲Paint提供的“写字”方法前我先给大家说一个Android中和字体相关的很重要的类FontMetrics。
FontMetrics意为字体测量,FontMetrics其实是Paint的一个内部类,而它里面呢就定义了top,ascent(上升),descent(下降),bottom,leading(行距)五个成员变量其他什么也没有
这里写图片描述
首先我们要知道Baseline基线,在Android中,文字的绘制都是从Baseline处开始的,Baseline往上至字符最高处的距离我们称之为ascent(上坡度),Baseline往下至字符最底处的距离我们称之为descent(下坡度),而leading(行间距)则表示上一行字符的descent到该行字符的ascent之间的距离,top和bottom文档描述地很模糊,其实这里我们可以借鉴一下TextView对文本的绘制,TextView在绘制文本的时候总会在文本的最外层留出一些内边距,为什么要这样做?因为TextView在绘制文本的时候考虑到了类似读音符号,可能大家很久没写过拼音了已经忘了什么叫读音符号了吧。

canvas.drawText(TEXT, 0, Math.abs(mFontMetrics.top), mPaint); 

Android中文本的绘制是从Baseline开始的,在屏幕上的体现便是Y轴坐标,所以在代码中我们将文本绘制的起点Y坐标向下移动Math.abs(mFontMetrics.top)个单位(注:mFontMetrics.top是负数),相当于把文本的Baseline向下移动Math.abs(mFontMetrics.top)个单位,此时文本的顶部刚好会和屏幕顶部重合。

我们绘制文本之前我们便可以获取文本的FontMetrics属性值,也就是说我们FontMetrics的这些值跟我们要绘制什么文本是无关的,而仅与绘制文本Paint的size和typeface有关

把字体垂直居中

canvas.drawText(TEXT, 0, canvas.getHeight()/2+(Math.abs(fontMetrics.ascent)-Math.abs(fontMetrics.descent))/2, paint);  

canvas.getHeight()获取的是自定义View的高度(单位px),如果设置为wrap_content获取的是屏幕的高度

由于字体绘制canvas.drawText(text, x, y, paint)的y是baseline基线的坐标,因此如果y=canvas.getHeight()/2的话,由于fontMetrics.ascent>fontMetrics.descent,所以字体位置偏上,因此我们应该先在ascent的高度中减去descent的高度再除以二再让屏幕的中点Y坐标(也就是高度的一半)加上这个偏移值。

绘制文字垂直居中的自定义控件:

public class CustomText extends View{
private Paint paint;
private static final String TEXT ="ap爱哥ξτβбпшㄎㄊ";
private FontMetrics fontMetrics;
    public CustomText(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    public CustomText(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    private void initPaint() {
        // TODO Auto-generated method stub
        paint=new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(50);
        paint.setColor(Color.BLACK);
        fontMetrics=paint.getFontMetrics();
         Log.d("Aige", "ascent:" + fontMetrics.ascent);  
            Log.d("Aige", "top:" + fontMetrics.top);  
            Log.d("Aige", "leading:" + fontMetrics.leading);  
            Log.d("Aige", "descent:" + fontMetrics.descent);  
            Log.d("Aige", "bottom:" + fontMetrics.bottom);  
    }
    @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);
            Log.d("canvasw", canvas.getWidth()+"");
            Log.d("canvash", canvas.getHeight()+"");
             canvas.drawText(TEXT, 0, canvas.getHeight()/2+(Math.abs(fontMetrics.ascent)-Math.abs(fontMetrics.descent))/2, paint);  
    }
}

文字换行:

Paint有一个唯一的子类TextPaint就是专门为文本绘制量身定做的“笔”,而这支笔就如API所描述的那样能够在绘制时为文本添加一些额外的信息,这些信息包括:baselineShift,bgColor,density,drawableState,linkColor,这些属性都很简单大家顾名思义或者自己去尝试下即可这里就不多说了,那么这支笔有何用呢?最常用的用法是在绘制文本时能够实现换行绘制!在正常情况下Android绘制文本是不能识别换行符之类的标识符的,这时候如果我们想实现换行绘制就得另辟途径使用StaticLayout结合TextPaint实现换行,StaticLayout是android.text.Layout的一个子类,很明显它也是为文本处理量身定做的,其内部实现了文本绘制换行的处理

staticLayout = new StaticLayout(TEXT, paint, canvas.getWidth(),
                Alignment.ALIGN_NORMAL, 1.0F, 0.0F, false);
        staticLayout.draw(canvas);
        canvas.restore();

Paint提供的一些绘制文本的方法:

ascent() //顾名思义就是返回上坡度的值
descent() //返回下坡度的值
getFontMetrics() //返回FontMetrics对象
getFontMetrics (Paint.FontMetrics metrics) //返回的是文本的行间距
getFontSpacing() //返回字符行间距
setUnderlineText(boolean underlineText) //设置下划线

setTypeface(Typeface typeface)
设置字体类型,Android中字体有四种样式:BOLD(加粗),BOLD_ITALIC(加粗并倾斜),ITALIC(倾斜),NORMAL(正常);而其为我们提供的字体有五种:DEFAULT,DEFAULT_BOLD,MONOSPACE,SANS_SERIF和SERIF,但是系统给我们的字体有限我们可不可以使用自己的字体呢?答案是肯定的!Typeface这个类中给我们提供了多个方法去个性化我们的字体

create(String familyName, int style)和create(Typeface family, int style)
把几种样式整合在一起。

textPaint.setTypeface(Typeface.create("SERIF", Typeface.NORMAL));  
textPaint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));  

这两种方法效果是一样的。

createFromAsset(AssetManager mgr, String path)、createFromFile(String path)和createFromFile(File path)
这三者也是一样的,它们都允许我们使用自己的字体比如我们从asset目录读取一个字体文件:

// 获取字体并设置画笔字体  
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "kt.ttf");  
textPaint.setTypeface(typeface);

修改TextView的字体为我们想要的字体

tv=(TextView) findViewById(R.id.tv);
Typeface typeface = Typeface.createFromAsset(getAssets(), "kt.ttf");  
tv.setTypeface(typeface);

setTextSkewX(float skewX) //设置文本在水平方向上的倾斜
官方推崇的值为-0.25可以得到比较好的倾斜文本效果,值为负右倾值为正左倾,默认值为0

setTextScaleX (float scaleX)
将文本沿X轴水平缩放,默认值为1,当值大于1会沿X轴水平放大文本,当值小于1会沿X轴水平缩放文本
不仅放大了文本宽度同时还拉伸了字符!这是亮点~~

setTextAlign (Paint.Align align)
设置文本的对其方式,可供选的方式有三种:CENTER,LEFT和RIGHT,其实从这三者的名字上看我们就知道其意思,但是问题是这玩意怎么用的?好像没什么用啊……我们的文本大小是通过size和typeface确定的(其实还有其他的因素但这里影响不大忽略~~),一旦baseline确定,对不对齐好像不相干吧……但是,你要知道一点,文本的绘制是从baseline开始没错,但是是从哪边开始绘制的呢?左端还是右端呢?而这个Align就是为我们定义在baseline绘制文本究竟该从何处开始。

setStrikeThruText (boolean strikeThruText) //文本删除线

measureText (String text) //测量文本宽度

setDither(boolean dither) //我们在绘制图像时的抗抖动,也称为递色
两种颜色交接的地方有一些色块之类的东西感觉很不柔和,因为在RGB模式下只能显示2^16=65535种色彩,因此很多丰富的色彩变化无法呈现,而Android呢为我们提供了抗抖动这么一个方法,它会将相邻像素之间颜色值进行一种“中和”以呈现一个更细腻的过渡色

setMaskFilter(MaskFilter maskfilter)
MaskFilter类中没有任何实现方法,而它有两个子类BlurMaskFilter和EmbossMaskFilter,前者为模糊遮罩滤镜(比起称之为过滤器哥更喜欢称之为滤镜)而后者为浮雕遮罩滤镜,我们先来看第一个

BlurMaskFilter

控件都有类似软阴影(周围就有一圈很淡的阴影效果)的效果,使用BlurMaskFilter 可以实现该效果。

 // 实例化画笔  
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  
        mPaint.setStyle(Paint.Style.FILL);  
        mPaint.setColor(0xFF603811);  

        // 设置画笔遮罩滤镜  
        mPaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.SOLID));
        @SuppressLint("NewApi")
    @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);
            setLayerType(LAYER_TYPE_SOFTWARE, null); 
            canvas.drawRect(50, 50, 200, 200, paint);
    }

该效果不支持硬件加速,所以setLayerType(LAYER_TYPE_SOFTWARE, null); 关闭硬件加速
BlurMaskFilter只有一个含参的构造函数BlurMaskFilter(float radius, BlurMaskFilter.Blur style),其中radius很容易理解,值越大我们的阴影越扩散;第二个参数style表示的是模糊的类型,上面我们用到的是SOLID,其效果就是在图像的Alpha边界外产生一层与Paint颜色一致的阴影效果而不影响图像本身,除了SOLID还有三种,NORMAL,OUTER和INNER;
NORMAL会将整个图像模糊掉:
OUTER会在Alpha边界外产生一层阴影且会将原本的图像变透明:
NNER则会在图像内部产生模糊:INNER效果其实并不理想,实际应用中我们使用的也少,我们往往会使用混合模式和渐变和获得更完美的内阴影效果。

EmbossMaskFilter

可以实现一种类似浮雕的效果,说白了就是让你绘制的图像感觉像是从屏幕中“凸”起来更有立体感一样(在设计软件中类似的效果称之为斜面浮雕)。

setPathEffect(PathEffect effect) 路径效果

PathEffect跟上面的很多类一样没有具体的实现,但是其有六个子类:CornerPathEffect、DiscretePathEffect、DashPathEffect、PathDashPathEffect、ComposePathEffect、SumPathEffect其效果如下:
这里写图片描述

当我们不设置路径效果的时候路径的默认效果就如上图第一条线那样直的转折生硬;
CornerPathEffect则可以将路径的转角变得圆滑如图第二条线的效果,CornerPathEffect的构造方法只接受一个参数radius,意思就是转角处的圆滑程度;
DiscretePathEffect离散路径效果,其会在路径上绘制很多“杂点”的突出来模拟一种类似生锈铁丝的效果如上图第三条线,其构造方法有两个参数,第一个呢指定这些突出的“杂点”的密度,值越小杂点越密集,第二个参数呢则是“杂点”突出的大小,值越大突出的距离越大反之反之
DashPathEffect虚线路径效果,第一个参数是一个浮点型的数组,我们在定义该参数的时候只要浮点型数组中元素个数大于等于2即可,也就是说上面我们的代码可以写成这样的:

mEffects[3] = new DashPathEffect(new float[] {20, 10, 50, 5, 100, 30, 10, 5}, mPhase); 

float[] {20, 10}的偶数参数20(注意数组下标是从0开始哦)定义了我们第一条实线的长度,而奇数参数10则表示第一条虚线的长度,如果此时数组后面不再有数据则重复第一个数以此往复循环,比如我们20,10后没数了,那么整条线就成了[20,10,20,10,20,10…………………………]这么一个状态,当然如果你无聊,也可以:

mEffects[3] = new DashPathEffect(new float[] {20, 10, 50, 5, 100, 30, 10, 5}, mPhase);

而DashPathEffect的第二个参数我称之为偏移值,动态改变其值会让路径产生动画的效果

PathDashPathEffect和DashPathEffect是类似的,不同的是PathDashPathEffect可以让我们自己定义路径虚线的样式,比如我们将其换成一个个小圆组成的虚线:

Path path = new Path();  
path.addCircle(0, 0, 3, Direction.CCW);  
mEffects[4] = new PathDashPathEffect(path, 12, mPhase, PathDashPathEffect.Style.ROTATE);

setStrokeCap(Paint.Cap cap);
该方法用来设置我们画笔的笔触风格,什么叫笔触呢,其实很简单,就像我们现实世界中的笔,如果你用圆珠笔在纸上戳一点,那么这个点一定是个圆,即便很小,它代表了笔的笔触形状; mPaint.setStrokeCap(Paint.Cap.ROUND);

setStrokeJoin(Paint.Join join)
设置结合处的形态, mPaint.setStrokeJoin(Paint.Join.ROUND);

setShadowLayer(float radius, float dx, float dy, int shadowColor)
该方法为我们绘制的图形添加一个阴影层效果:
radius表示阴影的扩散半径,而dx和dy表示阴影平面上的偏移值,shadowColor就不说了阴影颜色,最后提醒一点setShadowLayer同样不支持HW哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值