Search code examples
androidkotlincanvasviewdrawing

Could somebody explain to me how this custom View code works?


class DrawingView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
    private lateinit var mDrawPath: FingerPath
    private lateinit var mCanvasBitmap: Bitmap
    private lateinit var mCanvasPaint: Paint
    private lateinit var mDrawPaint: Paint
    private var mBrushSize = 0
    private var color = Color.BLACK
    private lateinit var canvas: Canvas

    init {
        setUpDrawing()
    }

    private fun setUpDrawing() {
        mDrawPaint = Paint()
        mDrawPath = FingerPath(color, mBrushSize.toFloat())
        mDrawPaint.color = color
        mDrawPaint.style = Paint.Style.STROKE
        mDrawPaint.strokeJoin = Paint.Join.ROUND
        mDrawPaint.strokeCap = Paint.Cap.ROUND
        mCanvasPaint = Paint(Paint.DITHER_FLAG)
        mBrushSize = 20

    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        canvas = Canvas(mCanvasBitmap)

    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawBitmap(mCanvasBitmap, 0f, 0f, mDrawPaint)
        if (!mDrawPath.isEmpty) {
            mDrawPaint.strokeWidth = mDrawPath.brushThickness
            mDrawPaint.color = mDrawPath.color
            canvas.drawPath(mDrawPath, mDrawPaint)
        }
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        val touchX = event?.x
        val touchY = event?.y

        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                mDrawPath.color = color
                mDrawPath.brushThickness = mBrushSize.toFloat()

                mDrawPath.reset()
                mDrawPath.moveTo(touchX!!, touchY!!)
            }

            MotionEvent.ACTION_MOVE -> {
                mDrawPath.lineTo(touchX!!, touchY!!)

            }

            MotionEvent.ACTION_UP -> {
                mDrawPath = FingerPath(color, mBrushSize.toFloat())
            }
            else -> return false
        }

        invalidate()
        return true
    }

    internal inner class FingerPath(var color: Int, var brushThickness: Float) : Path()
}

So, I'm taking a course about Android Development and the instructor is building a drawing app -- and I can't understand how to code works, because he's not explaining the 'why'; so, he types a lot of code without explaining why he uses that variable or why is he overring those functions; therefore, I don't understand how to code work. Could you help me in understanding how this code work?


Solution

  • class DrawingView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
        private lateinit var mDrawPath: FingerPath
        private lateinit var mCanvasBitmap: Bitmap
        private lateinit var mCanvasPaint: Paint
        private lateinit var mDrawPaint: Paint
        private var mBrushSize = 0
        private var color = Color.BLACK
        private lateinit var canvas: Canvas
    
        init {
    // init block will called first when instance will be created so we
    // are calling method setUpDrawing() as it is initialising everything
    // that required to draw like color , brush size , brush behaviour
    // (round , stroke etc .. ) . in simple manner , we can say painter is
    // collecting all required tools before starting to paint
    
            setUpDrawing() 
        }
    
        private fun setUpDrawing() {
            mDrawPaint = Paint()
            mDrawPath = FingerPath(color, mBrushSize.toFloat())
            mDrawPaint.color = color
            mDrawPaint.style = Paint.Style.STROKE
            mDrawPaint.strokeJoin = Paint.Join.ROUND
            mDrawPaint.strokeCap = Paint.Cap.ROUND
            mCanvasPaint = Paint(Paint.DITHER_FLAG)
            mBrushSize = 20
    
        }
    
     // this method is going to be called by system when size is going to be
    // changed so we are here creating blank board on which we are going to
    // draw
        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(w, h, oldw, oldh)
            mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
            canvas = Canvas(mCanvasBitmap)
    
        }
    
        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            canvas.drawBitmap(mCanvasBitmap, 0f, 0f, mDrawPaint)
            if (!mDrawPath.isEmpty) {
                mDrawPaint.strokeWidth = mDrawPath.brushThickness
                mDrawPaint.color = mDrawPath.color
                canvas.drawPath(mDrawPath, mDrawPaint) // drawing path on canvas
            }
        }
    
    // this method will be called by system when user is going to touch screen
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            val touchX = event?.x
            val touchY = event?.y
    
            when (event?.action) {
    // this event will be fired when user put finger on screen
                MotionEvent.ACTION_DOWN -> { 
                    mDrawPath.color = color
                    mDrawPath.brushThickness = mBrushSize.toFloat()
    
                    mDrawPath.reset() // reseting path before we set inital point
                    mDrawPath.moveTo(touchX!!, touchY!!)// set point from where drawing will be started 
                }
    
    // this event will be fired when user start to move it's fingure . this will be fired continually until user pickup fingure 
                MotionEvent.ACTION_MOVE -> {
                    mDrawPath.lineTo(touchX!!, touchY!!)
    
                }
    // this event will be fired when user will pick up fingure from screen
                MotionEvent.ACTION_UP -> {
                    mDrawPath = FingerPath(color, mBrushSize.toFloat())
                }
                else -> return false
            }
    
            invalidate() / /refreshing layout to reflect drawing changes
            return true
        }
    
        internal inner class FingerPath(var color: Int, var brushThickness: Float) : Path()
    }