view.invalidate() not working to redraw imageview - android

Ok guys, this may sound dumb, but I have been banging my head against the keyboard for some time now trying to figure out why this will not refresh. the basics: I have a little sample app that i am testing to see if i can rotate an image around a point a X amount of degrees, and show it one degree at a time to make a smooth animation. So I have a great sample i found that works great with a slider bar, basically setting the images rotation to a point on the slider bar, great! but.... when i try and create a for loop with a random number and use my for variable updating the image along the way every degree... it does nothing... and all i get is the updated image at the end... but when i drag my finger on he slider bar the graphic is updated instant as me spinning it... I cant figure out what i am doing wrong here... here is the code with the slider... i don't have my piece that creates the random number and draws it but essentially i did it behind a button click
essentially if you look at this piece i did the same behind a button again but it doesnt do it "real time". i called view.invalidate() and view.postinvalidate() to try to force it but no go...
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
curRotate = (float)progress;
drawMatrix();
}
private void drawMatrix(){
Matrix matrix = new Matrix();
matrix.postScale(curScale, curScale);
matrix.postRotate(curRotate);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bmpWidth, bmpHeight, matrix, true);
myImageView.setImageBitmap(resizedBitmap);
}

I think what you did was something like:
for (int degrees = 0 ; degrees < maxRotation ; i++) {
// perform the rotation by matrix
myImageView.invalidate();
}
This wouldn't work because invalidate() only schedules a redraw in the main thread event queue. This means that the redraw will be performed only when the current code has all been executed (in this case, the for cycle).
For a simple rotation a Tween Animation would be better suited. For more advanced stuff (like game animations) you might need to create a custom view or use SurfaceView.

Sounds like you're blocking the UI thread with your code to rotate the image.
I don't have any code to show you right now (reply back and when I'm home tonight I can post something that should help), but yuo will probably get better results placing your rotate code in an AsyncTask, see the Painless Threading area of the dev site for more info.

I was having the same problem, i used:
runOnUiThread(new Runnable() {
public void run() {
myImageView.setImageBitmap(image);
imageView.invalidate();
}
});

Related

How to display a larger image in a smaller ImageView with a sliding effect in Android?

