Skip to content Skip to sidebar Skip to footer

Undo And Redo In Canvas For Android

I am using a customized version of FingerPaint for Android with some other features, like inserting images and moving them. I decided to implement an Undo&Redo, since it will m

Solution 1:

Try below code Draw View:

package com.draw;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;

import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

publicclassDrawViewextendsViewimplementsOnTouchListener {
    private Canvas  mCanvas;
    private Path    mPath;
    private Paint       mPaint;   
    private ArrayList<Path> paths = newArrayList<Path>();
    private ArrayList<Path> undonePaths = newArrayList<Path>(); 

    private Bitmap im;
    publicDrawView(Context context) 
    {
        super(context);
        setFocusable(true);
        setFocusableInTouchMode(true);      
        this.setOnTouchListener(this);
        mPaint = newPaint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(0xFFFFFFFF);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(6);
        mCanvas = newCanvas();
        mPath = newPath();
        paths.add(mPath);

        im=BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher);


    }               
        @OverrideprotectedvoidonSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
        }

        @OverrideprotectedvoidonDraw(Canvas canvas) {            

            for (Path p : paths){
                canvas.drawPath(p, mPaint);
            }
        }

        privatefloat mX, mY;
        privatestaticfinalfloatTOUCH_TOLERANCE=4;

        privatevoidtouch_start(float x, float y) {
            mPath.reset();
            mPath.moveTo(x, y);
            mX = x;
            mY = y;
        }
        privatevoidtouch_move(float x, float y) {
            floatdx= Math.abs(x - mX);
            floatdy= Math.abs(y - mY);
            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
                mX = x;
                mY = y;
            }
        }
        privatevoidtouch_up() {
            mPath.lineTo(mX, mY);
            // commit the path to our offscreen
            mCanvas.drawPath(mPath, mPaint);
            // kill this so we don't double draw            
            mPath = newPath();
            paths.add(mPath);
        }

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

            }
             //toast the user 
        }

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

           }
             //toast the user 
        }

    @OverridepublicbooleanonTouch(View arg0, MotionEvent event) {
          floatx= event.getX();
          floaty= 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;
          }
          returntrue;
    }
}

and Draw Activity layout code below:

<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical" ><FrameLayoutandroid:id="@+id/main_frame"android:layout_width="fill_parent"android:layout_height="250dp"></FrameLayout><Buttonandroid:id="@+id/button2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Redo" /><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Undo" /></LinearLayout>

and Draw Activity Class below code:

package com.draw;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

publicclassDrawextendsActivity {
     ImageView iv1;
    @OverridepublicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        finalDrawViewdrawView=newDrawView(this);
        setContentView(R.layout.main);
        FrameLayout frm_layout=(FrameLayout) findViewById(R.id.main_frame);
        frm_layout.addView(drawView);
        Button btn_undo=(Button) findViewById(R.id.button1);
        btn_undo.setOnClickListener(newOnClickListener() {

            @OverridepublicvoidonClick(View v) {
                // TODO Auto-generated method stub
                drawView.onClickUndo();
            }
        });

        Button btn_redo=(Button) findViewById(R.id.button2);
        btn_redo.setOnClickListener(newOnClickListener() {

            @OverridepublicvoidonClick(View v) {
                // TODO Auto-generated method stub
                drawView.onClickRedo();
            }
        });
    }

}

This is sample paint app with undo and redo operations in android,it work's perfectly for me!

Solution 2:

This is working code. I test it on my own app and it is working very good, may be it's helpful to others. Please comment on it.

