Android onDraw loop - android

I've set global variables x,y in the Activity class.
I start a thread "t0" that continually update globals x and y.
I have onDraw pseudocode as follows (all on the UI thread):-
View.onDraw(){
if (x,y changed value) {
x0=x;
y0=y;
loop (x0-- until x0==0){
canvas.drawBitmap(bmp, x0, y0, bitmapPaint);
invalidate();
}
}
}
I was hoping that I'd see an animation of the bitmap moving across the screen on the x-axis, with each invalidate() re-drawing the new position. Instead I see it 'jump' to the last x position 0 (no intermediate stages).
I'm making the assumption that although x and y are updating via t0, I'm not too concerned since the loop is busy with the original x,y values (assigned to x0,y0).
I observe x,y updating and code is executed inside the 'if loop' (I see this via debug).
I tried adding a delay, but it didn't seem to make any difference. I can get it to re-draw directly to a new x,y position, but I need a smooth 'transition' via the loop to happen from one-x0-coord to another.
Any hints or tips would be appreciated.
Thanks in advance.
Steve

I think you can't cause an onDraw within an onDraw. calling invalidate() only causes that onDraw will be called after the current onDraw finishes. I am not sure how you really implemented it since it's pseudocode but i imagine you don't want the loop in the onDraw but do somethinglike this:
View.onDraw(){
if (x,y changed value) {
x0=x;
y0=y;
if (x0!=0){
x0--;
canvas.drawBitmap(bmp, x0, y0, bitmapPaint);
invalidate();
}
}
}

What is happening here I guess is, intermediates are getting drawn. But there is not enough time to show those. you have to set some thread.sleep inside it. It will work.

Related

Override onDraw() or draw()?

My project is based on surfaceView and up until now I've had all of my rendering in onDraw which I am overriding. All seemed to be OK.
However, I've just updated my SDK and now it gives me an error telling me:
Suspicious method call; should probably call "draw" rather than "onDraw"
Could someone please explain the difference between these two?
I've read some similar questions around the net but I've not found an explanation that I understand.
Thanks
I tried cleaning my project and it did solve the problem. Try it.
SurfaceView.draw() basically calls View.draw(); If you want to implement your drawing, you should do it in View.onDraw() which is for you to implement which even says in the source code comments.
This method is called by ViewGroup.drawChild() to have each child view
draw itself. This draw() method is an implementation detail and is not
intended to be overridden or to be called from anywhere else other
than ViewGroup.drawChild().
As for difference between them:
draw():
13416 /*
13417 * Draw traversal performs several drawing steps which must be executed
13418 * in the appropriate order:
13419 *
13420 * 1. Draw the background
13421 * 2. If necessary, save the canvas' layers to prepare for fading
13422 * 3. Draw view's content
13423 * 4. Draw children
13424 * 5. If necessary, draw the fading edges and restore layers
13425 * 6. Draw decorations (scrollbars for instance)
13426 */
onDraw() is empty. Its for you to implement.
I have the problem since ever.
I handle it like this:
1) Declare a method like the following.
#SuppressLint("WrongCall")
public void drawTheView() {
theCanvas = null;
try{
theCanvas = getHolder().lockCanvas();
if(theCanvas != null) {
onDraw(theCanvas);
}
} finally {
getHolder().unlockCanvasAndPost(theCanvas);
}
}
2) Now you can modify the onDraw() Method:
#Override
public void onDraw(Canvas canvas) {
//Do some drawing
}
You can call the drawTheView() method from everywhere you want and call the onDraw() method this way without getting the error...
I think this is a practical way.
Note that in the case of drawing, overriding draw() and calling super.draw is often used when a ViewGroup wants to draw content over its child views. Content drawn in onDraw will appear under children.
As friiky said, #SuppressLint("WrongCall") fixed my problem. However it must be in front of the method name, not the above.
What I did is put mouse over the error code, right click and select Add #SuppressLint("WrongCall")
onDraw gives you a canvas to draw to the screen.
draw() allows you to manually draw a canvas to the screen (you have to make the canvas yourself).

android updateThread draw() method and touchEvent interference

I have a game which displays an array of colored blocks. The user can move these blocks around the screen. When a touch event occurs, I take note of the cell that has been touched (cell[i][j].isMoving = true). If the user moves the block around I draw the rectangle relative to an offset value. When a touch up event is detected, I check whether or not the user has dragged the block far enough to signify a moving of a block.
My basic draw loop is as follows:
for(int i = 0; i < xCells, i++){
for(int j = 0; j < yCells; j++){
if(cell[i][j].isMoving)
canvas.drawRect(...) // draw with offsets
else
canvas.drawRect(..)
}
}
The problem I am having is when a user releases the block it occasionally flickers briefly.
When a touch up event occurs, the offset must be set to 0, the coordinates of the block changed (if requirements are met) and isMoving has to be set to false.
As I have a thread constantly running that calls the draw code above, it appears that the UI thread is altering the array of blocks, meaning it is in an inconsistent state when the draw method occurs.
Does anyone have suggestions on how to fix this? Could I use a handler? I've tried synchronizing the onTouchEvent method and the onDraw method, but this seems to occasionally block user input
thanks
The safe solution is to only allow one thread to create objects, add and remove them from a List or array after the game has started.
I had problems with random AIOOBEs (Array Index Out Of Bounds Exception) errors and no synchornize could solve it properly plus it was slowing down the response of the user.
My solution, which is now stable and fast (never had an AIOOBEs since) is to make UI thread inform the game thread to create or manipulate an object by setting a boolean flag and coordinates of the touch into simple variables.
Since the game thread loops about 60 times per second this proved to be sufficent to pick up the message from the UI thread and do something about it.
In your case onTouch event should never alter cell[i][j] directly but only set some integer to a particluar value. Then your thread would at some point evaluate this variables and set cell[i][j] and then draw, so onTouch can never unpredictably interfear with your thread.
This is a very simple solution and it works great...