i am having a image of size 1000X400px. Assuming the screen width of the target android device to be 320px. i want to slide that image from right to left. actually i want to do this for a sliding 2d scrolling platformer that i was trying to make. so please help as i am just a beginner(only 2 weeks into android dev.)
i was thinking if i could somehow make the program sleep for some time and i did this:
int START_X = 0;
int START_Y = 0;
final int WIDTH_PX = 320;
final int HEIGHT_PX = 400;
Bitmap SOURCE_BITMAP = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.background);
Bitmap img = Bitmap.createBitmap(SOURCE_BITMAP, START_X, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg=(ImageView)findViewById(R.id.imageView1);
bg.setImageBitmap(img);
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
img = Bitmap.createBitmap(SOURCE_BITMAP, START_X+100, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg.setImageBitmap(img);
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
img = Bitmap.createBitmap(SOURCE_BITMAP, START_X+200, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg.setImageBitmap(img);
but it directly executes the last statements after waiting for 4000ms i.e.
img = Bitmap.createBitmap(SOURCE_BITMAP, START_X+200, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg.setImageBitmap(img);
First of all: the reason why you only see the effect of the last setImageBitmap(img) is because the method doesn't actually stop everything and draw the bitmap. It just sets the "drawing process" in a queue and it takes care of it a little bit later.
The reason why it didn't render the bitmap even with the Thread.sleep call, is because you we're freezing the thread which renders the screen. :) The code you write (unless you write it in a separate thread) runs on the same thread as the code that handles the screen drawing. If you freeze that ... you basically block the entire app.
Second: As mentioned, Thread.currentThread().sleep(2000) is not a good approach to stop the thread. The main thread of an Android app is called the UI thread. If you freeze that, your ENTIRE app will freeze. So don't do it, whatever the case. :) Use AsyncTasks instead ... or the postDelayed method of the Handler or some other way. There are a couple more.
Now, to get back to the matter at hand:
If you want to create an Android game, I suggest looking over the libGDX game development framework.
But if you don't want to go right into that, the easiest way of doing what you've described above would be to use an Animation. See this guy's answer.
Another approach could be to use a ValueAnimator, see this . What it does is ... you set a start and end value, i.e. screenWidth and 0 and you set a duration in miliseconds. Some "magic" happens in the back and a method onAnimationUpdate is called periodically with values between the start and end values you entered . You can take that value and set it as the left position of the ImageView. This might move the View from right to left and obtain the effect you want.
A more "advanced" method of doing it
Now ... a good way of doing it, which comes to mind, could be to:
create a class that overrides the ImageView class.
Set that in the .xml file of your layout and make it match_parent.
In its constructor you could load the Bitmap value into memory and keep it in a Bitmap local attribute.
Now .. the next thing you could do is override the onDraw method of the ImageView.
In it, you could use the drawBitmap method of the Canvas object. It accepts a srcRect and destRect which basically describesc what chunk from the original image goes where on the ImageView's canvas.
You could have a separate thread to keep the duration by which the image moves along the screen and that thread could call the invalidate method of your ImageView thus forcing the onDraw method to be called and thus redrawing the bitmap a little bit more to the left.

Speed up bitmap animation in custom View

In my custom view i have 1 animation that i need to run at demand (on tile click). Currently i am using this method:
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//check what tile for clicked
getHandler().removeCallbacks(explosionThread);
getHandler().post(explosionThread);
}
}
break;
}
return super.onTouchEvent(event);
}
So i am calling (or sending to view thread, to be specific) a Runnable that calls it self until it comes to an end of an image...
private Runnable explosionThread=new Runnable(){
#Override
public void run() {
invalidate();
if(expCount<15){
getHandler().postDelayed(this, 10);
}
}
};
In my onDraw() method i implemented logic to go threw bitmap and draw it on screen ( using cnavas.drawBitmap(bitmap,srcRect,destRect,paint)....
Now, I want to avoid using SurfaceView (i have only 1 animation and View uses less resources).
I think the animation is slow because onDraw needs to draw whole screen each time invalidate() is called witch can be slow ( drawing 64 tiles with png images). I know that there is an overload of invalidate method, invalidate(Rect dirty) but i don't really know how to use it. If u think that that is the answer please write how to avoid drawing whole onDraw method ( or what method can I overwrite that is used by invalidate(Rect) method, if there is any).
If you have any other better way to speed up animation post it plz.
Thanks in advance....
That's right. One of the way to speed up rendering through canvas is to use invalidate(Rect). Rect passed to invalidate method defines area which will be redrawn. Your onDraw will be called after invalidate with clipping region being set up on canvas. So all your "drawBitmap" will be clipped by the rect.
for running the animation are using a .gif file or you are using a sequence of images run on a thread to show as an animation ?

using canvas.drawBitmap() with canvas.translate()

My code here receives a value for bmpX and bmpY and uses them to draw a bitmap at that location on the screen. This works exactly as I want it to, but I would rather use the canvas.translate() function to handle the movement of the image, rather than just canvas.drawBitmap(), because I will be applying other draw elements to the canvas and I want them all to move the same amount and direction.
My question is: where can I move the canvas.drawBitmap() in the code so that it just draws onto the canvas at the start, but it is moved any time after that using canvas.translate() instead? Anywhere I place the code still freezes the image at that point where I draw it, regardless of how much I change the position of canvas.translate(). I'm imaging its possible to "stick" the image to the canvas, so to speak, and then move the canvas, which also moves the image with it.
public void run() {
// TODO Auto-generated method stub
while(isRunning) {
if(!surfaceHolder.getSurface().isValid()) {
continue;
}
canvas = surfaceHolder.lockCanvas();
canvas.drawRGB(255, 255, 255);
canvas.drawBitmap(bmp, bmpX-(bmp.getWidth()/2),
bmpY-(bmp.getHeight()/2), null);
//canvas.translate(bmpX, bmpY);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
I hope I explained my problem clearly enough. Thanks!
Answering my own question here for clarity, afetr info I got offline. UI cannot be done on a thread like this, I needed to be using an android UI thread instead. runOnUIUpdatethread and async tasks

Detach child, update, and attach? AndEngine, Android

engine.registerUpdateHandler(new TimerHandler(0.2f,
new ITimerCallback() {
public void onTimePassed(final TimerHandler pTimerHandler) {
pTimerHandler.reset();
Rectangle xpRect = new Rectangle(30, 200,
(float) (((player.getXp()) / (player
.getNextLevelxp())) * 800), 40, vbom);
HUD.attachChild(xpRect);
}
}));
I have this so far in my createHUD method. It's pretty simple, it creates a rectangle showing the player's xp in relation to the xp needed for the next level and attaches it to the HUD. The only problem is that the old rectangle is never deleted. How can I have a rectangle like that one that updates itself and removes old ones?
If you use the detachChild() or any other detach method too regularly, you might run into problems sooner or later. Especially because the detaching can only be made on an update thread. You'll never know when exactly your rectangle will be detached again. So, to save you a lot of attaching and detaching, reuse the rectangle:
i) Save a reference of the Rectangle somewhere (as a global variable for example in your Playerclass).
ii) At the beginning when you load your stuff also initialize the rectangle:
Rectangle xpRect = new Rectangle(30, 200, 0, 40, vbom); // initialize it
HUD.attachChild(xpRect); // attach it where it belongs
xpRect.setVisible(false); // hide it from the player
xpRect.setIgnoreUpdate(true); // hide it from the update thread, because you don't use it.
At this point it doesn't matter where you put your rectangle or how big it is. It's only important that it is there.
iii) Now when you want to show the player his XP you only have to make it visible
public void showXP(int playerXP, int nextXP){
float width= (float) ((playerXP / nextXP) * 800); // calculate your new width
xpRect.setIgnoreUpdate(false); // make the update thread aware of your rectangle
xpRect.setWidth(width); // now change the width of your rectangle
xpRect.setVisible(true); // make the rectangle visible again
}
iv) When you no longer need it: In order to make it invisible again just call
xpRect.setVisible(false); // hide it from the player
xpRect.setIgnoreUpdate(true); // hide it from the update thread, because you don't
Of course you can now use the showXP() method in anyway you like and use it in your TimerHandler. If you want a more effect full appearance you do something like this instead:
public void showXP(int playerXP, int nextXP){
float width= (float) ((playerXP / nextXP) * 800); // calculate your new width
xpRect.setIgnoreUpdate(false); // make the update thread aware of your rectangle
xpRect.setWidth(width); // now change the width of your rectangle
xpRect.setVisible(true);
xpRect.registerEntityModifier(new FadeInModifier(1f)); // only this line is new
}
It's actually the same as the above method with just a little change in the last line, that makes the rectangle appear a little more smoothly...
To detach a child from a HUD ,you can write
aHUD.detachChild(rectangle);
To clear all children from HUD
aHUD.detachChildren();
To to clear all HUD from camera , you can write
cCamera.getHUD().setCamera(null);
After using one of above, You can create a HUD & also attach as usual.