publicclassMainextendsActivityimplementsOnColorChangedListener {
    // public static int selectedColor = Color.BLACK;publicstatic ArrayList<Path> mMaindialog;
    // private ArrayList<Path> undonePaths;// public int selectedcolor;privatestaticfinalStringCOLOR_PREFERENCE_KEY="color";
    private FrameLayout relativelayout;
    static String sdpath, location;
    Boolean i;
    // Instance variablesprivateBitmapmBitmap=null;
    Bitmap bitmap;
    private Paint mPaint, mBitmapPaint, mPaint1;
    private MyView mView;
    ImageView idd;
    // private Path mPath;intslll= Color.BLACK;
    Bitmapmap= ListView5.bMap;
    private Button ClearPaint, Colorpaint;
    Ghostdatabase gost;

    @OverridepublicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        idd = (ImageView) findViewById(R.id.imageView1);
        relativelayout = (FrameLayout) findViewById(R.id.frameLayout);

        DisplayMetricsmetrics= getBaseContext().getResources()
                .getDisplayMetrics();
        intw= metrics.widthPixels;
        inth= metrics.heightPixels;

        System.out.println(" width " + w);
        System.out.println(" height " + h);

        mView = newMyView(this, w, h);
        mView.setDrawingCacheEnabled(true);

        mPaint = newPaint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(4);

        ClearPaint = (Button) findViewById(R.id.ne);
        ClearPaint.setOnClickListener(newOnClickListener() {

            publicvoidonClick(View v) {
                // mBitmap.eraseColor(Color.TRANSPARENT);// mPath.reset();// mView.invalidate();

                mView.onClickUndo();

            }
        });
        Buttonsave22= (Button) findViewById(R.id.save);
        save22.setOnClickListener(newOnClickListener() {

            @OverridepublicvoidonClick(View v) {
                // TODO Auto-generated method stub
                File cacheDir;
                Toast.makeText(Main.this, "Photo", 500).show();
                Bitmap icon;
                relativelayout.setDrawingCacheEnabled(true);

                icon = Bitmap.createBitmap(relativelayout.getDrawingCache());
                Bitmapbitmap= icon;
                relativelayout.setDrawingCacheEnabled(false);
                // File mFile1 = Environment.getExternalStorageDirectory();Dated=newDate();
                StringfileName= d.getTime() + "mg1.jpg";

                FilestoragePath= (Environment.getExternalStorageDirectory());
                Filedest=newFile(storagePath + "/CityAppImages");

                if (!dest.exists()) {
                    dest.mkdirs();

                }

                FilemFile2=newFile(dest, fileName);
                sdpath = mFile2.getAbsolutePath();

                Log.d("qqqqqqqqqqqqqqqqqqqqqqq", "zzzzzzzz" + sdpath);
                try {
                    FileOutputStream outStream;

                    outStream = newFileOutputStream(mFile2);

                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);

                    outStream.flush();

                    outStream.close();
                    Toast.makeText(Main.this, "Photo Saved Sucessfully", 500)
                            .show();
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {

                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    Toast.makeText(Main.this, "Photo Not Saved Sucessfully",
                            500).show();
                }

                gost = newGhostdatabase(Main.this);
                gost.open();

                gost.insertTitle(sdpath);
            }
        });

        Buttonview= (Button) findViewById(R.id.listtt);
        view.setOnClickListener(newOnClickListener() {

            publicvoidonClick(View v) {

                Intentii=newIntent(Main.this, ListView5.class);
                startActivity(ii);

            }
        });

        ButtonColorpaint= (Button) findViewById(R.id.Color);
        Colorpaint.setOnClickListener(newOnClickListener() {

            publicvoidonClick(View v) {
                intcolor= PreferenceManager.getDefaultSharedPreferences(
                        Main.this).getInt(COLOR_PREFERENCE_KEY, Color.WHITE);
                // int _color = R.color.red;newColorPickerDialog(v.getContext(),
                        newOnColorChangedListener() {

                            publicvoidcolorChanged(int color) {
                                mPaint.setColor(color);

                                slll = color;

                                Log.i("TAG", "mpaint one" + mPaint);
                            }
                        }, mPaint.getColor()).show();
                Log.i("TAG", "mpaint two" + mPaint);
            }
        });
        relativelayout.addView(mView);
    }

    // //////////******************* Pinting view// *******************///////////////////publicclassMyViewextendsViewimplementsOnTouchListener {
        private Map<Path, Integer> colorsMap = newHashMap<Path, Integer>();
        private ArrayList<Path> mMaindialog = newArrayList<Path>();
        private ArrayList<Path> undonePaths = newArrayList<Path>();
        intcolorPicked= slll;
        // Paint mPaint1;private Canvas mCanvas;
        private Path mPath;

        publicMyView(Context c, int w, int h) {
            super(c);
            if (GlobalVariable.impath == 1) {
                Log.d("", "111111" + GlobalVariable.impath);
                System.out.println(GlobalVariable.impath);
                Intentii= getIntent();
                location = ii.getStringExtra("IMAGE");
                // bitmap.recycle();
                Log.d("", "location" + location);
                bitmap = BitmapFactory.decodeFile(location);
                mBitmap = Bitmap.createScaledBitmap(bitmap, 300, 300, false);
                Log.d("hhhhhhhhhhhhhhhssssssss", "mBitmap" + mBitmap);
                // mBitmap = Bitmap.createBitmap(100, 100,// Bitmap.Config.ARGB_8888);// idd.setImageBitmap(mBitmap);
                Log.d("hhhhhhhhhhhhhhhssssssss", "GlobalVariable.impath"
                        + GlobalVariable.impath);
            } elseif (GlobalVariable.impath == 2) {
                // bitmap.recycle();
                Log.d("", "22222222222222222222222" + GlobalVariable.impath);
                bitmap = BitmapFactory.decodeResource(getResources(),
                        R.drawable.base);
                mBitmap = Bitmap.createScaledBitmap(bitmap, 100, 100, false);
                // mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                Log.d("hhhhhhhhhhhhhhhssssssss1111111", "mBitmap" + mBitmap);
            }

            //
            mCanvas = newCanvas(mBitmap);
            mPath = newPath();

        }

        @OverrideprotectedvoidonSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);

        }

        @OverrideprotectedvoidonDraw(Canvas canvas) {

            canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

            for (Path p : mMaindialog) {
                mPaint.setColor(colorsMap.get(p));
                canvas.drawPath(p, mPaint);
            }
            mPaint.setColor(slll);
            canvas.drawPath(mPath, mPaint);
        }

        // //////************touching evants for painting**************///////privatefloat mX, mY;
        privatestaticfinalfloatTOUCH_TOLERANCE=0;

        privatevoidtouch_start(float x, float y) {
            mPath.reset();
            mPath.moveTo(x, y);
            mX = x;
            mY = y;
            undonePaths.clear();
        }

        privatevoidtouch_move(float x, float y) {
            floatdx= Math.abs(x - mX);
            floatdy= Math.abs(y - mY);
            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
                mX = x;
                mY = y;
            }
        }

        privatevoidtouch_up() {
            mPath.lineTo(mX, mY);
            // commit the path to our offscreen
            mCanvas.drawPath(mPath, mPaint);
            // kill this so we don't double draw
            mPath = newPath();
            mPath.reset();
            mMaindialog.add(mPath);
        }

        @OverridepublicbooleanonTouchEvent(MotionEvent event) {
            floatx= event.getX();
            floaty= event.getY();
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // touch_start(x, y);// invalidate();
                undonePaths.clear();
                mPath.reset();
                mPath.moveTo(x, y);
                mX = x;
                mY = y;
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                // touch_move(x, y);// invalidate();floatdx= Math.abs(x - mX);
                floatdy= Math.abs(y - mY);
                if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                    mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
                    mX = x;
                    mY = y;
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                // touch_up();// invalidate();
                mPath.lineTo(mX, mY);
                mMaindialog.add(mPath);
                colorsMap.put(mPath, slll);
                mPath = newPath();
                mPath.reset();
                invalidate();
                break;
            }
            returntrue;
        } // end of touch events for imageprivate Paint createPen(int colorPicked) {
            // TODO Auto-generated method stub
            mPaint1 = newPaint();
            mPaint1.setColor(colorPicked);
            mPaint1.setAntiAlias(true);
            mPaint1.setDither(true);
            mPaint1.setStyle(Paint.Style.STROKE);
            mPaint1.setStrokeJoin(Paint.Join.ROUND);
            mPaint1.setStrokeCap(Paint.Cap.ROUND);
            // mPaint1.setStrokeWidth(3);return mPaint1;
        }

        publicvoidonClickRedo() {
            if (undonePaths.size() > 0) {
                mMaindialog.add(undonePaths.remove(undonePaths.size() - 1));
                mView.invalidate();

            } else {

            }
            // toast the user
        }

        publicvoidonClickUndo() {
            if (mMaindialog.size() > 0) {
                undonePaths.add(mView.mMaindialog.remove(mView.mMaindialog
                        .size() - 1));
                mView.invalidate();
            }

            else {

            }
        }

        @OverridepublicbooleanonTouch(View arg0, MotionEvent arg1) {
            // TODO Auto-generated method stubreturnfalse;
        }
    }// end MyView@OverridepublicvoidcolorChanged(int color) {
        // TODO Auto-generated method stub
        PreferenceManager.getDefaultSharedPreferences(this).edit()
                .putInt(COLOR_PREFERENCE_KEY, color).commit();
        slll = color;
    }

}

Post a Comment for "Undo And Redo In Canvas For Android"