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"