Android - lost previous drawing on making a paint App - android

Below is my new painter app for android.
However, it does not reflect previously drawn object.
(when i touch up the screen, it lost the shape)
To, reflect previously drawn object, I tried to use 'Bitmap.createBitmap' method but it does not work.
please help me.
public class CreativePainterActivity extends Activity {
//
//private MyView vw;
Paint mPaint;
//--Variables to store the current figure info
private float _currentStartX; //where mouse first pressed
private float _currentStartY;
private float _currentEndX; //where dragged to or released
private float _currentEndY;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
setContentView(new MyView(this));
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFFFF00);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(8);
}
//view class
public class MyView extends View{
private Canvas mCanvas;
private Bitmap mBitmap;
private Paint mBitmapPaint;
Bitmap bm;
//private Paint mBitmapPaint;
public MyView(Context context){
super(context);
//ADDED
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mBitmap = Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mCanvas.drawColor(0xFFFFFFFF);
bm = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
}
public void onDraw(Canvas canvas){
//canvas.drawColor(Color.LTGRAY);
canvas.drawBitmap(bm, 0, 0, mBitmapPaint);
canvas.drawLine(_currentStartX, _currentStartY, _currentEndX, _currentEndY, mPaint);
}
//Methods for touch events
public boolean onTouchEvent(MotionEvent event){
if (event.getAction() == MotionEvent.ACTION_DOWN){
_currentStartX=event.getX();
_currentStartY=event.getY();
return true;
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
_currentEndX=event.getX();
_currentEndY=event.getY();
invalidate();
return true;
}
return true;
}
}//end of the class MyView
}//end of the class CreativePainterActivity

You will need to know a bit more about how views are drawn. Your views won't preserve whatever is there on, after you draw again. So, you should somehow save whatever was there before, and redraw the old stuff, along with the new changes.
A simpler solution, would be to save the previous drawings to a Bitmap, and then draw that Bitmap again on the canvas first, and add new stuff.
The flow
onDraw(){
drawBitmap(bmp);
drawOtherStuff();
bmp = saveOnScreenBitmap();
}
So, each time you need to save the last drawn bitmap, and re-draw it. Hope it's more clear now.
Some sample tutorials:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/FingerPaint.html
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.html
http://www.tutorialforandroid.com/2009/06/drawing-with-canvas-in-android.html

Related

Android Drawing a Smooth Path

