绘制空心圆stroke问题

stroke并不是在圆的半径外面,再往外绘制一个圆,而是就在圆周处绘制,绘制粗细等于paint.strokeWidth

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class CircleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {

val paint = Paint(Paint.ANTI_ALIAS_FLAG)
val RADIUS = 150F.dp
var centerX = 0f
var centerY = 0f
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
centerX = w/2f
centerY = h/2f
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 描个灰色的边,宽15dp
paint.color = Color.LTGRAY
paint.strokeWidth = 15f.dp
paint.style = Paint.Style.STROKE
canvas.drawCircle(centerX,centerY,RADIUS,paint)
// 描个青色的边,宽8dp
paint.color = Color.CYAN
paint.strokeCap = Paint.Cap.ROUND
paint.strokeWidth = 8f.dp
canvas.drawCircle(centerX,centerY,RADIUS,paint)
// 在圆周画2条竖线,表示圆周在哪
paint.strokeWidth = 1f
paint.color = Color.RED
canvas.drawLine(centerX-RADIUS,0f,centerX-RADIUS,1000f,paint)
canvas.drawLine(centerX+RADIUS,0f,centerX+RADIUS,1000f,paint)

}
}

1.png

可以看到,stroke并不是在圆的外面绘制,而是就在圆周处绘制,15的宽度会是圆外画7,圆内画7。
顺便画圆也可以用drawArc和drawOval来实现。


进入正题

绘制文字纵向居中和左对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class CircleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {

val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND

strokeWidth = 15f.dp
textSize = 34f.dp
}
val RADIUS = 150F.dp
var centerX = 0f
var centerY = 0f
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
centerX = w / 2f
centerY = h / 2f
}

val testStr = "abab"
val textBound = Rect()
val fontMetrics = Paint.FontMetricsInt()
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

paint.color = Color.LTGRAY
canvas.drawArc(
centerX - RADIUS,
centerY - RADIUS,
centerX + RADIUS,
centerY + RADIUS,
-90f,
360f,
false,
paint
)
paint.color = Color.BLUE
canvas.drawArc(
centerX - RADIUS,
centerY - RADIUS,
centerX + RADIUS,
centerY + RADIUS,
-90f,
250f,
false,
paint
)
// 画个圆心,定个位置,方便比较文字的纵向坐标
paint.strokeWidth = 5f.dp
canvas.drawPoint(centerX, centerY, paint)
paint.strokeWidth = 0f

// ————————————————————— 下面为调整文字居中对齐的2种方式 ———————————————————————

// 方法一、静态文字,可以直接测量高度,这是从[1,0]开始测量的,top为负数
// [2,-39][112,1]
paint.getTextBounds(testStr, 0, testStr.length, textBound)
canvas.drawText(
testStr,
centerX - (textBound.right + textBound.left) / 2,
centerY - (textBound.top + textBound.bottom) / 2,
paint
)
// 方法二、动态文字,可以用文字五条定位线中的top~bottom (包含留白)或 ascent ~ descent(文字主体部分)
// FontMetricsInt: top=-54 ascent=-47 descent=12 bottom=14 leading=0
// 和textBound一样,这玩意也是top负数
paint.getFontMetricsInt(fontMetrics)
Log.d(javaClass.simpleName,fontMetrics.toString())
canvas.drawText(
testStr, centerX - (textBound.right + textBound.left) / 2,
centerY - (fontMetrics.top + fontMetrics.bottom) / 2,
paint
)


// ————————————————————— 下面为调整文字左对齐 ———————————————————————
// 横坐标为0的话,如果2行的字体大小差距很大,依然不会对齐,因为每个字符有自然间距
// 通过textBound.left进行调整后,基本就对齐了,但还是会差几个像素,这是系统问题,没法解决
paint.color = Color.BLACK
paint.textSize = 14f.dp
paint.getTextBounds(testStr,0,testStr.length,textBound)
canvas.drawText(testStr, -textBound.left.toFloat(),-textBound.top.toFloat(), paint)

paint.textSize = 150f.dp
paint.getTextBounds(testStr,0,testStr.length,textBound)
canvas.drawText(testStr, -textBound.left.toFloat(),-textBound.top.toFloat(), paint)


}
}

绘制文字跳过图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class MultiTextView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
val testStr =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et turpis tortor. Sed efficitur arcu tellus, vitae elementum ante bibendum in. Proin id tortor sapien. Donec sit amet dictum elit. Nam bibendum quam elit, vel malesuada dolor rhoncus sit amet. Aliquam congue finibus dui, ut finibus tellus sollicitudin vitae. Aliquam finibus felis sed imperdiet viverra. Phasellus id lorem justo. Donec vehicula a augue at lacinia. Etiam tincidunt eros sed euismod aliquam.\n" +
"In hac habitasse platea dictumst. Ut tempor maximus mauris et semper. In facilisis, augue sit amet imperdiet porttitor, turpis sapien pharetra quam, vitae porta neque ligula nec ligula. Sed sed elit erat. Praesent viverra dui ac velit pretium, non volutpat nisi euismod. Praesent at condimentum felis, ut convallis augue. Nam ut velit eros. Curabitur tristique, nisl eget porta pretium, justo quam mollis nisi, at posuere orci dolor eu enim. Integer ac dui vitae tellus vestibulum egestas. Sed congue vitae elit ac posuere. Pellentesque ut risus id nibh porttitor consequat ut luctus neque. Duis a lectus facilisis, eleifend massa a, lobortis libero."

val paint = Paint(Paint.ANTI_ALIAS_FLAG)
val bmpWidth = 200f.dp.toInt()
val bmpTopPadding = 100
val options = BitmapFactory.Options()
val fontMetrics = Paint.FontMetricsInt()
lateinit var bmp: Bitmap
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)

options.inJustDecodeBounds = true // 不实际读取像素,只测量大小,几乎不消耗时间和空间
BitmapFactory.decodeResource(resources, R.drawable.kana, options)
options.inJustDecodeBounds = false
options.inDensity = options.outWidth
options.inTargetDensity = bmpWidth
bmp = BitmapFactory.decodeResource(resources, R.drawable.kana, options)
}

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

// 绘制图片 图片在[onSizeChanged]中初始化了
canvas.drawBitmap(bmp, width.toFloat() - bmpWidth, bmpTopPadding.toFloat(), paint)

// 绘制文字,绕过图片
paint.textSize = 20f.dp
paint.getFontMetricsInt(fontMetrics)
var start = 0
var verticalOffset = -fontMetrics.top //纵向初始偏移量
while (start < testStr.length) {
var maxWidth =
if (verticalOffset + fontMetrics.bottom < bmpTopPadding
|| verticalOffset + fontMetrics.top > bmpTopPadding + bmp.height
) {
width
} else {
width - bmpWidth
}
val count =
paint.breakText(testStr, start, testStr.length, true, maxWidth.toFloat(), null)
canvas.drawText(testStr, start, start + count, 0f, verticalOffset.toFloat(), paint)
start += count
//fontSpacing是基于字体和textSize定下的一行所占空间
verticalOffset += paint.fontSpacing.toInt()
}
}
}

2.png