Android自定义View绘制居中文本

本文实例为大家分享了Android自定义View绘制居中文本的具体代码,供大家参考,具体内容如下

自定义view的步骤:

1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
3、重写onMesure(非必须)
4、重写onDraw

1、自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性,只定义三个,有文本、颜色和字体大小:

<!--CustomTextView-->
    <declare-styleable name="CustomTitleView">
        <attr name="titleText" format="string"/>
        <attr name="titleTextColor" format="color"/>
        <attr name="titleTextSize" format="dimension"/>
</declare-styleable>

2、自定义一个TextView继承View,在构造方法中获取我们自定义的属性:

public class CustomTextView extends View {

    /**
     * 文本
     */
    private String mTitleText;
    /**
     * 文本的颜色
     */
    private int mTitleTextColor;
    /**
     * 文本的大小
     */
    private int mTitleTextSize;

    /**
     * 绘制时控制文本绘制的范围
     */
    private Rect mBound;
    private Paint mPaint;

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

    public CustomTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyleAttr, 0);
        mTitleText = a.getString(R.styleable.CustomTitleView_titleText);
        mTitleTextColor = a.getColor(R.styleable.CustomTitleView_titleTextColor, Color.BLACK);
        mTitleTextSize = a.getDimensionPixelSize(R.styleable.CustomTitleView_titleTextSize, (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
        a.recycle();

        /**
         * 获得绘制文本的宽和高
         */
        mPaint = new Paint();
        mPaint.setTextSize(mTitleTextSize);
        // mPaint.setColor(mTitleTextColor);
        mBound = new Rect();
        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
    }
 }

3、重写onMesure

我们在使用控件的时候一般会设置宽高。
设置类型有:wrap_content,match_parent,100dp(明确值)

自定义控件时, 如果设置了 明确的宽高(100dp),系统帮我们测量的结果就是我们设置的实际值;
如果是 wrap_content 或者 match_parent 系统帮我们测量的结果就是 match_parent。
所以当设置为 wrap_content 的时候我们需要 重写onMesure 方法重新测量。

重写之前了解 MeasureSpec 的 specMode,一共分为三种类型:
EXACTLY:一般表示设置了 明确值,或者 match_parent ;
AT_MOST:表示子控件限制在一个最大值内,一般为 wrap_content;
UNSPECIFIED:表示子控件像多大就多大,很少使用

 /**
     * EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
     AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
     UNSPECIFIED:表示子布局想要多大就多大,很少使用
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 获取宽高的设置模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //获取宽高的大小
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //最终宽高
        int width;
        int height;
        if (widthMode == MeasureSpec.EXACTLY) {//当设定了宽度,测量的宽度就等于设定的宽度
            width = widthSize;
        } else {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
            float textWidth = mBound.width();

            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
            width = desired;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
            float textHeight = mBound.height();

            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = desired;
        }
        //最终设置宽高
        setMeasuredDimension(width, height);
    }

原理就是:获取宽高的模式,如果是明确值,或者match_parent,直接获取原始值返回。
如果是 wrap_content,计算宽高:控件的宽高 + 左右(上下)内边距。

4、重写onDraw

@Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(mTitleTextColor);
         /*
         * 控件宽度/2 - 文字宽度/2
         * getWidth() / 2 - mBound.width() / 2
         */

         /*
         * 控件高度/2 + 文字高度/2,绘制文字从文字左下角开始,因此"+"
         * getHeight() / 2 + mBound.height() / 2
         */

        canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);

    }

在xml中这样写:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.xp.baseapp.activity.CustomTvActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <com.xp.baseapp.widget.drawview.CustomTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#f0f"
            custom:titleText="大家好9527ing"
            custom:titleTextColor="#000000"
            custom:titleTextSize="20sp"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="大家好9527ing"
            android:background="#ff0000"
            android:layout_marginLeft="3dp"
            android:textSize="20sp"/>
    </LinearLayout>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="大家好9527ing"
        android:layout_marginTop="3dp"
        android:background="#00f000"
        android:textSize="20sp"/>
</LinearLayout>

运行结果:

紫色的是自定义的TextView,红色和绿色的是系统的TextView。因为这里宽高设置为wrap_content,并且没有padding,和系统原生的TextView比宽度和高度都不够,还绘制不全。那接下来一个一个解决。