I have code that I need to improve.
Here's what's wrong: it's a little slow and choppy, meaning the lines aren't smooth and the drawing is a bit delayed.
public void touchStarted(Point point) {
if (null == drawingModePath) {
drawingModePath = new Path();
}
drawingModePath.moveTo(point.x, point.y);
}
public void touchMoved(Point point) {
drawingModePath.lineTo(point.x, point.y);
Bitmap bitmap = Bitmap.createBitmap((int) getWindowManager()
.getDefaultDisplay().getWidth(), (int) getWindowManager()
.getDefaultDisplay().getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
mainDrawingView.setImageBitmap(bitmap);
// Path
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
canvas.drawPath(drawingModePath, paint);
}
public void touchEnded(Point point) {
touchMoved(point);
}
In essence what this code does is drawing a path based on touchStarted, touchMoved, and touchEnded. If someone can help me optimize this, I'd be grateful. Perhaps if I don't recreate the bitmap each time touchMoved occurs? Not sure here... not sure... I use a UIBezierPath to perform this code on iOS and it's a bit faster (and smoother). Anyway, I come to you for help. Input appreciated.
you are recreating everything every move. that will affect the performance of drawing a lot. the event triggers every 8ms (or 16ms im not sure), imagine you are reinstantiating everything every 8ms? thats tough.
so this must be in the instantiation part
Bitmap bitmap = Bitmap.createBitmap((int) getWindowManager()
.getDefaultDisplay().getWidth(), (int) getWindowManager()
.getDefaultDisplay().getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
mainDrawingView.setImageBitmap(bitmap);
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
The touchMoved() should only record the new path and call the invalidate() to make the View redraw itself resulting in calling the draw method (onDraw()).
public void touchMoved(Point point) {
drawingModePath.lineTo(point.x, point.y);
invalidate();
}
and then implement onDraw() method to do the drawing
Heres how i do the drawing interface in one of my projects:
public class SignatureView extends View {
public SignatureView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
// instantiating my paint object
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
path = new Path();
}
#Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld)
{
// this is where i initialize my canvas, because in constructor, the view is not completely instantiated yet, so getting the height and width there will result in null exception.
bitmap = Bitmap.createBitmap(xNew, yNew, Bitmap.Config.ARGB_8888);
background_canvas = new Canvas(bitmap);
}
#Override
protected void onDraw(Canvas canvas)
{
// draw the new path to a buffer canvas
background_canvas.drawPath(path, paint);
// put the buffer in the real canvas
canvas.drawBitmap(bitmap, 0, 0, paint);
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
//this is like your move event, it just records the new path every move.
int action = ev.getActionMasked();
if ( action == MotionEvent.ACTION_DOWN )
{
path.moveTo(ev.getX(), ev.getY());
}
else if ( action == MotionEvent.ACTION_MOVE )
{
path.lineTo(ev.getX(), ev.getY());
// call invalidate() to make the view redraw itself, resulting in calling the onDraw() method.
invalidate();
}
else if ( action == MotionEvent.ACTION_UP )
{
onDone.method();
}
return true;
}
public void clear()
{
background_canvas.drawColor(Color.WHITE);
path.reset();
invalidate();
}
interface OnDone{
void method();
}
public void setOnDone(OnDone new_onDone)
{
onDone = new_onDone;
}
OnDone onDone;
private Paint paint;
private Bitmap bitmap;
private Canvas background_canvas;
private Path path;
public Bitmap getBitmap()
{
return bitmap;
}
}

Erase functionality not working in Custom SurfaceView Android

I have a custom surfaceView which will paint the surface based on Touch event. When i draw something on this view it is working fine. But when i tried to erase the paint, nothing got erased. Please find the sample code snippet below:
public class MySurfaceView extends SurfaceView {
private static final String TAG = "FreeHandDrawing";
public static Canvas mCanvas;
SurfaceHolder holder;
private static Path path;
private Paint paint;
private ArrayList<Path> pathArrayList = new ArrayList<>();
private boolean freeHandMode;
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
freeHandMode = false;
path = new Path();
holder = getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
setDrawingCacheEnabled(true);
this.setZOrderOnTop(true);
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(0xFF22FF11);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(8);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(freeHandMode) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.d("Action", "Placed");
path.moveTo(event.getX(), event.getY());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
Log.d("Action", "Moved");
path.lineTo(event.getX(), event.getY());
pathArrayList.add(path);
}
mCanvas = holder.lockCanvas();
if (mCanvas != null) {
if (pathArrayList.size() > 0) {
mCanvas.drawPath(pathArrayList.get(pathArrayList.size() - 1), paint);
}
holder.unlockCanvasAndPost(mCanvas);
} else {
Log.d(TAG, "Canvas is NULL");
}
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "On draw called");
}
public void eraseDrawing() {
pathArrayList.clear();
invalidate();
}
public void drawEnableDisable(boolean mode) {
freeHandMode = mode;
}
}
What is the problem with the code above ?
You should keep your drawing code in the onDraw method
#Override
protected void onDraw(Canvas canvas) {
// In case you want to delete/erase when path array/list is cleared.
if (pathArrayList.size() == 0) {
canvas.drawColor(0, Mode.CLEAR);
}
// In case, you want to draw all paths also in onDraw
for(Path path : pathArrayList) {
canvas.drawPath(path, paint);
}
}
When you clear the Path Array(list) and then upon calling invalidate(), onDraw gets triggered.
Note: You're supposed to call invalidate() at the end of onTouchEvent() to tell the system to update the screen. Calling invalidate() will get the framework to eventually call onDraw().
Also you should NOT use canvas obtained by lockCanvas as it will not be hardware accelerated. Instead you should use the canvas passed as an argument to onDraw() itself.
You can choose to make the system a bit smart by not having to draw the pull list of paths every frame and instead handle erases/redraws etc by using special flags. And otherwise just render the latest path generated by the most recent call to onTouchEvent().

canvas not drawing images in another method

