Rahul
Rahul

Reputation: 1667

Drawing lines with different color in Canvas

This is the first time I am working with the Canvas class in Android.

What I want is to draw lines in different colors on the canvas. I have an issue I cannot resolve.

I draw the first line in black, after I change the color to red and try to draw a second line in red, and the first line drawn in black color changes to red.

Code I used is:

import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class DrawView extends View implements OnTouchListener 
{
    private Canvas      m_Canvas;

    private Path        m_Path;

    private Paint       m_Paint;

    ArrayList<Pair<Path, Paint>> paths = new ArrayList<Pair<Path, Paint>>();

    ArrayList<Pair<Path, Paint>> undonePaths = new ArrayList<Pair<Path, Paint>>(); 

    private float mX, mY;

    private static final float TOUCH_TOLERANCE = 4;

//  Bitmap canvasBackground;

    public DrawView(Context context) 
    {
        super(context);
        setFocusable(true);
        setFocusableInTouchMode(true);      
        this.setOnTouchListener(this);

        onCanvasInitialization();
    }      

    public void onCanvasInitialization()
    {
        m_Paint = new Paint();
        m_Paint.setAntiAlias(true);
        m_Paint.setDither(true);
        m_Paint.setColor(Color.parseColor("#37A1D1"));
        m_Paint.setStyle(Paint.Style.STROKE);
        m_Paint.setStrokeJoin(Paint.Join.ROUND);
        m_Paint.setStrokeCap(Paint.Cap.ROUND);
        m_Paint.setStrokeWidth(2);      

        m_Canvas = new Canvas();

        m_Path = new Path();
        Paint newPaint = new Paint(m_Paint);
        paths.add(new Pair<Path, Paint>(m_Path, newPaint));

//      Bitmap canvas = BitmapFactory.decodeResource(getResources(), R.drawable.theme1_img_note).copy(Bitmap.Config.ARGB_8888, true);
//      canvasBackground = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888 );
//      m_Canvas = new Canvas(canvasBackground);        
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
    {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {      
        for (Pair<Path, Paint> p : paths) 
        {
            canvas.drawPath(p.first, p.second);
        }
    }

    public boolean onTouch(View arg0, MotionEvent event) 
    {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) 
        {
            case MotionEvent.ACTION_DOWN:
            touch_start(x, y);
            invalidate();
            break;
            case MotionEvent.ACTION_MOVE:
            touch_move(x, y);
            invalidate();
            break;
            case MotionEvent.ACTION_UP:
            touch_up();
            invalidate();
            break;
        }
        return true;
    }

    private void touch_start(float x, float y) 
    {
        m_Path.reset();
        m_Path.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y) 
    {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) 
        {
            m_Path.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
            mX = x;
            mY = y;
        }
    }
    private void touch_up() 
    {
        m_Path.lineTo(mX, mY);

        // commit the path to our offscreen
        m_Canvas.drawPath(m_Path, m_Paint);

        // kill this so we don't double draw            
        m_Path = new Path();
        Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
        paths.add(new Pair<Path, Paint>(m_Path, newPaint));
    }

    public void onClickPenButton(int penWidth) 
    { 
        m_Paint.setStrokeWidth(penWidth);

        m_Path = new Path();
        Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
        paths.add(new Pair<Path, Paint>(m_Path, newPaint));             
    }

    public void onClickPenColorButton(int penColor) 
    {       
        m_Paint.setColor(penColor);

        Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
        paths.add(new Pair<Path, Paint>(m_Path, newPaint));
    }

    public void onClickUndo () 
    { 
        if (paths.size()>0) 
        { 
            undonePaths.add(paths.remove(paths.size()-1));
            invalidate();
        }
        else
        {

        }
    }

    public void onClickRedo ()
    {
        if (undonePaths.size()>0) 
        { 
            paths.add(undonePaths.remove(undonePaths.size()-1)); 
            invalidate();
        } 
        else 
        {

        }
    }
}

Upvotes: 3

Views: 4673

Answers (2)

abbujaansboy
abbujaansboy

Reputation: 99

You will have to create seperate objects for both PATHs and PAINTs,then create properties of each paint object separately. And then in onDraw function, you'll have to specify the canvas drawCircle method for each path and its corresponding paint.

For example, I had a similar issue and it took me a while to solve it.

First I wanted to have a view where I can draw a line. The code was:

package com.example.ex13_singletouch

import android.content.Context
import android.graphics.*
import android.view.MotionEvent
import android.view.View

class SingleTouchView(context: Context): View(context) {    //AFTER COLON : WAS SOMETHING THAT WOULD BE RETURNED
    private val paint= Paint()
    private val path = Path()
    //TO CREATE THE X-Y POSITION OF THE FINGER ON THE SCREEN
    private var evenX: Float = 0F
    private var evenY: Float = 0F
    private var fingerDown = false  //CHECKS IF YOUR FINGER IS TOUCHING THE SCREEN OR NOT

