Create dynamically Bitmap with Canvas (Android, Java) - android

I have to implement a function that allow me to draw some Bitmap on surfaceView, this is a part of my code:
public void DrawMarker(float coordX, float coordY){
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bitmapRotated, coordX, coordY, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
This code allow me to draw a bitmap in a certain position (x,y) of the screen, the problem is that I call dynamically this function (for example 5 times), because I need to draw a Bitmap everytime I touch the screen, I don't understand why the images don't remain on the screen at each touches.
At each touches the images disappear and appear again, but at last the cycle (es. 5 times) I can see only some image and not all.
The situation is different if I draw some points with this function:
public void DrawMarker(float[] coordsF){
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawPoints(coordsF,paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
In this case all point remail on the screen.
**** EDIT 1 ****
/* Bind of onTouchEvent*/
public boolean onTouchEvent(MotionEvent event) {
canvas = surfaceHolder.lockCanvas();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
/* The Y coordinate must be adjusted because is moved downwards for the menu size */
float coordX = event.getX();
float coordY = event.getY();
if (tap < MAXTAP) {
DrawMarker(coordX, coordY);
tap++;
}
}
return false;
}
public void DrawMarker(float coordX, float coordY){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bitmap, coordX, coordY, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
***** SOLUTION *****
/* Draw Bitmap everytime I touch the screen */
public void DrawMarker(List<Coordinates> coords){
Canvas canvas = surfaceHolder.lockCanvas();
for(int i=0; i < coords.size(); i++) {
canvas.drawBitmap(bitmap, coords.get(i).coordX, coords.get(i).coordY, paint);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}

Related

How to draw to Canvas from SurfaceView?

I'm trying to do simple painter. The problem that it looks like Android has three independent Canvas and give me it for drawing sequentially.
I made UI with SurfaceView, took Holder from it.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sv = (SurfaceView) findViewById(R.id.sv);
holder = sv.getHolder();
holder.addCallback(callback);
}
Then took Surface.
#Override
public void surfaceCreated(SurfaceHolder holder) {
surface = holder.getSurface();
}
And by events from OnTouchListener() draw dots and lines.
private void paintStartDot(float x, float y) {
Canvas canvas = surface.lockCanvas(null);
canvas.drawPoint(x, y, drawPaint);
surface.unlockCanvasAndPost(canvas);
lastX = x;
lastY = y;
}
private void paintEndDot(float x, float y) {
Canvas canvas = surface.lockCanvas(null);
canvas.drawLine(lastX, lastY, x, y, drawPaint);
surface.unlockCanvasAndPost(canvas);
lastX = x;
lastY = y;
}
The screencast:
https://youtu.be/NNDnzrtMLZI
What is wrong?
Full source is available here:
https://github.com/tseglevskiy/canvasdemo1/blob/error/app/src/main/java/ru/jollydroid/canvasdemo1/MainActivity.java
The Canvas that Surface.lockCanvas gives you is not persistent. The moment you call unlockCanvasAndPost, the contents of the surface buffer are pushed out to the screen. Every time you call lockCanvas you need to redraw the picture from scratch.
If you want to update the canvas incrementally, you should keep an "off-screen" canvas backed by a Bitmap that you update in response to user actions. Then paint the bitmap to the Surface canvas.
private void paintStartDot(float x, float y) {
if (mBitmap == null) return; // not ready yet
Canvas canvas = new Canvas(mBitmap);
canvas.drawPoint(x, y, drawPaint);
// draw the bitmap to surface
canvas = surface.lockCanvas(null);
canvas.drawBitmap(mBitmap, 0, 0, null);
surface.unlockCanvasAndPost(canvas);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// good place to create the Bitmap
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config. ARGB_8888);
// also here we need to transform the old bitmap to new one
}
I'm not sure if you really need a SurfaceView. You could just extend the View and override its View.onDraw method. In that case, call view.invalidate() to indicate it needs to be redrawn.
See also: Android View.onDraw() always has a clean Canvas

Flip Canvas upside-down while preserving onTouchEvent method

I have created a bar graph by drawing rectangles on a canvas. The only problem is that graph is upside down. I tried using the following code to flip the canvas right from the start, but this affects the onTouchEvent method in a weird way:
public void onDraw(final Canvas canvas) {
int width = super.getWidth();
int height = super.getHeight();
canvas.scale(1f, -1f,
width * 0.5f, height * 0.5f);
mCanvas = canvas;
super.onDraw(canvas);
....//more code after that
If the code above is implemented, the graph is shown properly. However, the x & y coordinates of the rectangles are inverted. This means that if I click the top of the canvas where there is no shape, the onTouchEvent method is still fired off because the point is where the rectangle would have been if the canvas wasn't flipped.
#Override
public boolean onTouchEvent( MotionEvent event) {
super.onTouchEvent(event);
int x = (int)event.getX();
int y = (int)event.getY();
xStored = x; yStored=y;
if (event.getAction()==MotionEvent.ACTION_UP){
}else if(event.getAction()==MotionEvent.ACTION_DOWN){
System.out.println("Touching down!");
if(!drawNew){
for(Rect rect : rectangles){
if(rect.contains(x,y)){
System.out.println("Touched Rectangle, start activity."+x+","+y);
drawNew = true;
invalidate();
}else{
}
}
}else{
drawNew = false;
invalidate();
}
}else if(event.getAction()==MotionEvent.ACTION_MOVE){
}
this.postInvalidate();
return true;
}
Whats the best way to flip the canvas upside-down while still preserving the onTouchEvent method?
It turns out that it is better to move and rotate the object on the canvas, rather than rotating the canvas itself.

How to move a bitmap from one coordinate to another - Android development

I want to move a bitmap image along consecutive coordinates in SurfaceView. I have a bitmap myBall drawn in the coordinate (x1, y1) on a SurfaceView as follows (partial code)(
public class MainSurfaceView extends SurfaceView implements Runnable {...
...
#Override
public void run() {
while (isRunning) {
if (!myHolder.getSurface().isValid())
continue;
Canvas canvas;// Define canvas to paint on it
canvas = myHolder.lockCanvas();
//Draw full screen rectangle to hold the floor map.
Rect dest = new Rect(0, 0, getWidth(), getHeight());
Paint paint = new Paint();
paint.setFilterBitmap(true);
canvas.drawBitmap(bgImage, null, dest, paint);
//This is the ball I want to move
canvas.drawBitmap(myBall, x1, y1, null);
myHolder.unlockCanvasAndPost(canvas);
}
}
Now I want to move it to (x2, y2) then (x3, y3) and ... as many as needed and one after the other. I have tried to use TranslateAnimation but couldn't do it.
I have figured out how to animate using a coordinates. I will explain what I did hopping that it will be helpful to others:
First I saved the coordinates as Point object
List<Point> point_list = new ArrayList<Point>();
point_list.add(new Point(x_value, y_value));//Add the x and y coordinates to the Point\
Leave implementing Runnable and use onDraw() method instead of run() method as follows:
public class MainSurfaceView extends SurfaceView {...
....
#Override
protected void onDraw(Canvas canvas) {
// Draw full screen rectangle to hold the floor map.
Rect fArea = new Rect(0, 0, getWidth(), getHeight());
Paint paint = new Paint();
paint.setFilterBitmap(true);
// draw the paint on the rectangle with the floor map
canvas.drawBitmap(bgImage, null, fArea, paint);
// Get the coordinates of x & y as Point object
List<Point> myPoints = point_list;
// Start printing myBall on the floor (view)
try {
if (index < myPoints.size()) {
// Increment the value of index and use it as index for the point_list
index++;
}
// Print myBall in each coordinates of x & y using the index
canvas.drawBitmap(myBall, myPoints.get(index).x, myPoints.get(index).y, null);
} catch (IndexOutOfBoundsException e) {
// TODO: handle exception
}
}
I use try and catch to avoid IndexOutOfBoundryExeption
Cheers!
I think this might be what you're after. What you're trying to do is tween the bitmap and there are a few ways you can do that. There is also the ViewPropertyAnimator, which can animate an entire view.

android drawing app: line cannot be drawn on a bitmap loaded from gallery

I am working on a drawing app, but i do not know why when the picture is loaded from gallery, when further draw on it, the line just drawn will appear on Touch but will disappear when the finger is off the screen, i.e. the line drawn cannot be fixed onto the Bitmap.
Would there be anyone that know how to modify it? Many thanks!!!
coding:
public class DrawView extends View // the main screen that is painted
{
private static final float TOUCH_TOLERANCE = 10;
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 HashMap<Integer, Path> pathMap; // current Paths being drawn
private HashMap<Integer, Point> previousPointMap; // current Points
public DrawView(Context context, AttributeSet attrs)
{
super(context, attrs);
paintScreen = new Paint();
// set the default settings
paintLine = new Paint();
paintLine.setColor(Color.BLACK);
paintLine.setStyle(Paint.Style.STROKE);
paintLine.setStrokeWidth(5);
pathMap = new HashMap<Integer, Path>();
previousPointMap = new HashMap<Integer, Point>();
}
// Method onSizeChanged creates BitMap and Canvas after app displays
#Override
public void onSizeChanged(int w, int h, int oldW, int oldH)
{
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmap.eraseColor(Color.WHITE); // erase the BitMap with white
}
public void load_pic(String picturePath) // load a picture from gallery
{
pathMap.clear(); // remove all paths
previousPointMap.clear(); // remove all previous points
bitmap = BitmapFactory.decodeFile(picturePath);
invalidate(); // refresh the screen
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, 0, 0, paintScreen);
for (Integer key : pathMap.keySet())
canvas.drawPath(pathMap.get(key), paintLine); // draw line
}
// handle touch event
#Override
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getActionMasked();
int actionIndex = event.getActionIndex(); // pointer (i.e., finger)
// determine which type of action the given MotionEvent represents, then call the corresponding handling method
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN)
{
touchStarted(event.getX(actionIndex), event.getY(actionIndex), event.getPointerId(actionIndex));
} // end if
else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP)
{
touchEnded(event.getPointerId(actionIndex));
} // end else if
else
{
touchMoved(event);
} // end else
invalidate(); // redraw
return true; // consume the touch event
} // end method onTouchEvent
// called when the user touches the screen
private void touchStarted(float x, float y, int lineID)
{
Path path; // used to store the path for the given touch id
Point point; // used to store the last point in path
// if there is already a path for lineID
if (pathMap.containsKey(lineID))
{
path = pathMap.get(lineID); // get the Path
path.reset(); // reset the Path because a new touch has started
point = previousPointMap.get(lineID); // get Path's last point
} // end if
else
{
path = new Path(); // create a new Path
pathMap.put(lineID, path); // add the Path to Map
point = new Point(); // create a new Point
previousPointMap.put(lineID, point); // add the Point to the Map
} // end else
// move to the coordinates of the touch
path.moveTo(x, y);
point.x = (int) x;
point.y = (int) y;
}
...similar for other on Touch event
Do you delete the pathMap when the Touch event ends (i.e., the touchEnded method)?
That would result in the behaviour that you describe, as you are not drawing on the Bitmap, but rather on the Canvas that you are drawing both the line and the bitmap unto (and that Canvas is being redrawn on with every onDraw event); i.e., with each onDraw, you draw over your previous drawing.
I have solved the problem using the following code.
The bitmap needed to be copy for editing purposes.
bitmap = (BitmapFactory.decodeFile(picturePath));
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
bitmapCanvas = new Canvas(bitmap);
invalidate();

Redraw image on canvas on touch event?

I am trying to redraw an image on the canvas on an onTouch event. I am able to draw the image on the canvas, but I want the image to redraw at a particular x,y.
protected void onDraw(Canvas canvas)
{
mBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.ab);
mBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.ab);
this.canvas=canvas;
Paint p = new Paint();
p.setColor(Color.parseColor("#FFFFFF"));
canvas.drawLine(x1, y1, x2 , y2, p);
canvas.drawBitmap(mBitmap1, 70, 60, null);
canvas.drawBitmap(mBitmap1, 185, 60, null);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
final int x=(int)event.getX();
Log.i("***********xPos","="+x);
final int y=(int)event.getY();
Log.i("***********yPos","="+y);
if(event.getAction()==MotionEvent.ACTION_UP)
{
}
if(event.getAction()==MotionEvent.ACTION_DOWN)
{
canvas.drawBitmap(mBitmap1,50+x,60,null );
this.postInvalidate();
}
if(event.getAction()==MotionEvent.ACTION_MOVE)
{
}
return false;
}
I think I understand your problem. You are calling postinvalidate() method each time when action.down is called, so it calls ultimately call ondraw(). So it will redraw it on bitmap for particular setted value at which you put in ondraw again.
So you looks that it remain unchanged.
Follow these steps:
use some public variables for drawing bitmaps in ondraw method for x and y axis, lets say initx and inity
then on touch event:update this value by adding your x and y value to initx and inity resp.
like:initx=initx+x;
inity=inity+y;
And last in cation down event just call post.invalidate or ondraw method.
See the MotionEvent class that provides the coordinates of where the user touched the screen.

Categories

Resources