Android开发——自定义view之文字绘制

Android开发——自定义view之文字绘制,第1张

概述首先新建文件MyTextView,继承AppCompatTextView,并重写onDraw方法:publicclassMyTextViewextendsAppCompatTextView{/***需要绘制的文字*/privateStringmText;/***文本的颜色*/privateintmTextColor;/***文本 首先新建文件MyTextVIEw,继承AppCompatTextVIEw,并重写onDraw方法:
public class MyTextVIEw extends AppCompatTextVIEw {    /**     * 需要绘制的文字     */    private String mText;    /**     * 文本的颜色     */    private int mTextcolor;    /**     * 文本的大小     */    private int mTextSize;    private Paint mPaint;    public MyTextVIEw(Context context) {        super(context);    }    public MyTextVIEw(Context context, AttributeSet attrs) {        super(context, attrs);    }    public MyTextVIEw(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @OverrIDe    protected voID onDraw(Canvas canvas) {        super.onDraw(canvas);    }}

 

先处理自定义的属性,使我们在布局时可以随意更改该vIEw的文字内容、颜色、大小

在res/values/下创建一个名为attrs.xml的文件,然后定义如下属性: 
format的意思是该属性的取值是什么类型(支持的类型有string,color,demension,integer,enum,reference,float,boolean,fraction,flag)

<?xml version="1.0" enCoding="utf-8"?><resources>    <declare-styleable name="MyTextVIEw">        <attr name="mText" format="string" />        <attr name="mTextcolor" format="color" />        <attr name="mTextSize" format="dimension" />    </declare-styleable></resources>

 

在布局文件中引入我们的命名空间xmlns:lfm="http://schemas.androID.com/apk/res-auto"

<?xml version="1.0" enCoding="utf-8"?><linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"    xmlns:lfm="http://schemas.androID.com/apk/res-auto"    androID:layout_wIDth="match_parent"    androID:layout_height="match_parent"    androID:orIEntation="vertical">    <com.lfm.vIEw.MyTextVIEw        androID:ID="@+ID/my_tv"        androID:layout_wIDth="match_parent"        androID:layout_height="match_parent"        lfm:mText="金大人的梦"        lfm:mTextcolor="#000000"        lfm:mTextSize="30sp" /></linearLayout>

在构造方法中获取自定义属性的值:

public MyTextVIEw(Context context, AttributeSet attrs) {        super(context, attrs);        //获取自定义属性的值        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyTextVIEw);        mText = a.getString(R.styleable.MyTextVIEw_mText);        mTextcolor = a.getcolor(R.styleable.MyTextVIEw_mTextcolor, color.BLACK);        mTextSize = (int) a.getDimension(R.styleable.MyTextVIEw_mTextSize, 100);        a.recycle();  //回收    }

 

接下来处理onDraw方法里面的内容即可自定义view,画笔画布不可少,
    @OverrIDe    protected voID onDraw(Canvas canvas) {        super.onDraw(canvas);        // 绘制文字        mPaint = new Paint();        mPaint.setTextSize(mTextSize);
     canvas.drawText(mText,0,0,mPaint);
}

看下效果:

与想象的似乎不太一样,文字的X坐标和Y坐标并不是从屏幕的0、0开始的,我们看一下源码drawText方法:

/**     * Draw the text, with origin at (x,y), using the specifIEd paint. The origin is interpreted     * based on the Align setting in the paint.     *     * @param text The text to be drawn     * @param x The x-coordinate of the origin of the text being drawn     * @param y The y-coordinate of the baseline of the text being drawn     * @param paint The paint used for the text (e.g. color, size, style)     */    public voID drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {        super.drawText(text, x, y, paint);    }

从源码的注释发现,y坐标是根据一个叫baseline的值来开始绘制的,那么baseline是什么东西呢:

以上图来理解,baseline就是红线,指的是文字的基准线,就好像我们小学时候的标准拼音四格线一样:

因此,当y设置为0的时候,实际上就是基准线为0,那么此时我们在屏幕上就只能看到基准线以下的区域,所以才会出现运行效果图的那种情况,所以当我们设置baseline=100时看看效果:

@OverrIDe    protected voID onDraw(Canvas canvas) {        super.onDraw(canvas);        // 绘制文字        mPaint = new Paint();        mPaint.setTextSize(mTextSize);        mPaint.setcolor(mTextcolor);        float baseline = 100;        canvas.drawText(mText, 0, baseline, mPaint);    }

那么此时文字就可以显示出来了。

 

以上是最最基本的自定义textvIEw,那么加强一点难度,我们如何将文字横竖都居中呢?1、我们先把中心坐标给画出来,便于我们处理是否真的是居中了。
    @OverrIDe    protected voID onDraw(Canvas canvas) {        super.onDraw(canvas);        // 绘制文字        mPaint = new Paint();        mPaint.setTextSize(mTextSize);        mPaint.setcolor(mTextcolor);        float baseline = 100;        canvas.drawText(mText, 0, baseline, mPaint);        drawCenterlineX(canvas);        drawCenterlineY(canvas);    }    private voID drawCenterlineX(final Canvas canvas){        Paint paint = new Paint();        paint.setStyle(Paint.Style.FILL);//实线        paint.setcolor(color.RED);// 颜色        paint.setstrokeWIDth(3);// 线的宽度        canvas.drawline(getWIDth()/2,0,getWIDth()/2,getHeight(),paint);    }    private voID drawCenterlineY(final Canvas canvas){        Paint paint = new Paint();        paint.setStyle(Paint.Style.FILL);        paint.setcolor(color.BLUE);        paint.setstrokeWIDth(3);        canvas.drawline(0,getHeight()/2,getWIDth(),getHeight()/2,paint);    }

2、横向居中,有两种方法

(1)在绘制文字之前也就是drawText之前添加如下代码

mPaint.setTextAlign(Paint.Align.CENTER);canvas.drawText(mText, getWIDth()/2, baseline, mPaint);

 

(2)当setTextAlign不设置为CENTER时,默认则是为left,效果如图所示

//mPaint.setTextAlign(Paint.Align.CENTER);canvas.drawText(mText, getWIDth()/2, baseline, mPaint);

那么此时想要居中的话,应该在X轴向左偏移文字宽度的一半即可,效果图+代码如下

//mPaint.setTextAlign(Paint.Align.CENTER);float wIDth = mPaint.measureText(mText);//获取文字宽度canvas.drawText(mText, (getWIDth()-wIDth)/2, baseline, mPaint);

 3、纵向居中

纵向坐标的起始位置取决于baseline,先把baseline设置为屏幕高度的一半看看效果,为了有更明显的感觉,我把文字大小进行了调整,变得更大了

float baseline = getHeight()/2;canvas.drawText(mText, (getWIDth()-wIDth)/2, baseline, mPaint);

发现文字在纵向并没有居中,与前面的分析一样,也就是如果我们将baseline设置为控件高度的一半,那么文字的绘制是以该线为基准线,那么想要将文字纵向居中要如何处理呢?paint有一个属性

Paint.FontMetrics FontMetrics = paint.getFontMetrics();

FontMetrics有几个属性,对应的就是下图所标明的位置

//        public float ascent;//基准线距离上边界的距离,文字通常在这个范围内(由于各个国家地区文字不同,可能会有超出这个范围的,例如藏文,但是最高不会超出top),ascent为负数

//        public float bottom; //基准线距离下边界的最高距离,文字最低不能超出bottom的范围

//        public float descent;//基准线距离下边界的距离,文字通常在这个范围内(由于各个国家地区文字不同,可能会有超出这个范围的,例如藏文,但是最高不会超出bottom)

//        public float leading;//看成行距即可

//        public float top; //基准线距离上边界的最高距离,文字最高不能超出top的范围,top为负数

注:AndroID中,X或Y偏移时,往左和上是减,往右和下是加,而top和ascent的值为负数

那么实际上想要文字垂直方向居中,那么基于上图再结合我们的运行效果图,我们可以得出以下结论:

此时图中红线也就是基准线,也是屏幕的中心线,如果我们想让文字垂直居中,在中心线不变的情况下,那么文字需要再往下移动一段距离,也就是基准线(baseline)需要往下移动才能使文字居中

结合图中字母Jj,可以想象成,文字下移一段距离之后,红线才会处于文字的中间,那么这个文字下移的距离就是baseline下移的距离,那么我们可以得出结论

首先红线的位置在我们代码里面是 getHeight()/2的位置

文字先向下走ascent的距离,再向上走(descent+ascent)/2的距离,就居中了。

因为ascent为负数,所以要取绝对值,所以向下走是 getHeight()/2-ascent,再向上走getHeight()/2-ascent-(descent-ascent)/2

float baseline = getHeight() / 2 - FontMetrics.ascent - (FontMetrics.descent - FontMetrics.ascent) / 2;

一步一步简化公式:

float baseline = getHeight() / 2 - FontMetrics.ascent - FontMetrics.descent/2 + FontMetrics.ascent/ 2;
float baseline = getHeight() / 2 - FontMetrics.descent/2 - FontMetrics.ascent + FontMetrics.ascent/ 2;
float baseline = getHeight() / 2 - FontMetrics.descent/2 - FontMetrics.ascent/2;
float baseline = getHeight() / 2 - (FontMetrics.descent + FontMetrics.ascent)/2;

看到上面这段代码就非常的熟悉了,这个公式应该在很多博客里面都见到过,就是这样简算得到的,如果不进行这样的分析,估计很多人都无法理解这个公式到底是什么意思。看下效果图:

最终onDraw方法里面的代码:

    @OverrIDe    protected voID onDraw(Canvas canvas) {        super.onDraw(canvas);        // 绘制文字        mPaint = new Paint();        mPaint.setTextSize(mTextSize);        mPaint.setcolor(mTextcolor);        //mPaint.setTextAlign(Paint.Align.CENTER);        float wIDth = mPaint.measureText(mText);//获取文字宽度        Paint.FontMetrics FontMetrics = mPaint.getFontMetrics();        float baseline = getHeight() / 2 - (FontMetrics.descent + FontMetrics.ascent)/2;        canvas.drawText(mText, (getWIDth() - wIDth) / 2, baseline, mPaint);        drawCenterlineX(canvas);        drawCenterlineY(canvas);    }
完。 总结

以上是内存溢出为你收集整理的Android开发——自定义view之文字绘制全部内容,希望文章能够帮你解决Android开发——自定义view之文字绘制所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1054041.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存