Android自定义View Paint

本文介绍了在Android应用中使用Paint对象实现画笔模式、线条绘制、圆与矩形绘制,以及圆环中的文字排版技巧,包括strokeCap和strokeJoin设置,以及如何利用breakText和ellipsize进行文本优化。

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/123524086
本文出自【赵彦军的博客】

本文示例代码详见:https://gitee.com/zhaoyanjun/text-draw

画笔模式

mPaint.setStyle(Paint.Style.FILL);  //设置画笔模式为填充
  • STROKE //描边
  • FILL //填充
  • FILL_AND_STROKE //描边加填充

实验代码

Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(40);     //为了实验效果明显,特地设置描边宽度非常大

// 描边
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(200,200,100,paint);

// 填充
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(200,500,100,paint);

// 描边加填充
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(200, 800, 100, paint);

在这里插入图片描述

画线条

class MyView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {

    private val paint = Paint()

    init {
        paint.color = resources.getColor(R.color.black, null) //画笔颜色
        paint.isAntiAlias = true //抗锯齿
        paint.strokeWidth = 30f //画笔宽度
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val startX = width / 2f
        val startY = height / 2f
        //画线条
        canvas.drawLine(startX, startY, startX + 200, startY + 200, paint)
    }
}

效果图

在这里插入图片描述

setStrokeCap(Paint.Cap cap)

设置线冒样式,取值有

  • Cap.ROUND(圆形线冒)
  • Cap.SQUARE(方形线冒)
  • Paint.Cap.BUTT(无线冒)

注意:冒多出来的那块区域就是线帽!就相当于给原来的直线加上一个帽子一样,所以叫线帽

在这里插入图片描述

setStrokeJoin(Paint.Join join)

设置线段连接处样式,取值有:

  • Join.MITER(结合处为锐角)、
  • Join.Round(结合处为圆弧)、
  • Join.BEVEL(结合处为直线)

在这里插入图片描述

画圆

canvas.drawCircle(startX, startY, 100f, paint)

在这里插入图片描述

画矩形

canvas.drawRect(startX, startY, startX + 100, startY + 100, paint)

在这里插入图片描述

画弧形

canvas.drawArc(rectF, 30f, 180f, true, paint)

在这里插入图片描述

其他

setStrokeWidth(float width) 设置画笔宽度
setAntiAlias(boolean aa) //抗锯齿

清空画笔样式

//清空画笔复位
reset()

源码如此:
在这里插入图片描述

实战-绘制圆环

效果图

在这里插入图片描述

class MyView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {

    private val paint = Paint()
    private val radius = 300f

    init {
        paint.color = resources.getColor(R.color.black, null) //画笔颜色
        paint.isAntiAlias = true //抗锯齿
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val startX = width / 2f
        val startY = height / 2f

        //绘制圆环
        paint.color = Color.parseColor("#dddddd")
        paint.strokeWidth = 30f
        paint.style = Paint.Style.STROKE
        canvas.drawCircle(startX, startY, radius, paint)

        //绘制弧形
        paint.color = Color.RED
        paint.strokeCap = Paint.Cap.ROUND
        val rectF = RectF(startX - radius, startY - radius, startX + radius, startY + radius)
        canvas.drawArc(rectF, -90f, 225f, false, paint)
    }
}

实战-圆环中绘制文字

在这里插入图片描述
重要的是文字居中显示。

计算文字在圆环中的居中文字,有两种方式:

  • 方式一:paint.getTextBounds(content, 0, content.length, bounds)
  • 方式二:val fontMetrics = paint.fontMetrics

具体示例,如下所示:

class MyView(context: Context, attrs: AttributeSet?) : View(context, attrs) {

    private val paint = Paint()
    private val radius = 300f

    init {
        paint.isAntiAlias = true //抗锯齿
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val startX = width / 2f
        val startY = height / 2f

        //绘制圆环
        paint.color = Color.parseColor("#dddddd")
        paint.strokeWidth = 30f
        paint.style = Paint.Style.STROKE
        canvas.drawCircle(startX, startY, radius, paint)

        //绘制弧形
        paint.color = Color.RED
        paint.strokeCap = Paint.Cap.ROUND
        val rectF = RectF(startX - radius, startY - radius, startX + radius, startY + radius)
        canvas.drawArc(rectF, -90f, 225f, false, paint)

        //绘制文字
        val content = "ababcfp"
        paint.textSize = 80f
        paint.style = Paint.Style.FILL
        //居中绘制
        paint.textAlign = Paint.Align.CENTER
        //加载字体
        paint.typeface = ResourcesCompat.getFont(context, R.font.fangzheng)

        //获取文字的的边界,这个边界基于文字baseline
        val bounds = Rect()
        paint.getTextBounds(content, 0, content.length, bounds)
        //获取边界的高度
        val boundHeight = bounds.bottom - bounds.top
        canvas.drawText(content, startX, startY + boundHeight / 2, paint)

        //方式二
        val fontMetrics = paint.fontMetrics
        val boundHeight2 = fontMetrics.descent - fontMetrics.ascent
        canvas.drawText(content, startX, startY + boundHeight2 / 2, paint)
    }
}

breakText 计算文本显示长度

测量文本,如果测量的宽度超过maxWidth,则提早停止

int breakText(char[] text, int index, int count, float maxWidth, float[] measuredWidth);
int breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth);
int breakText(CharSequence text, int start, int end, boolean measureForwards, 
float maxWidth, float[] measuredWidth);

//measureForwards:true则从首个字符开始测量,否则从最后一个字符开始测量
//maxWidth:可接受的最大长度
//count:绘制字符总数
//measureWidth:绘制的字符的实际宽度

案例:计算宽度为 100dp 的 TextView 能在屏幕上显示多少个字符

val textString = "今天星期一,好开心,天气真好,适合出去玩啊啊啊"

val maxWidth = dip2px(this, 100f).toFloat()
val paint = binding.text.paint
val measuredWidth = FloatArray(1)
val count = paint.breakText(textString, true, maxWidth, measuredWidth)
Log.d("yu--", "字符宽度 count:$count")

dip2px 工具方法

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    fun dip2px(context: Context, dpValue: Float): Int {
        val scale = context.resources.displayMetrics.density
        return (dpValue * scale + 0.5f).toInt()
    }

日志输出:

D/yu--: 字符宽度 count:7

意味着 textView 宽度100dp, 一行最多显示 7个字符 。

TextUtils.ellipsize

计算 textview 显示范围,如果超出最大宽度,用省略号代替。

val text = "1234567890今天不错,星期四"
val paint = binding.tv.paint
val width = binding.tv.width.toFloat()
val result = TextUtils.ellipsize(text, paint, width, TextUtils.TruncateAt.END, false, object :TextUtils.EllipsizeCallback {
     override fun ellipsized(start: Int, end: Int) {
          Log.d("yu--", "ellipsized $start $end")
     }
 })
binding.tv.text = result
Log.d("yu--", " $result")

//日志输出
D/yu--: ellipsized 10 18
D/yu--:  1234567890

显示效果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值