首先解决宽度:

将原来的测量方法:

float textWidth = mBound.width();//这样宽度会不全,比系统的textView短

改为比较精确的测量文本宽度的方法:

float textWidth = mPaint.measureText(mTitleText);//比较精确的测量文本宽度的方式

运行结果:

现在宽度就和系统的TextView一样宽了。

然后解决高度问题:

先了解一下Android是怎么样绘制文字的,这里涉及到几个概念,分别是文本的top,bottom,ascent,descent,baseline。

Baseline是基线,在android中,文字的绘制都是从Baseline处开始的,Baseline往上至字符“最高处”的距离我们称之为ascent(上坡度),Baseline往下至字符“最低处”的距离我们称之为descent(下坡度);

leading(行间距)则表示上一行字符的descent到该行字符的ascent之间的距离; 

top和bottom文档描述地很模糊,其实这里我们可以借鉴一下TextView对文本的绘制,TextView在绘制文本的时候总会在文本的最外层留出一些内边距,因为TextView在绘制文本的时候考虑到了类似读音符号,下图中的A上面的符号就是一个拉丁文的类似读音符号的东西:

Baseline是基线,Baseline以上是负值,以下是正值,因此 ascent,top是负值, descent和bottom是正值。

因此我们这样改,将原来的测量方法:

float textHeight = mBound.height();

改为比较精确的测量文本宽度的方法:

Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
float textHeight = Math.abs((fontMetrics.bottom - fontMetrics.top));

运行结果:

最后就是解决文本居中的问题:
将之前的绘制文本宽度

getWidth() / 2 - mBound.width() / 2

改为

int startX = (int) (getWidth() / 2 - mPaint.measureText(mTitleText) / 2);

绘制文本高度

getHeight() / 2 + mBound.height() / 2

改为

//解决高度绘制不居中
Paint.FontMetricsInt fm = mPaint.getFontMetricsInt();
int startY = getHeight() / 2 - fm.descent + (fm.bottom - fm.top) / 2;

getHeight()/2-fm.descent 的意思是 将整个文字区域抬高至控件的1/2
(fm.bottom - fm.top)其实就是文本的高度,(fm.bottom - fm.top) / 2的意思就是将文本下沉文本高度的一半

运行结果:

现在基本和系统的TextView效果差不多了。由于demo中写的东西比较多,这里就只贴出自定义类的源码

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import com.xp.baseapp.R;

public class CustomTextView extends View {

    /**
     * 文本
     */
    private String mTitleText;
    /**
     * 文本的颜色
     */
    private int mTitleTextColor;
    /**
     * 文本的大小
     */
    private int mTitleTextSize;

    /**
     * 绘制时控制文本绘制的范围
     */
    private Rect mBound;
    private Paint mPaint;

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

