自定义View_单字符展示控件

前两天看到这样一个设计【如图】,于是就写了一下,顺便写个文章~

当时直接想到的就是用一个LinearLayout动态的addView就行了,即便是自定义ViewGroup大体也是这样的思路和原理,后面想想能不能直接使用一个TextView或者View搞定,于是就有了下面的效果图:

绘制效果图
属性设置效果图

GitHub传送门
OK,接下来先来看下思路:

总体思路

  • 1、对字符串进行分割成char数组,每一个字符单独绘制;
  • 2、背景框使用drawRoundRect方法或者drawBitmap方法进行绘制,这里我们选择不使用图片。
  • 3、字符使用drawText绘制;

扩展性

为了支持一定的扩展性,我们需要考虑一些自定义的属性值,比如:文字颜色、文字大小、背景框颜色、边框......等等。罗列汇总如下:

 
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
 

其实到了这里,思路和属性值都明确了,接下来就没什么难度了,直接上核心代码:

测量

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    /**
     * 获取字符的数量
     * 计算总间隔宽度和总背景框的宽度之和,就是整个View的宽度
     * 背景框的高度作为Vie的高度
     */
    int size = mTextArr.length;
    float mDividerWidthTotal = (size - 1) * mDividerWidth;
    float mmRectWidthTotal = size * mRectWidth;
    setMeasuredDimension((int) (mDividerWidthTotal + mmRectWidthTotal), (int) mRectHeight);
}

绘制

for (int i = 0; i < mTextArr.length; i++) {
    //计算第i个背景框的左上角的x坐标
    float start = i * (mRectWidth + mDividerWidth);
    //初始化RectF的坐标,这里需要注意考虑画笔本身的宽度
    RectF rectF = new RectF(start + mRectFLineWidth / 2, mRectFLineWidth / 2,
            start + mRectWidth - mRectFLineWidth / 2, getHeight() - mRectFLineWidth / 2);
    //绘制背景框
    canvas.drawRoundRect(rectF, mRadius, mRadius, mPaintBackground);
    //绘制边框
    canvas.drawRoundRect(rectF, mRadius, mRadius, mPaintBackgroundLine);
    //绘制字符
    String text = mTextArr[i] + "";
    //获取字符宽度
    float textWidth = mPaintText.measureText(text, 0, text.length());
    float dx = start + mRectWidth / 2 - textWidth / 2;
    Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
    float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
    float baseLine = getHeight() / 2 + dy;
    canvas.drawText(text, dx, baseLine, mPaintText);
}

字符串转char数组

/**
 * 赋值-测量-绘制
 *
 * @param str
 */
public void start(String str) {
    char a[] = str.toCharArray();
    this.mTextArr = a;
    /**
     * 重新测量
     */
    requestLayout();
}

OK,到此结束,下面上全家福代码:

全家福

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;

/**
 * 自定义单字符展示控件
 */
public class CustomTextView extends View {
    /**
     * 上下文
     */
    private Context mContext;
    /**
     * 背景框画笔
     */
    private Paint mPaintBackground;
    /**
     * 背景框边框画笔
     */
    private Paint mPaintBackgroundLine;
    /**
     * 文字画笔
     */
    private Paint mPaintText;
    /**
     * 间隔宽度
     */
    private float mDividerWidth;
    /**
     * 背景框宽度
     */
    private float mRectWidth;
    /**
     * 背景框高度
     */
    private float mRectHeight;
    /**
     * 背景框圆角
     */
    private float mRadius;
    /**
     * 背景框边框宽度
     */
    private float mRectFLineWidth;
    /**
     * 字符数据
     */
    private char[] mTextArr = new char[]{};

