ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ Android ] customView의 onDraw 함수 디버깅 기록
    Android & Kotlin 2022. 3. 4. 14:37

    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)
        }
    }

     

    댓글

Designed by Tistory.