    public CustomTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyleAttr, 0);

        mTitleText = a.getString(R.styleable.CustomTitleView_titleText);
        mTitleTextColor = a.getColor(R.styleable.CustomTitleView_titleTextColor, Color.BLACK);
        mTitleTextSize = a.getDimensionPixelSize(R.styleable.CustomTitleView_titleTextSize, (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
        a.recycle();

        /**
         * 获得绘制文本的宽和高
         */
        mPaint = new Paint();
        mPaint.setTextSize(mTitleTextSize);
        // mPaint.setColor(mTitleTextColor);
        mBound = new Rect();
        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
    }

    /**
     * EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
     AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
     UNSPECIFIED:表示子布局想要多大就多大,很少使用
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 获取宽高的设置模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //获取宽高的大小
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //最终宽高
        int width;
        int height;
        if (widthMode == MeasureSpec.EXACTLY) {//当设定了宽度,测量的宽度就等于设定的宽度
            width = widthSize;
        } else {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
//            float textWidth = mBound.width();//这样宽度会不全,比系统的textView短
            float textWidth = mPaint.measureText(mTitleText);//比较精确的测量文本宽度的方式
            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
            width = desired;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
//            float textHeight = mBound.height();//这样高度会不全,比系统的textView窄
            Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
            float textHeight = Math.abs((fontMetrics.bottom - fontMetrics.top));

            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = desired;
        }

        //最终设置宽高
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {

//        mPaint.setColor(Color.YELLOW);
//        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);

        mPaint.setColor(mTitleTextColor);
         /*
         * 控件宽度/2 - 文字宽度/2
         * getWidth() / 2 - mBound.width() / 2
         */

         /*
         * 控件高度/2 + 文字高度/2,绘制文字从文字左下角开始,因此"+"
         * getHeight() / 2 + mBound.height() / 2
         */

        int startX = (int) (getWidth() / 2 - mPaint.measureText(mTitleText) / 2);

         //解决高度绘制不居中
        Paint.FontMetricsInt fm = mPaint.getFontMetricsInt();
        int startY = getHeight() / 2 - fm.descent + (fm.bottom - fm.top) / 2;

//        canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
        canvas.drawText(mTitleText, startX, startY, mPaint);
    }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android手机开发 控件 TextView文字居中

    有2种方法可以设置TextView文字居中: 一:在xml文件设置:Android:gravity="center" 二:在程序中设置:txtTitle.setGravity(Gravity.CENTER); 设置控件居中: android:layout_gravity="center"是对textview控件在整个布局中居中,也可以在其父layout中调用设置android:gravity="center" 程序中也是需要设置其所在控件的父la

  • Android Canvas的drawText()与文字居中方案详解

    自定义View是绘制文本有三类方法 // 第一类 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, flo

  • Android布局居中的几种做法

    Android的布局文件中,如果想让一个组件(布局或View)居中显示在另一个布局(组件)中,可以由这么几种做法: android:layout_gravity android:gravity android:layout_centerInParent layout_gravity android:layout_gravity ,用来指定当前组件(布局或View)在父组件(布局)中的位置,父布局应该是LinearLayout或者它的后裔. layout_gravity取值可能是: top bot

  • Android中ImageView无法居中的问题解决方法

    代码如下:[java] 复制代码 代码如下: <LinearLayout         android:layout_width="wrap_content"         android:layout_height="fill_parent"         android:orientation="vertical"         android:layout_weight="1"         androi

  • Android编程实现修改标题栏位置使其居中的方法

    本文实例讲述了Android编程实现修改标题栏位置使其居中的方法.分享给大家供大家参考,具体如下: package com.example.libraryclient; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.Window; import com.example.R; public class HomePageActivity exte

  • Android编程中TextView宽度过大导致Drawable无法居中问题解决方法

    本文实例讲述了Android编程中TextView宽度过大导致Drawable无法居中问题解决方法.分享给大家供大家参考,具体如下: 在做项目的时候,很多时候我们都要用到文字和图片一起显示,一般设置TextView的DrawableLeft.DrawableRight.DrawableTop.DrawableBottom就行了.但是有一种情况是当TextView的熟悉是fill_parent或者使用权重的时候并且设置了起Gravity的ceter的时候,Drawable图片是无法一起居中的,为了

  • Android Canvas drawText文字居中的一些事(图解)

    1.写在前面 在实现自定义控件的过程中,常常会有绘制居中文字的需求,于是在网上搜了一些相关的博客,总是看的一脸懵逼,就想着自己分析一下,在此记录下来,希望对大家能够有所帮助. 2.绘制一段文本 首先把坐标原点移动到控件中心(默认坐标原点在屏幕左上角),这样看起来比较直观一些,然后绘制x.y轴,此时原点向上y为负,向下y为正,向左x为负,向右x为正,以(0,0)坐标开始绘制一段文本: @Override public void draw(Canvas canvas) { super.draw(ca

  • Android自定义View绘制居中文本

    本文实例为大家分享了Android自定义View绘制居中文本的具体代码,供大家参考,具体内容如下 自定义view的步骤: 1.自定义View的属性2.在View的构造方法中获得我们自定义的属性3.重写onMesure(非必须)4.重写onDraw 1.自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性,只定义三个,有文本.颜色和字体大小: <!--CustomTextView-->     <declare-styleable na

  • Android自定义view绘制表格的方法

    本文实例为大家分享了Android自定义view绘制表格的具体代码,供大家参考,具体内容如下 先上效果图 平时很少有这样的表格需求,不过第一想法就是自定义view绘制表格,事实上我确实是用的canvas来绘制的,整个过程看似复杂,实为简单,计算好各个点的坐标后事情就完成一半了.不废话show code import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; imp

  • Android自定义View绘制彩色圆弧

    本文实例为大家分享了Android自定义View绘制彩色圆弧的具体代码,供大家参考,具体内容如下 效果如下: 自定义View代码如下: package com.example.yan; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; i

  • Android自定义View绘制贝塞尔曲线的方法

    本文实例为大家分享了Android自定义View绘制贝塞尔曲线的具体代码,供大家参考,具体内容如下 在平面内任选 3 个不共线的点,依次用线段连接. 在第一条线段上任选一个点 D.计算该点到线段起点的距离 AD,与该线段总长 AB 的比例. 根据上一步得到的比例,从第二条线段上找出对应的点 E,使得 AD:AB = BE:BC. 连接这两点 DE. 从新的线段 DE 上再次找出相同比例的点 F,使得 DF:DE = AD:AB = BE:BC. 到这里,我们就确定了贝塞尔曲线上的一个点 F.接下

  • Android自定义View绘制贝塞尔曲线实现流程

    目录 前言 二阶贝塞尔曲线 三阶贝塞尔曲线 前言 对于Android开发,实现贝塞尔曲线还是比较方便的,有对应的API供你调用.由于一阶贝塞尔曲线就是一条直线,实际没啥多大用处,因此,下面主要讲解二阶和三阶. 二阶贝塞尔曲线 在Android中,使用quadTo来实现二阶贝塞尔 path.reset() path.moveTo(startX, startY) path.quadTo(currentX, currentY, endX, endY) canvas.drawPath(path, cur

  • Android自定义View绘制贝塞尔曲线中小红点的方法

    目录 前言 需求 效果图 代码 主要问题 简单画法 使用贝塞尔曲线 前言 上一篇文章用扇形图练习了一下安卓的多点触控,实现了单指旋转.二指放大.三指移动,四指以上同时按下进行复位的功能.今天这篇文章用很多应用常见的小红点,来练习一下贝塞尔曲线的使用. 需求 这里想法来自QQ的拖动小红点取消显示聊天条数功能,不过好像是记忆里的了,现在看了下好像效果变了.总而言之,就是一个小圆点,拖动的时候变成水滴状,超过一定范围后触发消失回调,核心思想如下: 1.一个正方形view,中间是小红点,小红点距离边框有

  • Android自定义View绘制随机生成图片验证码

    本篇文章讲的是Android自定义View之随机生成图片验证码,开发中我们会经常需要随机生成图片验证码,但是这个是其次,主要还是想总结一些自定义View的开发过程以及一些需要注意的地方. 按照惯例先看看效果图: 一.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 3.重写onMesure 4.重写onDraw 其中onMesure方法不一定要重写,但大部分情况下还是需要重写的 二.View 的几个构造函数 1.public CustomV

  • Android自定义View绘制的方法及过程(二)

    上一篇<Android 自定义View(一) Paint.Rect.Canvas介绍>讲了最基础的如何自定义一个View,以及View用到的一些工具类.下面讲下View绘制的方法及过程 public class MyView extends View { private String TAG = "--------MyView"; private int width, height; public MyView(Context context, AttributeSet a

  • Android自定义View绘制四位数随机码

    现在有这样一个需求,实现显示随机随机数可能在代码中直接很简单的就实现了,但是现在我们直接自定义View来实现这个效果,那么我们来分析一波吧,我们允许开发者自己设置这个textview的大小,颜色,和初始四位随机数的文字,那么我们需要提供自定义属性,好吧,首先把自定义属性的简单使用介绍一下吧: 首先在res/values文件夹下建利attrs.xml文件,由于这次我们功能决定我们要提供三个自定义属性,分别是textTitle String类型的,textColor是color类型的,textSiz

  • 自定义滑动按钮为例图文剖析Android自定义View绘制

    自定义View一直是横在Android开发者面前的一道坎. 一.View和ViewGroup的关系 从View和ViewGroup的关系来看,ViewGroup继承View. View的子类,多是功能型的控件,提供绘制的样式,比如imageView,TextView等,而ViewGroup的子类,多用于管理控件的大小,位置,如LinearLayout,RelativeLayout等,从下图可以看出 从实际应用中看,他们又是组合关系,我们在布局中,常常是一个ViewGroup嵌套多个ViewGro

随机推荐