含有边框的TextView-Android

语言: CN / TW / HK

theme: channing-cyan

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

前言

实际的项目中我们经常会遇到边框的问题,一开始我都是直接用shape来实现,但是这种方式非常的麻烦,后面又用了三方库SuperTextView,后面学习了自定义View自己来实现一下吧.

Code

``` public class BorderTextView extends AppCompatTextView {

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

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

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


/**
 * @param borderColor  border颜色
 * @param borderWidths border 宽度
 * @param borderRadius border 圆角半径
 */
public void setBorder(final int borderColor, final int[] borderWidths, final int[] borderRadius) {
    setTextColor(borderColor);
    Drawable drawable = new GradientDrawable() {
        @Override
        public void draw(Canvas canvas) {
            super.draw(canvas);
            drawBorder(canvas, borderColor, borderWidths, borderRadius);
        }
    };
    setBackground(drawable);
}

/**
 * 绘制border
 */
private void drawBorder(Canvas canvas, final int borderColor, final int[] borderWidths, final int[] borderRadius) {
    //获取当前canvas的宽高
    Rect rect = canvas.getClipBounds();
    final int width = rect.width();
    final int height = rect.height();

    int borderWidthLeft;
    int borderWidthTop;
    int borderWidthRight;
    int borderWidthBottom;

    //取得我们的边框宽度,并附加给相应变量
    if (borderWidths != null && borderWidths.length == 4) {
        borderWidthLeft = Math.min(width / 2, borderWidths[0]);
        borderWidthTop = Math.min(height / 2, borderWidths[1]);
        borderWidthRight = Math.min(width / 2, borderWidths[2]);
        borderWidthBottom = Math.min(height / 2, borderWidths[3]);
    } else {
        return;
    }

    // 设置画笔
    Paint paint = new Paint();
    //抗锯齿
    paint.setAntiAlias(true);
    //画笔颜色
    paint.setColor(borderColor);
    //画笔样式
    paint.setStyle(Paint.Style.STROKE);
    //设置边框宽度
    paint.setStrokeWidth(borderWidthLeft);

    // 判断当前边框是否相等
    if ((borderWidthLeft == borderWidthTop) && (borderWidthLeft == borderWidthRight) && (borderWidthLeft == borderWidthBottom)) {
        if (borderWidthLeft == 0) {
            return;
        }
        // borderRadius != null且borderWidth!-0;计算并画出圆角边框,否则为直角边框
        if (borderRadius != null && borderRadius.length == 4) {
            int sum = 0;
            /**
             * 循环传递的最后一个参数,相加
             * 是数组的原因是适应更多的边框需求,因为你不一定四个边框都是一个圆角度数
             */
            for (int i = 0; i < borderRadius.length; i++) {
                if (borderRadius[i] < 0) {
                    return;
                }
                sum += borderRadius[i];
            }
            //如果传递的都是0直接绘制即可
            if (sum == 0) {
                canvas.drawRect(rect, paint);
            }
            int borderWidth = borderWidthLeft;

            int mMaxRadiusX = width / 2 - borderWidth / 2;
            int mMaxRadiusY = height / 2 - borderWidth / 2;

            int topLeftRadiusX = Math.min(mMaxRadiusX, borderRadius[0]);
            int topLeftRadiusY = Math.min(mMaxRadiusY, borderRadius[0]);
            int topRightRadiusX = Math.min(mMaxRadiusX, borderRadius[1]);
            int topRightRadiusY = Math.min(mMaxRadiusY, borderRadius[1]);
            int bottomRightRadiusX = Math.min(mMaxRadiusX, borderRadius[2]);
            int bottomRightRadiusY = Math.min(mMaxRadiusY, borderRadius[2]);
            int bottomLeftRadiusX = Math.min(mMaxRadiusX, borderRadius[3]);
            int bottomLeftRadiusY = Math.min(mMaxRadiusY, borderRadius[3]);

            //绘制左上圆角,通过旋转来达到圆角的效果,本质上其实绘制的是圆弧
            if (topLeftRadiusX < borderWidth || topLeftRadiusY < borderWidth) {

                RectF arc1 = new RectF(0, 0, topLeftRadiusX * 2, topLeftRadiusY * 2);
                paint.setStyle(Paint.Style.FILL);
                canvas.drawArc(arc1, 180, 90, true, paint);
            } else {
                RectF arc1 = new RectF(borderWidth / 2, borderWidth / 2, topLeftRadiusX * 2 - borderWidth / 2, topLeftRadiusY * 2 - borderWidth / 2);
                paint.setStyle(Paint.Style.STROKE);
                canvas.drawArc(arc1, 180, 90, false, paint);
            }
            //绘制上方的边框
            canvas.drawLine(topLeftRadiusX, borderWidth / 2, width - topRightRadiusX, borderWidth / 2, paint);

            //绘制右上圆角
            if (topRightRadiusX < borderWidth || topRightRadiusY < borderWidth) {
                RectF arc2 = new RectF(width - topRightRadiusX * 2, 0, width, topRightRadiusY * 2);
                paint.setStyle(Paint.Style.FILL);
                canvas.drawArc(arc2, 270, 90, true, paint);
            } else {
                RectF arc2 = new RectF(width - topRightRadiusX * 2 + borderWidth / 2, borderWidth / 2, width - borderWidth / 2, topRightRadiusY * 2 - borderWidth / 2);
                paint.setStyle(Paint.Style.STROKE);
                canvas.drawArc(arc2, 270, 90, false, paint);
            }
            //绘制右边边框
            canvas.drawLine(width - borderWidth / 2, topRightRadiusY, width - borderWidth / 2, height - bottomRightRadiusY, paint);
            //绘制右下圆角
            if (bottomRightRadiusX < borderWidth || bottomRightRadiusY < borderWidth) {
                RectF arc3 = new RectF(width - bottomRightRadiusX * 2, height - bottomRightRadiusY * 2, width, height);
                paint.setStyle(Paint.Style.FILL);
                canvas.drawArc(arc3, 0, 90, true, paint);
            } else {
                RectF arc3 = new RectF(width - bottomRightRadiusX * 2 + borderWidth / 2, height - bottomRightRadiusY * 2 + borderWidth / 2, width - borderWidth / 2, height - borderWidth / 2);
                paint.setStyle(Paint.Style.STROKE);
                canvas.drawArc(arc3, 0, 90, false, paint);
            }
            //绘制底部边框
            canvas.drawLine(bottomLeftRadiusX, height - borderWidth / 2, width - bottomRightRadiusX, height - borderWidth / 2, paint);
            //绘制左下圆角
            if (bottomLeftRadiusX < borderWidth || bottomLeftRadiusY < borderWidth) {
                RectF arc4 = new RectF(0, height - bottomLeftRadiusY * 2, bottomLeftRadiusX * 2, height);
                paint.setStyle(Paint.Style.FILL);
                canvas.drawArc(arc4, 90, 90, true, paint);
            } else {
                RectF arc4 = new RectF(borderWidth / 2, height - bottomLeftRadiusY * 2 + borderWidth / 2, bottomLeftRadiusX * 2 - borderWidth / 2, height - borderWidth / 2);
                paint.setStyle(Paint.Style.STROKE);
                canvas.drawArc(arc4, 90, 90, false, paint);
            }
            //绘制左边边框
            canvas.drawLine(borderWidth / 2, topLeftRadiusY, borderWidth / 2, height - bottomLeftRadiusY, paint);
        } else {
            //如果没有传递圆角的参数,直接绘制即可
            canvas.drawRect(rect, paint);
        }
    } else {
        //当边框的宽度不同时,绘制不同的线粗,通过borderWidthLeft,rect.top,rect.bottom来确定每根线所在的位置
        if (borderWidthLeft > 0) {
            paint.setStrokeWidth(borderWidthLeft);
            canvas.drawLine(borderWidthLeft / 2, rect.top, borderWidthLeft / 2, rect.bottom, paint);
        }
        if (borderWidthTop > 0) {
            paint.setStrokeWidth(borderWidthTop);
            canvas.drawLine(rect.left, borderWidthTop / 2, rect.right, borderWidthTop / 2, paint);
        }
        if (borderWidthRight > 0) {
            paint.setStrokeWidth(borderWidthRight);
            canvas.drawLine(rect.right - borderWidthRight / 2, rect.top, rect.right - borderWidthRight / 2, rect.bottom, paint);
        }
        if (borderWidthBottom > 0) {
            paint.setStrokeWidth(borderWidthBottom);
            canvas.drawLine(rect.left, rect.bottom - borderWidthBottom / 2, width, rect.bottom - borderWidthBottom / 2, paint);
        }
    }
}

} ```

效果

image.png

相应代码里都有注释,代码本质是通过绘制四根线来实现边框的效果,通过我们传递的两个参数,一个是边框宽度,利用数组,拥有更强的扩展性,可以设置四个方向的线粗.第二个是圆角度数,顺序分别是左上,右上,右下,左下.

当我们的圆角有参数时,线的宽度是有改变的,会稍微短一点,留给矩形控件,防止过度绘制.

Drawable drawable = new GradientDrawable() { @Override public void draw(Canvas canvas) { super.draw(canvas); drawBorder(canvas, borderColor, borderWidths, borderRadius); } };

这一部分代码你也可以使用BitmapDrawable,不过编译器会提示过时,问题不大,也能运行.

这种代码我不知道该怎么解释,相应的RectF,canvas的构造方法我都介绍吐了,都是一个样子,只不过计算宽高很复杂而已,总之思路就向上面说的一样.