    //构造函数
    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);
        mContext = context;

        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
        String str = array.getString(R.styleable.CustomTextView_ct_text);
        //这里判断字符串是否为null,否则会异常
        if (TextUtils.isEmpty(str)) str = "";
        float textSize = array.getDimension(R.styleable.CustomTextView_ct_textSize, ScreenUtils.sp2px(mContext, 14));
        int textColor = array.getColor(R.styleable.CustomTextView_ct_textColor, Color.WHITE);
        mRectWidth = array.getDimension(R.styleable.CustomTextView_ct_rect_width, ScreenUtils.dip2px(mContext, 30));
        mRectHeight = array.getDimension(R.styleable.CustomTextView_ct_rect_height, ScreenUtils.dip2px(mContext, 40));
        mDividerWidth = array.getDimension(R.styleable.CustomTextView_ct_rect_divider, ScreenUtils.dip2px(mContext, 4));
        int mRectBackgroundColor = array.getColor(R.styleable.CustomTextView_ct_rect_background_color, Color.BLACK);
        mRadius = array.getDimension(R.styleable.CustomTextView_ct_rect_radius, ScreenUtils.dip2px(mContext, 4));
        int mRectFLineColor = array.getColor(R.styleable.CustomTextView_ct_rect_line_color, Color.BLACK);
        mRectFLineWidth = array.getDimension(R.styleable.CustomTextView_ct_rect_line_width, ScreenUtils.dip2px(mContext, 2));
        array.recycle();

        initView(str, textSize, textColor, mRectBackgroundColor, mRectFLineColor);

    }

    /**
     * 初始化
     *
     * @param str                  字符串
     * @param textSize             文字大小
     * @param textColor            文字颜色
     * @param mRectBackgroundColor 背景框颜色
     * @param mRectFLineColor      边颜色
     */
    private void initView(String str, float textSize, int textColor, int mRectBackgroundColor, int mRectFLineColor) {

        /**
         * 初始化字符数组
         */
        char a[] = str.toCharArray();
        this.mTextArr = a;

        /**
         * 文字画笔初始化
         */
        mPaintText = new Paint();
        mPaintText.setAntiAlias(true);
        mPaintText.setColor(textColor);
        mPaintText.setTextSize(textSize);

        /**
         *背景框画笔初始化
         */
        mPaintBackground = new Paint();
        mPaintBackground.setAntiAlias(true);
        mPaintBackground.setColor(mRectBackgroundColor);
        mPaintBackground.setStyle(Paint.Style.FILL);

        /**
         *边框画笔初始化
         */
        mPaintBackgroundLine = new Paint();
        mPaintBackgroundLine.setAntiAlias(true);
        mPaintBackgroundLine.setColor(mRectFLineColor);
        mPaintBackgroundLine.setStyle(Paint.Style.STROKE);
        mPaintBackgroundLine.setStrokeWidth(mRectFLineWidth);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /**
         * 获取字符的数量
         * 计算总间隔宽度和总背景框的宽度之和,就是整个View的宽度
         * 背景框的高度作为Vie的高度
         */
        int size = mTextArr.length;
        float mDividerWidthTotal = (size - 1) * mDividerWidth;
        float mmRectWidthTotal = size * mRectWidth;
        setMeasuredDimension((int) (mDividerWidthTotal + mmRectWidthTotal), (int) mRectHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for (int i = 0; i < mTextArr.length; i++) {

            //计算第i个背景框的左上角的x坐标
            float start = i * (mRectWidth + mDividerWidth);
            //初始化RectF的坐标,这里需要注意考虑画笔本身的宽度
            RectF rectF = new RectF(start + mRectFLineWidth / 2, mRectFLineWidth / 2,
                    start + mRectWidth - mRectFLineWidth / 2, getHeight() - mRectFLineWidth / 2);
            //绘制背景框
            canvas.drawRoundRect(rectF, mRadius, mRadius, mPaintBackground);
            //绘制边框
            canvas.drawRoundRect(rectF, mRadius, mRadius, mPaintBackgroundLine);
            //绘制字符
            String text = mTextArr[i] + "";
            //获取字符宽度
            float textWidth = mPaintText.measureText(text, 0, text.length());
            float dx = start + mRectWidth / 2 - textWidth / 2;
            Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
            float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
            float baseLine = getHeight() / 2 + dy;
            canvas.drawText(text, dx, baseLine, mPaintText);
        }
    }

    /**
     * 赋值-测量-绘制
     *
     * @param str
     */
    public void start(String str) {
        char a[] = str.toCharArray();
        this.mTextArr = a;
        /**
         * 重新测量
         */
        requestLayout();
    }

    /**
     * 修改边框颜色
     *
     * @param colorStr
     */
    public void setRectLineColor(String colorStr) {
        try {
            int color = Color.parseColor(colorStr);
            mPaintBackgroundLine.setColor(color);
            invalidate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置边框的宽度
     *
     * @param mRectFLineWidth
     */
    public void setmRectFLineWidth(float mRectFLineWidth) {
        this.mRectFLineWidth = mRectFLineWidth;
        mPaintBackgroundLine.setStrokeWidth(this.mRectFLineWidth);
        invalidate();
    }

    /**
     * 设置背景框的宽度
     *
     * @param mRectWidth
     */
    public void setmRectWidth(float mRectWidth) {
        this.mRectWidth = mRectWidth;
        requestLayout();
    }

    /**
     * 设置背景框的高度
     *
     * @param mRectHeight
     */
    public void setmRectHeight(float mRectHeight) {
        this.mRectHeight = mRectHeight;
        requestLayout();
    }

    /**
     * 修改背景颜色
     *
     * @param colorStr
     */
    public void setRectColor(String colorStr) {
        try {
            int color = Color.parseColor(colorStr);
            mPaintBackground.setColor(color);
            invalidate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置圆角大小
     *
     * @param mRadius
     */
    public void setmRadius(float mRadius) {
        this.mRadius = mRadius;
        invalidate();
    }

    /**
     * 设置字符的大小
     *
     * @param size
     */
    public void setTextSize(float size) {
        mPaintText.setTextSize(size);
        invalidate();
    }

    /**
     * 修改文字颜色
     *
     * @param colorStr
     */
    public void setTextColor(String colorStr) {
        try {
            int color = Color.parseColor(colorStr);
            mPaintText.setColor(color);
            invalidate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置背景框直接的间隔宽度
     *
     * @param mDividerWidth
     */
    public void setmDividerWidth(float mDividerWidth) {
        this.mDividerWidth = mDividerWidth;
        requestLayout();
    }
}

GitHub传送门:源码

你可能感兴趣的:(自定义View_单字符展示控件)