Canvas do not update (invalidate) until whole while loop ends

I'm trying to move a ball on canvas. a and b are similar to x,y coordinate positions. Any way from my code im trying to get different values dynamically. The a,b are global variables. But it seems that "invalidate()" or the refreshing of screen only happens afer end of the whole loop. Do you know why?. And if i have to build this on another thread please suggest me with some simple codes.
private void shootBall(){
while (a>b){
a = getPositionX();
b = getPositionY();
invalidate();
}
}
}
I think it's more correct to say that you can call invalidate() from within a loop, but that that invalidation will not be handled (the canvas won't be redrawn) until after your loop is complete. The problem is that you are calling invalidate on the same thread (the UI toolkit thread) as the one that would call your onDraw() method. So unless/until you hand control back to the toolkit, it cannot possibly do the rendering. So your invalidate() call does actually invalidate the view ... but the view won't be redrawn until after your loop completes and your function returns.
It is more correct to change the position in some function that is called via some timer (which is essentially what the animation classes do). In that function, you would change the values, invalidate() appropriately, and return. Then the toolkit re-renders the scene and your function will get future callbacks and update the position accordingly.
do it like this, and use postInvalidate() instead:
private void shootBall(){
new Thread(new Runnable() {
public void run() {
while (a>b){
a = getPositionX();
b = getPositionY();
postInvalidate();
}
}
}).start();
}
edit: but as mentioned before, don't assume that the invalidate redraws the screen, it marks it as to-be-redrawn and the UI thread will get around to it.
You can put invalidate() at the end of onDraw() like in this example: How can I use the animation framework inside the canvas?
However this works well on some devices while bottlenecks and slows down on other.
To use a thread and SurfaceView go through all of these tutorials: http://www.droidnova.com/playing-with-graphics-in-android-part-i,147.html
UI cant be modified form any new thread..you should use invalidate() in the same thread where your view

How to make an array or arraylist of Rect

I am going crazy trying to figure this out. I am trying to make an array or arraylist of Rect (rectangles) that I can update the coordinates and draw on the screen(to make them move).
Right now I have a separate class called Fire in which I make a new rectangle each iteration with the new coordinates in its own onDraw() method. In the View's onDraw() method's first iteration I add a new Fire to an arraylist.
In the Fire class I have:
public void onDraw(Canvas canvas){
moveF();
Rect r = new Rect(_l,_t,_r,_b);
canvas.drawRect(r, paint);
}
In the View class I have:
protected void onDraw(Canvas canvas) {
int i = 0;
canvas.drawColor(Color.WHITE);
if(i==0){
fires.add(new Fire(20,100,40,120));
i++;
}
for(Fire fire : fires){
fire.onDraw(canvas);
}
}
I got rid of pointless parts of code, but this is the important stuff. The Rectangle prints, however it prints all the previous locations as well and I don't understand why. I have been trying to fix this forever and any help you guys could give would be greatly appreciated. I was able to implement this in java easy, but android is giving me problems.
Thanks in advance!!!
From the code, you are adding new rectangles to the list, and then drawing each rectangle. But from the description, it seems that you just want to draw a single rectangle, with updated coordinates.
Instead of creating a new Rect each time, reuse a rectangle and update its coordinates with set(...).
A second problem is that you set i=0 and then immediately check for i==0, which would be always true. Try something like this instead:
static final Rect rect = new Rect();
// your code here
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
moveF();
rect.set(_l,_t,_r,_b);
canvas.drawRect(rect, paint);
}
With each call to View.onDraw you create a new fire and add it to your list.
Then you iterate over all fires and draw them.
So you get more and more fires.
Do all fires move the same way or is there some random component?
It might be that you don't see the previous locations of one fire, but that there are many fires moving on the same path.
Do you want to have 1 fire moving around or more and more fires moving around independently?

Custom View - Avoid redrawing when non-interactive

I have a complex custom view - photo collage.
What is observed is whenever any UI interaction happens, the view is redrawn.
How can I avoid complete redrawing (for example, use a cached UI) of the view specially when I click the "back" button to go back to previous activity because that also causes redrawing of the view.
While exploring the API and web, I found a method - getDrawingCache() - but don't know how to use it effectively.
How do I use it effectively?
I've had other issues with Custom Views that I outline here.
I found a better way than using getDrawingCache.
In the method onDraw, apart from drawing in the natural canvas, I also draw on an memory-only canvas.
Bitmap cacheBmp = Bitmap.Create(....);
Canvas cacheCanvas = new Canvas(cacheBmp);
void onDraw(Canvas c)
{
if(updateDueToInteraction)
{
c.drawXXX(...);
cacheCanvas.drawXXX(...);
} else
{
c.drawBitmap(cacheBmp, 0, 0);
}
}
First of all you will have to use the setDrawingCacheEnabled(true) method, so that you're View is cache-enabled. Then, you can use the getDrawingCache(boolean) method which returns a Bitmap representing the View. Then, you can draw that bitmap manually.
If you don't enable caching by calling the setDrawingCacheEnabled(true) method, you will have to call buildDrawingCache() before (and call destroyDrawingCache() when you're done).
Bye!

Categories

Resources