Android 自定义 View 五(绘制文本大小、多行多列居中)

1,181 阅读8分钟
原文链接: www.jianshu.com

一、绘制文本

在Canvas中绘制文本,使用前面文章的坐标系

1、drawText的几种方法

public void drawText (String text, float x, float y, Paint paint)
public void drawText (String text, int start, int end, float x, float y, Paint paint)
public void drawText (CharSequence text, int start, int end, float x, float y, Paint paint)
public void drawText (char[] text, int index, int count, float x, float y, Paint paint)

public void drawText (String text, float x, float y, Paint paint)
x 、 y轴表示绘制文本左下角到坐标

        mPaint.setColor(Color.RED);
        mPaint.setTextSize(50);
        String string="HeDan";
        canvas.drawText(string,0,0,mPaint);

xyText.png


public void drawText (String text, int start, int end, float x, float y, Paint paint)
绘制一部分Text,start表示从第几个字符开始,end表示第几个字符之前结束

//public void drawText (String text, int start, int end, float x, float y, Paint paint)
        canvas.drawText(string,0,3,0,0,mPaint);

starttext.png


public void drawText (CharSequence text, int start, int end, float x, float y, Paint paint)使用CharSequence绘制,方法,效果同上
public void drawText (char[] text, int index, int count, float x, float y, Paint paint)使用字符数组绘制,方法,效果同上

2、drawPosText

给文本中到每个字符都设定一个坐标,不推荐使用

3、drawTextOnPath

通过Path进行文本绘制,path是一个比较重要且有趣的东西,在之后涉及知识中会进行分析。

Path path = new Path();
path.lineTo(0,200);
canvas.drawTextOnPath(string,path,100,100,mPaint);

这里简单写一个,path为一条从原点到(0,200)的直线,水平偏移量、垂直偏移量都设置为100,效果如下:


pathtext.png

二、文本居中

1、单行文本居中

a、 public void getTextBounds(String text, int start, int end, Rect bounds)

通过Rect获取文本宽度和高度,方法如下

Rect rect = new Rect();
mPaint.getTextBounds(string,0,string.length(),rect);
 int width = rect.width();//文本宽度
int height = rect.height();//文本高度
b、getFontMetrics(),getFontMetricsInt()用于返回字符串的测量,而两个方法的区别就是返回值得类型。返回值一共五个属性:
  • Top: baseline到文本顶部的最大的距离
  • Ascent:baseline到文本顶部到推荐距离
  • Descent:baseline到文本底部到推荐距离
  • Bottom:baseline到文本底部到最大距离
  • Leading:两行文本之间推荐到额外距离,一般为0(推荐不考虑)

String-Center.png

在Android的坐标系中,向下为Y轴正方向,向上为Y轴负方向,所部baseling之上到Top与Ascent都是负数,而baselin之下到Descent、Bottom都是正数。如果要让字符串在垂直方向上居中,则需要在纵坐标上增加Asecent绝对值与descent的差。

 Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
 float textHeight=(-fontMetrics.ascent-fontMetrics.descent)/2;
 canvas.drawText(string,0,textHeight,mPaint);

ycentent.png
c、setTextAlign可以设置画笔绘制文本到对齐方式,选择center即可完成文本到水平居中
 Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
 float textHeight=(-fontMetrics.ascent-fontMetrics.descent)/2;
 mPaint.setTextAlign(Paint.Align.CENTER);
  canvas.drawText(string,0,textHeight,mPaint);

xcentent.png

d、measureText可以测量文本的宽度,即横向长度,这样也可以来完成居中设置,但在这之前,需要恢复文本对齐方式至默认设置(居左)。

     Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
     float textHeight=(-fontMetrics.ascent-fontMetrics.descent)/2;
    float textWidth = mPaint.measureText(string);
     canvas.drawText(string,-textWidth/2,textHeight,mPaint);

效果同上C。

2、多行文本居中