    //INITIALIZATION
    init {
        paint.isAntiAlias = true
        paint.strokeWidth = 6F
        paint.strokeJoin = Paint.Join.ROUND
        paint.color = Color.BLACK
        paint.style = Paint.Style.STROKE
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        evenX = event!!.x
        evenY = event!!.y

        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                fingerDown = true
                path.moveTo(evenX,evenY)
                return true
            }
            MotionEvent.ACTION_MOVE -> {
                path.lineTo(evenX,evenY)    //WHEN YOU MOVE YOUR FINGER ON THE SCREEM, X AND Y KEEPS UPDATING AND IT DRAWS A LINE
            }
            MotionEvent.ACTION_UP -> {
                fingerDown = false
            }
            else -> return false
        }
        invalidate()    //JUMP TO THE ON DRAW TO DRAW SOMETHING THAT WE DEFINE
        return super.onTouchEvent(event)
    }

    //THIS FUNCTION SHAPES OUT THE THING WE DRAW. MEANS IT WILL MAKE A LINE STRAIGH OR WILL MAKE A CIRCLE ROUND
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas!!.drawPath(path, paint)
        if (fingerDown){
            canvas.drawCircle(evenX, evenY, 10F, paint)
        }
    }

}

Then I planned to draw lines that could have 3 differnt colours - Black, Blue and Red. Here is my updated code

package com.example.ex13_singletouch

import android.content.Context
import android.graphics.*
import android.view.MotionEvent
import android.view.View
import java.util.*
import kotlin.collections.ArrayList

class SingleTouchView(context: Context): View(context) {    //AFTER COLON : WAS SOMETHING THAT WOULD BE RETURNED
    //private val paint= Paint()
    private val paint1= Paint()
    private val paint2= Paint()
    private val paint3= Paint()
    //private val path = Path()
    private val path1 = Path()
    private val path2 = Path()
    private val path3 = Path()
    //TO CREATE THE X-Y POSITION OF THE FINGER ON THE SCREEN
    private var evenX: Float = 0F
    private var evenY: Float = 0F
    private var fingerDown = false  //CHECKS IF YOUR FINGER IS TOUCHING THE SCREEN OR NOT
    var colorControl = 1

    //INITIALIZATION
    init {
        paint1.isAntiAlias = true
        paint1.strokeWidth = 6F
        paint1.strokeJoin = Paint.Join.ROUND
        paint1.color = Color.BLACK
        paint1.style = Paint.Style.STROKE

        paint2.isAntiAlias = true
        paint2.strokeWidth = 6F
        paint2.strokeJoin = Paint.Join.ROUND
        paint2.color = Color.BLUE
        paint2.style = Paint.Style.STROKE

        paint3.isAntiAlias = true
        paint3.strokeWidth = 6F
        paint3.strokeJoin = Paint.Join.ROUND
        paint3.color = Color.RED
        paint3.style = Paint.Style.STROKE
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        evenX = event!!.x
        evenY = event!!.y

        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                fingerDown = true
                //path.moveTo(evenX,evenY)
                if (colorControl==1){
                    path1.moveTo(evenX,evenY)
                }
                if (colorControl==2){
                    path2.moveTo(evenX,evenY)
                }
                if (colorControl==3){
                    path3.moveTo(evenX,evenY)
                }
                return true
            }
            MotionEvent.ACTION_MOVE -> {
                //path.lineTo(evenX,evenY)    //WHEN YOU MOVE YOUR FINGER ON THE SCREEM, X AND Y KEEPS UPDATING AND IT DRAWS A LINE
                if (colorControl==1){
                    path1.lineTo(evenX,evenY)
                }
                if (colorControl==2){
                    path2.lineTo(evenX,evenY)
                }
                if (colorControl==3){
                    path3.lineTo(evenX,evenY)
                }
            }
            MotionEvent.ACTION_UP -> {
                fingerDown = false
                if (colorControl==3){
                    colorControl=1
                }
                else {
                    colorControl++
                }
            }
            else -> return false
        }
        invalidate()    //JUMP TO THE ON DRAW TO DRAW SOMETHING THAT WE DEFINE
        return super.onTouchEvent(event)
    }

    //THIS FUNCTION SHAPES OUT THE THING WE DRAW. MEANS IT WILL MAKE A LINE STRAIGH OR WILL MAKE A CIRCLE ROUND
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas!!.drawPath(path1, paint1)
        canvas!!.drawPath(path2, paint2)
        canvas!!.drawPath(path3, paint3)
    }
}

You can look at the differences between these 2 codes and see for the changes I made. I hope this is helpful.

Upvotes: 0

iTech
iTech

Reputation: 18460

You have only one m_Paint object, so when onDraw is called it will draw all paths using the last color you have set.

You can store the color for each path and inside onDraw you have to set the color before drawing the path

Edit

Here is the outline for a quick solution, night not be the most elegant one:

  • A HashMap with the path as key and the Color as value
  • In onClickPenColorButton save the color to an instance say currentColor
  • In touch_start you can push the path object with the currentColor into the HashMap
  • Modify onDraw to get the color for each path. This code is only for illustration so modify it as needed.

    protected void onDraw(Canvas canvas)
    {                           
       for (Path p : paths)
       {  
          // Assuming your HashMap variable is pathColor
          m_Paint.setColor(pathColor.get(p));
    
          canvas.drawPath(p, m_Paint);
       }
    }
    

Upvotes: 4

Related Questions