[ Android ] customView의 onDraw 함수 디버깅 기록
1. 문제 상황
CumtomView 특정 점이 터치가 되면 리스너로 View(MVP 모델의 ) 전달하고 해당 위치 데이터를 이용해 Presenter 를 거쳐 Model을 업데이트 한 뒤 다시 model 의 바뀐 내용대로 포문을 돌면서 커스텀 뷰를 그렸다.
즉 다시말해 customView 밖에서 리스트를 순회하면서 customView의 onDraw 및 invalidate() 를 여러번 호출 상황이었다.
그런데 커스텀뷰가 잘 그려질 때도 있고 그려지지 않을 때도 있었다.
특히 모델의 데이터 즉 리스트가 커질 수록 이 문제는 두드러지게 나타났다.
data의 흐름을 따라 디버깅을 해도 별 문제가 없었고 하루를 통째로 날렸다. ㅜㅜㅜ
// 문제가 발생하던 코드
// 이하 Activity
// ...
override fun showRectangle(rectangleList: MutableList<Rectangle>) {
rectangleList.forEach {
myCanvas.drawRectangle(it)
}
}
// 이하 MyCanvas -> cusotmview
// ...
fun drawRectangle(rec: Rectangle) = with(rec) {
rect =createRectF(rec)
if (rec == selectedRectangle) {
paint.apply {
this.style = Paint.Style.STROKE
this.color = Color.BLACK
}
} else {
paint.apply {
this.style = Paint.Style.FILL
this.color = Color.argb(getAlpha(), rgba.r, rgba.g, rgba.b)
}
}
paint.color = Color.argb(getAlpha(), rgba.r, rgba.g, rgba.b)
invalidate()
}
public override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.drawRect(rect, paint)
}
2. 원인 파악
문제는 View 생성주기에 대한 이해부족으로 invalidate()를 남발한 것이 었다. 아래 view 생명주기를 고려하여 위 코드를 살펴보면
1. 리스트에서 invalidate() 를 호출한다.
2. 이에따라 dipatchToDraw() 부터 호출해나간다.
3. onDraw 까지 다 마치지 않은 상황에서 다음 리스트 원소를 받아 또 invalidate() 를 실행
즉 정상동작할때는 onDraw() 가 끝나는 시점이 리스트의 다음 원소에 접근해 invlidate() 를 호출하는 시점보다 먼저일때였고 이상이 있을때는 반대였던 것이다.
3. 해결 방안
invalidate() 를 한번만 호출 하면되는 상황을 만들면 되었다.
기존에 리스틀 customView 내부로 가져와서 onDraw 안에서 순회하면서 그려나가고 모든 작업이 끝났을때 invalidate() 를 호출함으로써 해결할 수 있었다.
// customView
class MyCanvas(context: Context,) : View(context) {
fun drawRectangle(recList: MutableList<Rectangle>) {
rectangles = recList
invalidate()
}
public override fun onDraw(canvas: Canvas?) {
selectedRectangles.forEach {
val paint = setBorderPaint(it)
rect = createRecF(it)
canvas?.drawRect(rect, paint)
}
super.onDraw(canvas)
}
class MainActivity : AppCompatActivity(), CanvasContract.View {
private lateinit var binding: ActivityMainBinding
lateinit var canvasPresent: Present
lateinit var myCanvas: MyCanvas
...
override fun showRectangle(rectangleList: MutableList<Rectangle>) {
myCanvas.drawRectangle(rectangleList)
}
}