有设定好的字符串数组,画笔,画布,坐标点

  • 1、drawText每次只能绘制一行,通过for循环
  • 2、字符串数组大于1时,每个字符到高度应该为-top+bottom,总高度为length*(-top+bottom)
  • 3、偏移量就等于length*(-top+bottom)/2-bottom
  • 4、如果顺序向上(Y负方向)排列到话,那么第i个字符串到高度就是-(length-i-1)*(-top+bottom)
  • 5、每个字符串高度减去偏移量,就应该是每个字符串到baseline的y坐标
    详细代码
    private void textCenter(String [] strings, Paint paint, Canvas canvas, Point point,Paint.Align aligin){
          paint.setTextAlign(aligin);
          Paint.FontMetrics fontMetrics = paint.getFontMetrics();
          float top=fontMetrics.top;
          float bottom=fontMetrics.bottom;
          int length=strings.length;
          float total=(length-1)*(-top+bottom)+(-fontMetrics.ascent+fontMetrics.descent);
          float offset=total/2-bottom;
          for(int i=0;i<length;i++){
              float yAxis=-(length-i-1)*(-top+bottom)+offset;
              canvas.drawText(strings[i]+"",point.x,point.y+yAxis,paint);
          }
      }
    偏移量取值的变化,是因为在字符串的首尾两个字符串,需要把top改成ascent,bottom改成descent。通过设置画笔来完成横向的居中,居左,居右。

使用代码

String[] striings={"HeDan","是","一","个","好","学","生"};
//String[] striings={"床前明月光,","疑是地上霜。","举头望明月,","低头思故乡。"};
Point point=new Point(0,0);
//textCenter(striings,mPaint,canvas,point, Paint.Align.CENTER);
textCenter(striings,mPaint,canvas,point, Paint.Align.LEFT);

ytext.png

ytext1.png

ytext2.png

3、多列文本

  • 1、一个字符串数组
  • 2、每个字符数组中获取最宽到字符宽度我字符数组的宽度
  • 3、计算数组x坐标,再掉用之前多行文本居中到代码

    居右详细代码
    private void textCenter(char[]  strings, Paint paint, Canvas canvas, Point point,Paint.Align aligin){
          paint.setTextAlign(aligin);
          Paint.FontMetrics fontMetrics = paint.getFontMetrics();
          float top=fontMetrics.top;
          float bottom=fontMetrics.bottom;
          int length=strings.length;
          float total=(length-1)*(-top+bottom)+(-fontMetrics.ascent+fontMetrics.descent);
          float offset=total/2-bottom;
          for(int i=0;i<length;i++){
              float yAxis=-(length-i-1)*(-top+bottom)+offset;
              canvas.drawText(strings[i]+"",point.x,point.y+yAxis,paint);
          }
      }
    
      private void rowTextRight(String[] strings,Paint paint,Canvas canvas,Point point){
          int length = strings.length;
          float len ,newLen;
          float total=0;
          for(int j=0;j<length;j++){
              char[] chars = strings[j].toCharArray();
              len=paint.measureText(chars[0]+"");
              for (int i=1; i<chars.length; i++){
                  newLen = mPaint.measureText(chars[i]+"");
                  len = Math.max(newLen,len);
              }
              point.x=point.x-(int)total;
              textCenter(chars,paint,canvas,point, Paint.Align.RIGHT);
              point.x = point.x + (int) total;
              total = total+len;
          }
      }

    使用代码

       String[] striings={"鹅,鹅,鹅,","曲项向天歌,","白毛浮绿水,","红掌拨清波,"};
          Point point=new Point(0,0);
          rowTextRight(striings,mPaint,canvas,point);

ytext3.png
居左详细代码
  private void rowTextLeft(String[] strings,Paint paint,Canvas canvas,Point point){
        int length = strings.length;
        float len ,newLen;
        float total=0;
        for(int j=0;j<length;j++){
            char[] chars = strings[j].toCharArray();
            len=paint.measureText(chars[0]+"");
            for (int i=1; i<chars.length; i++){
                newLen = mPaint.measureText(chars[i]+"");
                len = Math.max(newLen,len);
            }
            point.x=point.x+(int)total;
            textCenter(chars,paint,canvas,point, Paint.Align.LEFT);
            point.x = point.x - (int) total;
            total = total+len;
        }
    }

ytext.png
居中详细代码
  private void rowTextCenter(String[] strings,Paint paint,Canvas canvas,Point point){
        int length = strings.length;
        float len ,newLen;
        float total=0;
        for(int j=0;j<length;j++){
            char[] chars = strings[j].toCharArray();
            len=paint.measureText(chars[0]+"");
            for (int i=1; i<chars.length; i++){
                newLen = mPaint.measureText(chars[i]+"");
                len = Math.max(newLen,len);
            }
            total = total+len;
        }

        for(int j=0;j<length;j++){
            char[] chars = strings[j].toCharArray();
            len=paint.measureText(chars[0]+"");
            for (int i=1; i<chars.length; i++){
                newLen = mPaint.measureText(chars[i]+"");
                len = Math.max(newLen,len);
            }
            if(j==0){
                point.x=(int)-(total-len)/2;
            }
            textCenter(chars,paint,canvas,point, Paint.Align.CENTER);
            point.x=point.x+(int)len;
        }
    }