I have extended View to use canvas. I have draw basic drawings in onDraw() method, when user touches in the canvas I have to draw an image there, for that I have used canvas inside onTouchEvent() method,it is not drawing anything there, the code is given below, what is the problem and how can i resolve this
public class ScreenView extends View(){
static Canvas canvas;
Bitmap bm;
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
bm = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
canvas.draw.......
......
...........
}
public boolean onTouchEvent(final MotionEvent event) {
handleTouches(event.getX(), event.getY());
return false;
}
public void handleTouches(float x, float y) {
xLocTouch = (int) x;
yLocTouched = (int) y;
Paint paint = new Paint();
paint.setColor(Color.BLACK);
canvas.drawBitmap(bm, xLocTouch ,yLocTouched , paint);
}
}
You should call invalidate() method inside onTouchEvent, then your onDraw() method will be called, and you just should store your x and y coordinates, and then draw bitmap to this coordinates, like this:
public class ScreenView extends View {
int xLocTouched;
int yLocTouched;
Bitmap bm;
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
bm = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
//your basic drawings also should depends on xLocTouched and yLocTouched.
Paint paint = new Paint();
paint.setColor(Color.BLACK);
canvas.drawBitmap(bm, xLocTouched ,yLocTouched , paint);
}
public boolean onTouchEvent(final MotionEvent event) {
xLocTouched = (int) event.getX();
yLocTouched = (int) event.getY();
invalidate();
return false;
}
}

Android drawing app OnTouchListener cannot be implemented

