[Android] - CustomView DrawText
이번 프로젝트를 진행하면서 CustomView를 굉장히 많이 다뤘었고, CustomView에서 텍스트 관련 작업을 맡았는데,
CustomView에서 DrawText에 대한 자료도 옛날 자료가 많고, 맨땅에 헤딩하듯이 개발하면서 겪은 경험입니다..
우리 팀 프로젝트처럼 디테일하게 Text에 대해서 다루지 않을 수도 있지만,, 많은 도움 되었으면 합니다ㅎ
DrawText란?
Canvasd에서 제공하는 함수입니다.
여러 오버로딩된 함수가 많지만 보편적으로 사용되는 함수는 아래와 같습니다.
public void drawText (String text,
float x,
float y,
Paint paint)
함수의 내용은 그리고자 하는 text를 (x, y)를 시작점으로 해서 paint 객체를 이용해서 그리는 것입니다.
여기서 paint의 객체는 그릴 텍스트의 크기, 폰트, 컬러, 정렬기준과 같이 다양한 것을 설정할 수 있습니다.
val textPaint =
TextPaint().apply {
color = Color.RED
isAntiAlias = true
textAlign = Paint.Align.CENTER
textSize = Dp(12f).toPx(context)
typeface = ResourcesCompat.getFont(context, R.font.pretendard_bold)
}
코드와 같이 텍스트가 그려질 x, y 좌표를 지정해 주고 drawText를 호출하면 텍스트가 그려질 것이라고 생각했습니다.
하지만.. 왜인지 뭔가 어색한 느낌이 들더라고요...
drawRectangle을 통해서 사각형을 그리고, 사각형의 중심좌표에 텍스트를 그렸습니다.
가만.. 보면 텍스트가 중앙보다 위로 가 있지 않나요?? 제가 눈이 안 좋아서 그런갑 보다 하고 있었는데
팀원들도 텍스트가 조금 위에 붕 떠있는 거 같아요!!라고 얘기해 주셨고, drawText에 대해서 잘못 이해하고 있었던 걸로 판단했다.
즉 지금 그려지는 상황은 아래와 같은 상황인 것이다..
나는 텍스트가 사각형 중앙에 그려지길 원했지만 y좌표를 중앙으로 주면 그림과 같이 중앙을 기준으로 텍스트가 붕 떠버리는 것이다.
해결방법은 간단하다.
y값을 변화시켜 주면 된다.
많은 고민을 하다가 텍스트의 높이 값을 측정해서 그 절반을 그리고자 하는 좌표와 더해주면 되지 않을까??라고 생각했다.
그렇다면 텍스트의 높이를 구하는 방법이 필요하다.
우선 drawText에서 text의 width를 구하는 방법은 간단하다.
textPaint().measureText(text)
로그를 찍어보니 텍스트별로 width가 다르게 나온 것을 확인할 수 있었다.
이 width는 정말 텍스트 하나의 크기 * 텍스트 글자의 수가 아닌 실제 텍스트가 그려지는 width의 길이라서
정확한 width인지 측정하기가 어려웠다. width의 영향을 주는 것은 텍스트의 폰트와 그려지는 텍스트의 모양(a,1,2,!~)등 모두 다 다르기 때문이다. 하지만 나는 정확한 width가 아닌 내가 그리고자 하는 폰트와 텍스트에 대한 width가 필요했기 때문에 상관없었다.
다음은 height를 구해야 한다.
height를 구하는 방법은 두 가지가 있다.
위 그림과 같이 drawText의 좌표계를 이용해서 Ascent와 Decent의 값을 더하면 height가 된다.
Ascent는 textPaint가 그린 글자 중 가장 높은 글자의 높이이고, decent는 가장 낮은 높이가 된다.
다음 방법으로는 textPaint의 bound(사각형)을 구해서 높이를 측정하는 방식이다.
그림이 정확하지 않지만, textPaint의 bound를 구해서 사각형의 높이를 구할 수 있다.
실제 boudn는 정확하게 text를 크기에 맞게 감싼다.
val bound = textPaint.getTextBounds(line, 0, line.length, bounds)
이렇게 해서 bound.height를 통해서 text의 높이를 구할 수 있었다.
확실히 위 사각형 사진보다 텍스트가 중앙에 그려지는 것을 확인할 수 있다.
y좌표는 123123이 그려지는 텍스트의 높이가 있을 것이다.
그리고 사각형의 중앙좌표 y가 있을 것이다.
--> newY = y + (123123의 높이의 절반값)을 더하면 다음과 같이 중앙에 위치하도록 된다.
문제는 또 다른 곳에서 발생했다..
여러 줄에 텍스트에 대한 대응의 코드가 없었다.
즉 사용자가 12334566 이렇게 입력할 수도 있지만
1
2
3
4
5
6과 같이 입력한 경우 대응이 없었다. 이렇게 입력하는 경우 drawText의 text는 1\n2\n3\n4\n5\n으로 판단하지만 실제 높이의 길이는 한 줄로 판단한 것이었다.
따라서 줄 바꿈으로 split 한 다음에 텍스트에 대한 높이를 구했다.
private fun sumTotalHeight(description: String): Float {
val bounds = Rect()
var sum = 0f
description.split("\n").forEach { line ->
drawInfo.textPaint.getTextBounds(line, 0, line.length, bounds)
sum += bounds.height()
}
return sum
}
이렇게 텍스트에 대해서 높이를 구했고, 높이를 사각형의 높이로 대입해서 그려보았다.
음.. 그려진 거 같은데 뭔가 조금 줄간의 간격이 있으면 좋을 것 같다고 판단했다.
그래서 sumTotalHeight를 구하는 과정에서 일종의 marin 값을 더해서 높이를 구했다.
즉 1의 높이 + 마진값 + 2의 높이 + 마진값 --> 이렇게 해서 각 텍스트마다 텍스트의 높이 + 마진값을 더해줬다.
수정 코드는 다음과 같다.
private fun sumTotalHeight(description: String): Float {
val bounds = Rect()
var sum = 0f
description.split("\n").forEach { line ->
drawInfo.textPaint.getTextBounds(line, 0, line.length, bounds)
sum += bounds.height() + drawInfo.lineHeight.dpVal
}
return sum
}
내가 의도했던 모양과 일치하게 텍스트와 사각형의 높이가 적용된 것을 확인할 수 있었다.
하지만 이렇게 각 줄마다 텍스트의 너비가 일정한 것은 대응이 되지 않았다.
1
12
123
1234
12345
1 이렇게 텍스트가 그려질 경우
기대했던 사각형의 모양은 왼쪽이었지만 실제로 오른쪽과 같은 결과가 나왔다.
즉 width값의 산정이 잘못된 것이었다.
처음엔 간단하게
textPaint().measureText(text)
위와 같은 코드로 widht를 업데이트했지만, 코드상으로 마지막줄에 적힌 width 값으로 사각형의 width가 계산되었고,
width도 각 줄마다 계산을 해서 최댓값으로 계산해줘야 한다는 것을 깨달았다.
private fun sumWidth(description: String): Float {
var sum = 0f
description.split("\n").forEach {
sum = maxOf(sum, drawInfo.textPaint.measureText(it))
}
return sum
}
여러 줄에 해당하는 가로와 세로의 길이를 성공적으로 대응할 수 있었다.
느낀점
실제로 xml에서 textView를 만들어서 text를 적는 단순한 작업을
customview를 통해서 하나하나 구현하려니 정말 생각해줘야 할게 많았다.
텍스트가 그려질 좌표와, 텍스트간의 간격등 신경써줘야 할 게 너무 많아서
수학적으로 괴로웠다..
참고
https://seminzzang.tistory.com/131
[안드로이드 스튜디오 정리#19] drawText, breakText
이 게시물은 다음 링크를 참조하여 학습했습니다. Paint | Android Developers android.net.wifi.hotspot2.omadm developer.android.com Android Center text on canvas I'm trying to display a text using the code below. The problem is that the tex
seminzzang.tistory.com
Canvas | Android Developers
developer.android.com