```

ytext.png

4、文本自动换行居中

自动换行在Android 自定义View 一(初体验onDraw(),自定义属性,onMeasue()方法,测量换行)这里在介绍一个系统到方法:
StaticLayout,可以设置宽度,当前行文本超过此宽度后,进行自动换行,提供提供ALIGN_CENTER(居中)、ALIGN_NORMAL(标准)、ALIGN_OPPOSITE(与标准相反)三种对齐方式
详细代码:

  private void textCenter(String string, TextPaint textPaint, Canvas canvas, Point point, int width, Layout.Alignment align,float spacingmult,float spacingadd,boolean includepad){
        StaticLayout staticLayout = new StaticLayout(string,textPaint,width, align,spacingmult,spacingadd,includepad);
        canvas.save();
        canvas.translate(-staticLayout.getWidth()/2+point.x,-staticLayout.getHeight()/2+point.y);
        staticLayout.draw(canvas);
        canvas.restore();

    }

使用方法

  String mString="HeDan是一个好学生";
        TextPaint tp = new TextPaint();
        tp.setColor(Color.BLUE);
        tp.setStyle(Paint.Style.FILL);
        Point point=new Point(0,0);
        tp.setTextSize(50);
        textCenter(mString,tp,canvas,point,200,Layout.Alignment.ALIGN_CENTER,1.5f,0,false);

ytext.png

下面贴出学习源码:

public class MultipleText extends View {
    private Paint mPaint;
    private float pWidth;
    private float pHeight;
    private int mWidth;
    private int mHeight;

    public MultipleText(Context context) {
        this(context, null);
    }

    public MultipleText(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MultipleText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);//设置画笔填充
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setColor(Color.parseColor("#52adff"));//设置画笔颜色

    }

    ;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(mWidth / 2, mHeight / 2);

        mPaint.setStrokeWidth(10);//设置原点宽度
        canvas.drawPoint(0, 0, mPaint);//绘制原点
        canvas.drawPoints(new float[]{pWidth, 0, 0, pHeight, -pWidth, 0, 0, -pHeight}, mPaint);//绘制边缘点*/

        mPaint.setStrokeWidth(1);//重新设置画笔到宽度
        canvas.drawLine(-pWidth, 0, pWidth, 0, mPaint);//绘制X轴
        canvas.drawLine(0, -pHeight, 0, pHeight, mPaint);//绘制Y轴

        mPaint.setStrokeWidth(3);//设置箭头宽度
        canvas.drawLines(new float[]{pWidth, 0, pWidth * 0.95f, -pWidth * 0.05f, pWidth, 0, pWidth * 0.95f, pWidth * 0.05f}, mPaint);//X轴箭头
        canvas.drawLines(new float[]{0, pHeight, -pHeight * 0.05f, pHeight * 0.95f, 0, pHeight, pHeight * 0.05f, pHeight * 0.95f}, mPaint);//Y轴箭头

        mPaint.setColor(Color.RED);
        mPaint.setTextSize(50);
        String string="HeDan";
        //public void drawText (String text, float x, float y, Paint paint)
        //canvas.drawText(string,0,0,mPaint);
        //public void drawText (String text, int start, int end, float x, float y, Paint paint)
       // canvas.drawText(string,0,3,0,0,mPaint);
      /*  Path path=new Path();
        path.lineTo(0,200);
        canvas.drawTextOnPath(string,path,100,100,mPaint);
        Rect rect = new Rect();
        mPaint.getTextBounds(string,0,string.length(),rect);
        int width = rect.width();//文本宽度
        int height = rect.height();//文本高度*/
       /* Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        float textHeight=(-fontMetrics.ascent-fontMetrics.descent)/2;
        float textWidth = mPaint.measureText(string);
        canvas.drawText(string,-textWidth/2,textHeight,mPaint);*/

  /*      String[] striings={"鹅,鹅,鹅,","曲项向天歌,","白毛浮绿水,","红掌拨清波,"};
        Point point=new Point(0,0);
      textCenter(striings,mPaint,canvas,point, Paint.Align.LEFT);
        rowTextCenter(striings,mPaint,canvas,point);*/

        String mString="HeDan是一个好学生";
        TextPaint tp = new TextPaint();
        tp.setColor(Color.BLUE);
        tp.setStyle(Paint.Style.FILL);
        Point point=new Point(0,0);
        tp.setTextSize(50);
        textCenter(mString,tp,canvas,point,200,Layout.Alignment.ALIGN_CENTER,1.5f,0,false);



    }
    private void textCenter(String [] strings, Paint paint, Canvas canvas, Point point,Paint.Align aligin){
        paint.setTextAlign(aligin);
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        float top=fontMetrics.top;
        float bottom=fontMetrics.bottom;
        int length=strings.length;
        float total=(length-1)*(-top+bottom)+(-fontMetrics.ascent+fontMetrics.descent);
        float offset=total/2-bottom;
        for(int i=0;i<length;i++){
            float yAxis=-(length-i-1)*(-top+bottom)+offset;
            canvas.drawText(strings[i],point.x,point.y+yAxis,paint);
        }
    }

    private void textCenter(char[]  strings, Paint paint, Canvas canvas, Point point,Paint.Align aligin){
        paint.setTextAlign(aligin);
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        float top=fontMetrics.top;
        float bottom=fontMetrics.bottom;
        int length=strings.length;
        float total=(length-1)*(-top+bottom)+(-fontMetrics.ascent+fontMetrics.descent);
        float offset=total/2-bottom;
        for(int i=0;i<length;i++){
            float yAxis=-(length-i-1)*(-top+bottom)+offset;
            canvas.drawText(strings[i]+"",point.x,point.y+yAxis,paint);
        }
    }

    private void rowTextRight(String[] strings,Paint paint,Canvas canvas,Point point){
        int length = strings.length;
        float len ,newLen;
        float total=0;
        for(int j=0;j<length;j++){
            char[] chars = strings[j].toCharArray();
            len=paint.measureText(chars[0]+"");
            for (int i=1; i<chars.length; i++){
                newLen = mPaint.measureText(chars[i]+"");
                len = Math.max(newLen,len);
            }
            point.x=point.x-(int)total;
            textCenter(chars,paint,canvas,point, Paint.Align.RIGHT);
            point.x = point.x + (int) total;
            total = total+len;
        }
    }

    private void rowTextLeft(String[] strings,Paint paint,Canvas canvas,Point point){
        int length = strings.length;
        float len ,newLen;
        float total=0;
        for(int j=0;j<length;j++){
            char[] chars = strings[j].toCharArray();
            len=paint.measureText(chars[0]+"");
            for (int i=1; i<chars.length; i++){
                newLen = mPaint.measureText(chars[i]+"");
                len = Math.max(newLen,len);
            }
            point.x=point.x+(int)total;
            textCenter(chars,paint,canvas,point, Paint.Align.LEFT);
            point.x = point.x - (int) total;
            total = total+len;
        }
    }
    private void rowTextCenter(String[] strings,Paint paint,Canvas canvas,Point point){
        int length = strings.length;
        float len ,newLen;
        float total=0;
        for(int j=0;j<length;j++){
            char[] chars = strings[j].toCharArray();
            len=paint.measureText(chars[0]+"");
            for (int i=1; i<chars.length; i++){
                newLen = mPaint.measureText(chars[i]+"");
                len = Math.max(newLen,len);
            }
            total = total+len;
        }

        for(int j=0;j<length;j++){
            char[] chars = strings[j].toCharArray();
            len=paint.measureText(chars[0]+"");
            for (int i=1; i<chars.length; i++){
                newLen = mPaint.measureText(chars[i]+"");
                len = Math.max(newLen,len);
            }
            if(j==0){
                point.x=(int)-(total-len)/2;
            }
            textCenter(chars,paint,canvas,point, Paint.Align.CENTER);
            point.x=point.x+(int)len;
        }
    }

    private void textCenter(String string, TextPaint textPaint, Canvas canvas, Point point, int width, Layout.Alignment align,float spacingmult,float spacingadd,boolean includepad){
        StaticLayout staticLayout = new StaticLayout(string,textPaint,width, align,spacingmult,spacingadd,includepad);
        canvas.save();
        canvas.translate(-staticLayout.getWidth()/2+point.x,-staticLayout.getHeight()/2+point.y);
        staticLayout.draw(canvas);
        canvas.restore();

    }




    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        pWidth = mWidth / 2 * 0.8f;//X轴边缘原点到原点到距离
        pHeight = mHeight / 2 * 0.8f;//Y轴边缘原点到原点到距离
    }