I am working on a drawing app and extending View, and would like to implements OnTouchListener. Codes as follows:
public class DoodleView extends View implements OnTouchListener //ERROR1
{
private Bitmap bitmap; // drawing area for display or saving
private Canvas bitmapCanvas; // used to draw on bitmap
private Paint paintScreen; // use to draw bitmap onto screen
private Paint paintLine; // used to draw lines onto bitmap
private Path mPath;
private Paint mPaint, circlePaint, outercirclePaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private float xleft,xright,xtop,xbottom;
private float mX, mY;
// DoodleView constructor initializes the DoodleView
public DoodleView(Context context)
{
super(context); // pass context to View's constructor
this.context_new=context;
this.setOnTouchListener(this); //ERROR2
paintScreen = new Paint(); // used to display bitmap onto screen
// set the initial display settings for the painted line
paintLine = new Paint();
paintLine.setAntiAlias(true); // smooth edges of drawn line
paintLine.setColor(Color.BLACK); // default color is black
paintLine.setStyle(Paint.Style.STROKE); // solid line
paintLine.setStrokeWidth(5); // set the default line width
paintLine.setStrokeCap(Paint.Cap.ROUND); // rounded line ends
mPath = new Path();
paths.add(mPath);
} // end DoodleView constructor
OnSizeChanged:
#Override
public void onSizeChanged(int w, int h, int oldW, int oldH)
{
super.onSizeChanged(w, h, oldW, oldH);
DoodlzViewWidth = w;
DoodlzViewHeight = h;
bitmap = Bitmap.createBitmap(getWidth(), DoodlzViewHeight, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmap.eraseColor(Color.WHITE); // erase the BitMap with white
}
onDraw:
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, 0, 0, paintScreen); // draw the background screen
// for each path currently being drawn
for (Path p : paths){canvas.drawPath(p, paintLine);}
Toast.makeText(getContext(), "ondraw", Toast.LENGTH_SHORT).show();
}
onTouchEvent:
#Override
public boolean onTouchEvent(View arg0, MotionEvent event) //ERROR3
{
float x = event.getX();
float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
touchStarted(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touchMoved(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchEnded();
invalidate();
break;
}
return true;
}
However, I really scratch my head and dont know why for
ERROR1: implements OnTouchListener reports red underlines says
OnTouchListener cannot be resolved to a type , and suggest import android.view.View. But I tried click to import by it still unwilling to be imported, and I see the import list it is already there
ERROR2: The method setOnTouchListener(View.OnTouchListener) in the type View is not applicable for the arguments (DoodleView), should be because of ERROR1
ERROR3: The method onTouchEvent(View, MotionEvent) of type DoodleView must override or implement a supertype method, should be also be because of ERROR1.
Are there anyone know what is happening? I got stuck here without move! Thanks a lot!
It seems like a simple import issue. Try to add
import android.view.View
if not already present in your class and change class declaration as follows:
public class DoodleView extends View implements View.OnTouchListener {
....

Erase bitmap parts using PorterDuff mode

I try to erase parts of a bitmap in my Android application by using Porter-Duff Xfermodes.
I have a green background which is overlayed by a blue bitmap. When I touch the screen a "hole" in the overlaying bitmap is supposed to be created making the green background visible. Instead of a hole my current code produces a black dot.
Below is my code. Any ideas, what I am doing wrong here?
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new DrawView(this));
}
public class DrawView extends View implements OnTouchListener {
private int x = 0;
private int y = 0;
Bitmap bitmap;
Canvas bitmapCanvas;
private final Paint paint = new Paint();
private final Paint eraserPaint = new Paint();
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
// Set background
this.setBackgroundColor(Color.GREEN);
// Set bitmap
bitmap = Bitmap.createBitmap(320, 480, Bitmap.Config.RGB_565);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.drawColor(Color.BLUE);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
eraserPaint.setAntiAlias(true);
}
#Override
public void onDraw(Canvas canvas) {
bitmapCanvas.drawColor(Color.BLUE);
bitmapCanvas.drawCircle(x, y, 10, eraserPaint);
canvas.drawBitmap(bitmap, 0, 0, paint);
}
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
invalidate();
return true;
}
}
Here is working code... may help somebody
public class ImageDemo extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Panel(this));
}
class Panel extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
Bitmap bitmap;
Canvas pcanvas;
int x = 0;
int y =0;
int r =0;
public Panel(Context context) {
super(context);
Log.v("Panel", ">>>>>>");
setFocusable(true);
setBackgroundColor(Color.GREEN);
// setting paint
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAntiAlias(true);
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.mickey);
// converting image bitmap into mutable bitmap
bitmap = bm.createBitmap(295, 260, Config.ARGB_8888);
pcanvas = new Canvas();
pcanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
pcanvas.drawBitmap(bm, 0, 0, null);
}
#Override
protected void onDraw(Canvas canvas) {
// draw a circle that is erasing bitmap
pcanvas.drawCircle(x, y, r, mPaint);
canvas.drawBitmap(bitmap, 0, 0,null);
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// set parameter to draw circle on touch event
x = (int) event.getX();
y = (int) event.getY();
r =20;
// At last invalidate canvas
invalidate();
return true;
}
}
}
First thought, I'm not sure if setting alpha to 0 on your erase paint object is a good idea. That might make the whole thing ineffective.
Also, you should always use Bitmap.Config.ARGB_8888 if you're dealing with alphas.
If you're having trouble with the PorterDuff stuff, though, I would suggest simplifying your approach to ONLY do that (temporarily). That will help you narrow down the part which isn't working. Comment out everything to do with touch and view updates.
Then you can single out what part of the drawing isn't working right. Set up your constructor like this:
DrawView()
{
/* Create the background green bitmap */
...
/* Create foreground transparent bitmap */
...
/* Draw a blue circle on the foreground bitmap */
...
/* Apply the foreground to the background bitmap
using a PorterDuff method */
...
}
onDraw()
{
/* Simply draw the background bitmap */
...
}
If you set things up like that, you should be able to tell how your PD method is affecting the green bitmap, and change things accordingly.
Here is another advancement for your solution ... See Demo example
public class MainActivity extends Activity {
Bitmap bp;
Canvas bitmapCanvas;
DrawView drawImg;
LinearLayout ln1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ln1 = (LinearLayout) findViewById(R.id.ln1);
drawImg = new DrawView(this);
ln1.addView(drawImg);
}
public class DrawView extends View implements View.OnTouchListener {
private int x = 0;
private int y = 0;
Bitmap bitmap;
Path circlePath;
Paint circlePaint;
private final Paint paint = new Paint();
private final Paint eraserPaint = new Paint();
public DrawView(Context context){
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
// Set background
this.setBackgroundColor(Color.CYAN);
bp = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
// Set bitmap
bitmap = Bitmap.createBitmap(320, 480, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.drawColor(Color.TRANSPARENT);
bitmapCanvas.drawBitmap(bp, 0, 0, null);
circlePath = new Path();
circlePaint = new Paint();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setStrokeJoin(Paint.Join.ROUND);
eraserPaint.setStrokeCap(Paint.Cap.ROUND);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
eraserPaint.setAntiAlias(true);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, paint);
bitmapCanvas.drawCircle(x, y, 30, eraserPaint);
canvas.drawPath(circlePath, circlePaint);
}
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
bitmapCanvas.drawCircle(x, y, 30, eraserPaint);
circlePath.reset();
circlePath.addCircle(x, y, 30, Path.Direction.CW);
int ac=event.getAction();
switch(ac){
case MotionEvent.ACTION_UP:
Toast.makeText(MainActivity.this, String.valueOf(x), Toast.LENGTH_SHORT).show();
circlePath.reset();
break;
}
invalidate();
return true;
}
}
}
read more

Categories

Resources