Home > Blockchain >  What is wrong with my code. I am unable to draw on the screen
What is wrong with my code. I am unable to draw on the screen

Time:07-25

I have checked my code several times and don't know where is it going wrong. i am trying to build a drawing app and after doing all this code I am unable to get any output. Not able to draw on the canvas.

please help me get rid of this issue.

This is my DrawingView.kt

package com.example.drawingapp

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.core.content.res.ResourcesCompat


class DrawingView(context : Context, attrs:AttributeSet): View(context, attrs){

    private var myDrawPath: CustomPath? = null
    private var myCanvasBitmap: Bitmap? = null
    private var myDrawPaint: Paint? = null
    private var myCanvasPaint: Paint? = null
    private var myCanvas: Canvas?= null

    private var myBrushSize :Float = 0.toFloat()
    private  var myColor = Color.BLACK

    init{
        drawingSetUp()
    }

    private fun drawingSetUp(){
        myDrawPaint = Paint()
        myDrawPath = CustomPath(myColor,myBrushSize)
        myDrawPaint!!.color= myColor
        myDrawPaint!!.style = Paint.Style.STROKE
        myDrawPaint!!.strokeJoin =Paint.Join.ROUND
        myDrawPaint!!.strokeCap =Paint.Cap.ROUND
        myCanvasPaint= Paint(Paint.DITHER_FLAG)
        myBrushSize=20.toFloat()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        myCanvasBitmap= Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        myCanvas = Canvas(myCanvasBitmap!!)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        myCanvas!!.drawBitmap(myCanvasBitmap!!, 0f,0f , myCanvasPaint)

        if (!myDrawPath!!.isEmpty){
            myDrawPaint!!.strokeWidth = myDrawPath!!.brushThickness
            myDrawPaint!!.color= myDrawPath!!.color
            myCanvas!!.drawPath(myDrawPath!!, myDrawPaint!!)
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {

        val touchX = event?.x
        val touchY = event?.y

        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                myDrawPath!!.color = myColor
                myDrawPath!!.brushThickness = myBrushSize

                myDrawPath!!.reset()

                if (touchX != null) {
                    if (touchY != null) {
                        myDrawPath!!.moveTo(touchX, touchX)
                    }
                }
            }
            MotionEvent.ACTION_MOVE -> {
                if (touchX != null) {
                    if (touchY != null) {
                        myDrawPath!!.lineTo(touchX, touchY)
                    }
                }
            }
            MotionEvent.ACTION_UP->{
                myDrawPath = CustomPath(myColor, myBrushSize)
            }
            else->return false

        }
        invalidate()
return true
    }

    internal inner class CustomPath(var color:Int, var brushThickness:Float): Path(){

    }


}

This is my activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<com.example.drawingapp.DrawingView
    android:id="@ id/drawing_view"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"

    />

   </androidx.constraintlayout.widget.ConstraintLayout>

And this is my MainActivity.kt file

    package com.example.drawingapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


    }

}

CodePudding user response:

You're drawing to the wrong Canvas. You need to draw to the one passed in to onDraw, that's the one linked to what your View will actually display.

When you do this:

myCanvasBitmap= Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
myCanvas = Canvas(myCanvasBitmap!!)

you're creating your own, personal Bitmap, and creating your own personal Canvas to draw stuff on that bitmap. You can do that - but unless you draw that bitmap to the canvas onDraw gives you at some point, you'll never see it.

I'm assuming you wanted to do this:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    // draw your stored bitmap to the view's canvas
    canvas.drawBitmap(myCanvasBitmap!!, 0f,0f , myCanvasPaint)

    if (!myDrawPath!!.isEmpty){
        myDrawPaint!!.strokeWidth = myDrawPath!!.brushThickness
        myDrawPaint!!.color= myDrawPath!!.color
        // draw this stored path to the view's canvas, on top of
        // the stored bitmap you just drew
        canvas.drawPath(myDrawPath!!, myDrawPaint!!)
    }
}

i.e. drawing to canvas and not myCanvas. I don't know where you're actually drawing stuff to myCanvas though (to build up your stored image), you still need to do that so you have something to paint on the view's canvas. I'm assuming that happens elsewhere and the path you're drawing in onDraw is like a temporary overlay that isn't part of the stored image yet


Also just as a piece of advice, you need to avoid that !! stuff everywhere - that's a big sign you've made something nullable that shouldn't be nullable, and now you have to fight with the null-checking system because you're sure it can't be null but the system thinks it could be.

If you just move all your drawingSetup code into init, it'll see that you're assigning everything a value and you won't need to make them null. It's because you're assigning them through another function called from init that it can't be sure you're doing that - it's a limitation of the language unfortunately. Or you could just assign them directly:

private var myDrawPath = CustomPath(myColor,myBrushSize)
private lateinit var myCanvasBitmap: Bitmap
private var myDrawPaint = Paint().apply {
    color= myColor
    style = Paint.Style.STROKE
    strokeJoin = Paint.Join.ROUND
    strokeCap = Paint.Cap.ROUND
}
// etc

If you do want to keep that initialisation function (e.g. because you want to reset everything to normal) at least make your vars lateinit non-nullable types - if they're never going to be null when they're accessed, don't make them nullable! You just have to make sure lateinit stuff is assigned a value before something tries to read it - but the same is true for making something null and doing !! every time you access it, aka "trust me bro it's not null"

  • Related