Animate Bitmap to Move on a Curve

How would I animate a bitmap in android so that it moves across the screen in a parabolic arch or any other curved path? Currently, the method I'm using is to use the onDraw() method to draw a bitmap to the canvas with an x/y coordinate and then increasing that x/y coordinate by one after the bitmap has been drawn, at which point the method calls invalidate() to redraw the bitmap with the new position.
Update:
Maybe this will give a bit more context to what i'm trying to do. Below is the implementation I have right now for animating my bitmap:
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(gBall, x, y, null);
x += changeX;
y += changeY;
if(x >= (canvas.getWidth()-gBall.getWidth()) || x <= 0)
changeX = -changeX;
if(y >= (canvas.getHeight()-gBall.getHeight()) || y <= 0)
changeY = -changeY;
invalidate();
Is there a way while still using this implementation to make the bitmap gBall curves as it approaches the edge of the screen?
Use a Handler to controle the speed :
public void draw(Canvas canvas, ...){
if (System.currentTimeMillis() - lastCall < PERIOD-50){
mHandler.postDelayed(mReDrawRunnable,PERIOD);
return;
}
//to call back
mHandler.removeCallbacks(mReDrawRunnable);
mHandler.postDelayed(mReDrawRunnable,PERIOD);
lastCall = System.currentTimeMillis();
//your code here
...
}
private long lastCall = 0;
private static final PERIOD = 250; //millis
private Handler mHandler = new Handler();
private Runnable mReDrawRunnable==new Runnable() {
public void run() {YourClass.this.invalidate();}
};
This is a quick way to do it, it should work. You should create an other thread to control the drawing.
Implement a custom Animator. To implement a custom animator, all you have to do is overide the applyTransformation method of the Animator class. You can then call View.startAnimation with an instance of your custom class. Given the lengths that google developers have gone to to implement smooth animations, this is likely to be the best solution -- much better than performing actions off a handler, which is likely to cause glitches due to garbage collects. A properly implmement Animation, that performs no memory allocations in its applyTransform method can run without incurring any garbage collects at all.
If you look at platform sources, it quickly becomes apparent that glitch-free animations were a primary development goal in Android 4.x. Google engineers have put a lot of work in to making sure that Animations run without glitches. Your draw-and-invalidate strategy may actually work plausibly well. The Handler approach not so much. But if it were me, I'd take the extra time to leverage the effort that has been put into Animations.

Categories

Resources