Now I need to draw a new Canvas in an existent Canvas (the parent Canvas), like Bitmap's works.
The logic is this:
Each Sprite can have others Sprite nodes (LinkedList<Sprite> nodes);
If don't have nodes (nodes = null) then, will draw the (Bitmap) Sprite.image directly to parent canvas, received on update method, like (currently working):
public void update(Canvas canvas){
if(this.nodes == null){
canvas.drawBitmap(this.image, ...);
}
...
}
If it have nodes, I need create a new Canvas, draw nodes bitmap on this, and draw the new Canvas on parent Canvas. This don't works, but the idea is like this:
public void update(Canvas canvas){
...
else { // this.nodes != null
Canvas newCanvas = new Canvas();
for(Sprite node: this.nodes){
node.update(newCanvas);
}
// Canvas working like Bitmap! (?)
canvas.drawCanvas(newCanvas, this.x, this.y, this.paint);
}
}
Real example usage: well, I have a Sprite called CarSprite. The CarSprite have a private class called WheelSprite extends Sprite. The CarSprite implements two nodes (two dimensional example) of WheelSprite, that will be positioned in the CarSprite to seems the whell of car. So far, no problem.
But if CarSprite is affected by a devil one object in the level, this will be turn transparent. I will do, for instance, objectOfCarSprite.paint.setAlpha(127);. The problem is, if I do it (currently working mode), only the car bitmap will turn transparent, but not the well, because it is drawed directly to global Canvas object.
This part does not influence much: currently, I'm getting the parent (of parentN...) Paint object alpha, and with some math, I get a transparent solution to wheel that works almost as well I need, but if, for instance, the WheelSprite object is over car bitmap, is possible to see the ground of car, like picture.
Exists a better way to do like I want, or only like work currently?
I do it. But is very slow, but works fine.
final Bitmap split = Bitmap.createBitmap(GameActivity.screenWidth,
GameActivity.screenHeight,
Bitmap.Config.ARGB_8888);
final Canvas subCanvas = new Canvas(split);
for(final Sprite sprite: this.nodes) {
sprite.update(subCanvas);
}
canvas.drawBitmap(split, this.x, this.y, this.paint);
First we need have a Canvas object (here just canvas);
After, we need make a Bitmap where we will draw. This need get a width and height from current canvas. I used a shortcut on this case;
Next, we need make a new Canvas, it will be passed to Sprite.nodes;
After all, we need draw the bitmap on global canvas;
Note: this works very well, but affect to much the speed.
Related
Here is the scenario of the issue .
A custom view which is a canvas on which I need to draw .
2 buttons beneath it ,lets call them A and B
When A is clicked -> Image A is drawn to the above canvas .
When B is clicked -> Image B is drawn to the above canvas .
The issue demands that the previously drawn image on canvas must be preserved . That is if you click button A followed by button B then the canvas must contain two images . So the previous images need to be preserved.
Problem : How do you achieve this ?
Possible solution 1 : Create an ArrayList and keep adding images to this arrayList . Pass the updated arrayList to canvas onDraw method and redraw every single image for every button click .
Possible solution 2 : There has to be some method to preserve the state of the canvas so that on each button click you draw on canvas's last preserved state and draw only the new image .
Further Requirements : The Images drawn to canvas could be dragged so need to keep track of updated positions .
I am at an impasse and couldn't find a good tutorial or a book tackling such requirement , any help is appreciated .
You can create the Bitmap, and draw what you want on it.
Bitmap mBitmap;
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
if (mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
mBitmap = Bitmap.createBitmap(right - left, bottom - top, Bitmap.Config.ARGB_8888);
redrawAllYourStuffTo(mBitmap);
}
}
when button is pressed, you can draw directly to bitmap, like this:
Canvas canvas = new Canvas(mBitmap);
canvas. ... // draw operations.
// after the bitmap canvas drawing finished, call
invalidate();
in onDraw just paint your bitmap
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
I'm writing a custom view that displays signals. In order to shorten my onDraw() time I cache everything I've drawn so far in a Bitmap and just append to that in every onDraw() call. By doing this I can save huge amounts of time since I only need to draw a few fixels at a time instead of redoing the whole thing.
There is on thing bothering me though - it appears as drawing directly to the provided canvas provides a more "accurate" drawing than drawing on the bitmap first and then drawing the bitmap on the canvas. By looking at the lower part of the following picture you can see the difference:
I uploaded a demo project displaying the discrepancy at https://github.com/gardarh/android-uglybitmapdrawing/ but the relevant code is as follows:
#Override
public void onDraw(Canvas canvas) {
if(cachedBitmap == null) {
cachedBitmap = Bitmap.createBitmap(getWidth(), 200, Config.ARGB_8888);
cachedCanvas = new Canvas(cachedBitmap);
}
for(int i = 0; i < COORDS.length; i++) {
float[] curCoords = COORDS[i];
canvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
cachedCanvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
}
canvas.drawBitmap(cachedBitmap, 0, 120, null);
}
Why are the two traces not the same and more importantly, how can I make the lower trace look like the upper one?
The reason for the differences is that the canvas drawing is done by hardware acceleration (GPU), and the bitmap drawing is done by software (CPU). If you disable hardware acceleration, they become the exact same.
If you multiply the X coordinates by 10, you will see that the difference is in the way lines are joined. These are minor one pixel difference and I wouldn't bother with them. I am not sure which one is the more accurate, they seem like just slightly different implementations.
Android framework API provides 2D drawing APIs for simple animation that does not require major dynamic changes.
There are two ways of implementation using these API.
1. Drawing to a View
2. Drawing on a Canvas
1.Drawing a circle to View
Drawing to view is a better option when your UI does not require dynamic changes in the application.
This can be achieved simply by extending the View class and define an onDraw() callback method.
Use the Canvas given to you for all your drawing,
using various Canvas.draw...() methods (Ex: canvas.drawCircle(x / 2, y / 2, radius, paint);). onDraw() is a callback method invoked when the view is initially drawn.
Below is a simple example code to draw a circle:-
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
}
public class MyView extends View {
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
int radius;
radius = 100;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
canvas.drawPaint(paint);
// Use Color.parseColor to define HTML colors
paint.setColor(Color.parseColor("#FB9J2F"));
canvas.drawCircle(x / 2, y / 2, radius, paint);
}
} }
2. Drawing rectangle on a canvas
To draw dynamic 2D graphics where in your application needs to regularly re draw itself, drawing on a canvas is a better option. A Canvas works for you as an interface, to the actual surface upon which your graphics will be drawn.
If you need to create a new Canvas, then you must define the bitmap upon which drawing will actually be performed. The Bitmap is always required for a Canvas.
The below example explains to draw a rectangle:-
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mylayout"> </LinearLayout>
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Paint paint = new Paint();
paint.setColor(Color.parseColor("#DD4N5C"));
Bitmap bitmap = Bitmap.createBitmap(512, 800, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawRect(150, 150, 250, 250, paint);
LinearLayout layout = (LinearLayout) findViewById(R.id.mylayout);
layout.setBackgroundDrawable(new BitmapDrawable(bitmap));
}
}
I disagree that you are saving much by going the bitmap route. You might consider a data structure that stores the drawn signal and converting this to a JSON object that can be serialized/deserialized.
As for your question, this is an educated guess but it has more to do with rescaling of the drawn signal, since you are not using getHeight() when you create the bitmap.
I have drawn a bitmap image over a canvas.
Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.sq);
canvas.drawColor(color.black);
Rect dstRectForRender = new Rect(0,0,320,450);
canvas.drawBitmap(image, null,dstRectForRender,null);
The image gets displayed based on my screnn on a cnavs.
On my touch input, I need to pass the x and y co-ordinate position of the image and fill that pixel with a color to show that the image is painted on a drag event.
How can I pass the x and y coo-ordinate parameters? Which functions should I use to plot the pixels on the image?
I appreciate your help and sweet time.
I'm not sure if this is the best way to do this, but it would be much easier to do this if you defined your own subclass of ImageView and name it something like DrawableImageView. You'd have to make sure you implement all the basic constructors from ImageView, then override the onTouchEvent method. From that event you can get the touch coordinates and store them in an ArrayList<Point> and use that ArrayList by overriding the onDraw method and "painting" the image.
public class DrawableImageView extends ImageView {
ArrayList<Point> list = new ArrayList<Point>();
//constructors..
#Override
public boolean onTouchEvent (MotionEvent event) {
float x = event.getX();
float y = event.getY();
list.add(new Point(x,y));
invalidate();
}
This is just a very brief overview of how to start your class, and may not be the most accurate way of doing things (depending on your specific code). Now, instead of using <ImageView> tags in your xml (or, loading an ImageView programatically), you refer to your subclass like so:
<your.package.name.DrawableImageView
/>
Edit
In response to your comment, there is no predetermined way to draw over an image. You must implement this yourself, which is why I recommended storing Points in an ArrayList. I'm not really sure what you're trying to achieve here, but to draw (for example) black dots over an image you have to override onDraw:
public void onDraw(Canvas c) {
super.onDraw(c);
for(Point p : list) {
//Draw black point at x and y.. I'm posting from my cell so I can't go into much detail
}
}
Also, to force a View to redraw itself you need to use the invalidate() method in your onTouchEvent() (which I've added above).
I'm trying to draw a few lines on a DrawView which extends SurfaceView.
in my onDraw method I loop over a list of float arrays and draw lines.
#Override
public void onDraw(final Canvas canvas) {
synchronized (lines) {
super.onDraw(canvas);
for (float[] l : lines) {
paint.setAntiAlias(true);
canvas.drawLines(l, paint);
}
}
}
I would like to animate each of these lines. I've tried to use ViewAnimator even though it's only Honeycomb and above but either I don't understand how to use it or it just isn't meant to be used with canvas.drawLine(). I've tried to use paint.setPathEffect but it doesn't seem to work at all. Anyone have any idea how to do this? I just want each line to take something like 1 second to draw one after the other.
i'm attempting to change the background image of a custom View with some success. the image will change but the problem is that i still see traces of the old image. when i attempt to clear the canvas before drawing the new image, it doesn't appear to work. i create a bitmap to store the image. when changing the image, i call Canvas.drawColor() before drawing the new image but the old image persists. i've tried drawColor(0), drawColor(Color.BLACK), c.drawColor(0, PorterDuff.Mode.CLEAR), and none of the above works. as such, i had to post this for review from more experienced minds than mine.
the actual code is as follows:
private int bgnd;
private boolean switching;
public void setBgnd(int incoming){
switching = true;
switch (incoming){
case R.drawable.image1:
bgnd = incoming;
this.invalidate();
break;
case R.drawable.image2:
bgnd = incoming;
this.invalidate();
break;
}
}
protected void onDraw(Canvas c){
if(switching == true){
Bitmap b = BitmapFactory.decodeResource(getResources(), bgnd);
c.drawColor(0, PorterDuff.Mode.CLEAR);
c.drawBitmap(b, 0, 0, null);
switching = false;
}else{
Bitmap b = BitmapFactory.decodeResource(getResources(), bgnd);
c.drawBitmap(b, 0, 0, null);
}
}
Just like you, I struggled how to clear a top layer/surfaceview in my multiple layer/surfaceview app. After 2 days searching and coding, I found out my own way and this is how I cleared a canvas before drawing, you can use it when having multiple layers/surfaceviews. The background layer will not be covered with black, that is the trick.
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
canvas.drawPaint(paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC));
// start your own drawing
You can use Canvas's method drawRGB.
Do you not need to also call invalidate() from within your onDraw method, so that changes made in that onDraw are updated on the screen?
The invalidate() in your switch will invoke your onDraw after you call setBgnd, but there's nothing saying to redraw after you've made changes to the Canvas.
I just meet this problem.
I solve it by repeating “..Drawxxx();Post();” for at least 3 times,it works well.
I guess ,“double buffering” is the reason,sometimes it is “trible buffering”.we need to repeat our drawing to make sure that each “buffer” is updated..