I am trying to introduce a delay between image change in a for loop. I tried Thread.sleep(1000), but the delay is introduced before the image change and after the total sleep period is over, the ImageView dispalys the final image. I tried a lot of solutions available on the internet but nothing works.
public void start(View view){
ImageView display = (ImageView)findViewById(R.id.imageView);
for (int i=0;i<6;i++){
display.setImageResource(R.drawable.dice1+i);
Thread.sleep(2000);
}
}
I need the iteration to wait for 2 seconds so that the image can be viewed.
Two problems.
1)Don't sleep on the main thread. Ever. Instead, create a Handler and use postDelayed with a Runnable that changes the image.
2)You can't do math on resource ids like that. You can't assume they'll be consecutive just because they're next to each other in the file. Create an array with the ids and use the image at index i.
You can get the sequence of image by the image name as you asked
private int image = 0;
private static final int MAX_IMAGES = 10;
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 55) {
image++;
if (image > MAX_IMAGES) {
image = 1;
}
imageView.setImageDrawable(getImageByName("dice" + image, TestActivity.this));
mHandler.sendEmptyMessageDelayed(55, 2000);
}
}
};
public static Drawable getImageByName(String name, Context context) {
Resources resources = context.getResources();
final int resourceId = resources.getIdentifier(name, "drawable",
context.getPackageName());
return ContextCompat.getDrawable(context, resourceId);
}
Now start the image sequence by calling
mHandler.sendEmptyMessageDelayed(55, 2000);
Your image will be changed per 2 secs
I think a CountDown timer can help in this situation. A few days ago I asked a similar question at stackoverflow, and I got this answer. I think it may be a good reference for you.
Related
I want to change the image of an Image view using a for loop, a String array and the png images in drawable folder.One Image should rest for 5 seconds then the next should appear.Here is the code I used.And it's not working properly.The images are not changing.It waits 5 seconds per each loop but the images are not changing.At last the last image is loaded.Please someone help me...
private String [] array1 = {"card2c","card2d","card2h","card2s",
"card3c","card3d","card3h","card3s",
"card4c","card4d","card4h","card4s",
"card5c","card5d","card5h","card5s",
"card6c","card6d","card6h","card6s",
"card7c","card7d","card7h","card7s",
"card8c","card8d","card8h","card8s",
"card9c","card9d","card9h","card9s",
"card10c","card10d","card10h","card10s",
"cardjc","cardjd","cardjh","cardjs",
"cardqc","cardqd","cardqh","cardqs",
"cardkc","cardkd","cardkh","cardks",
"cardac","cardad","cardah","cardas",};
for(int j=0;j<52;j++)
{
int resID_temp1 = getResources().getIdentifier(array1[j] , "drawable", getPackageName());
Drawable image_temp1 = getResources().getDrawable(resID_temp1);
Player1.setImageDrawable(image_temp1); // Player1 is the ImageView
try {
Thread.sleep(5000);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
Tried the Timer
(for(int j=0;j<52;j++)
{
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//switch image here
int resID_temp1 = getResources().getIdentifier(array1[j] , "drawable", getPackageName());
Drawable image_temp1 = getResources().getDrawable(resID_temp1);
Player1.setImageDrawable(image_temp1);
}
}, 0, 5000);
Based on your code example, you're changing the image resource, but then immediately sleeping the same thread that changed the image resource which will block it and prevent the image from visibly changing (particularly bad if this is the UI thread as this will lock up your app).
You should do as JohnyTex suggests and use a TimerTask to change the image every 5 seconds which does not require sleeping and so will not block the current thread.
Try using timers instead:
//Declare final variable here
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
//Switch (declared final) image variable here
}
});
}
}, 0, 5000);
Something like that... I am not by my computer though.
Concepts: runOnUithread and final variables might also be of interest to read about. final is because otherwise the variable cant be manipulated on another thread (actually Its only another scope really here). RunOnUiThread is because graphical components must only be manipulated on the main UI thread, although the TimerTask is run on another separate thread.
in my application i'm checking image of button for 1sec.n changing it.what happen is when first time it checks n changes image of button.and the 2nd time its not changing the image.
here is the code-
myTimer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
Random rand=new Random();
int num = rand.nextInt(buttonIds.length);
int buttonId = buttonIds[num];
Button bb=(Button) findViewById(buttonId);
Drawable a=bb.getBackground();
if(getResources().getDrawable(R.drawable.happy).equals(a))
{
bb.setBackgroundResource(R.drawable.happy);
}
else
{
bb.setBackgroundResource(R.drawable.whoa);
}
}
});
}
},0, 1000);
first time image of button is happy(name of image file).
how can i change image of button and also check??
Thanku
I think your if condition is not correct, you a checking if the background image is happy, you are again setting happy..instead you should set whoa...
If I were you, I would use a boolean(isHappy) to notify if the picture is happy or not, it is easier and faster since you don't have to instance the current image and maybe it will solve your problem, I am not completely sure about "equals" works like that, but is only my guess.
Anyway I think your code has a small mistake, the condition makes the picture to be always happy.
I have like 45 PNG files that I want to use in a animation sequence using the XML animation file. I noticed that I'm not able to animate past 10 PNG files, any more then that I get problems where the animation does play past the 10th image or it would just crash.
Is there a way to get past that limitation?
you need check the crash reason, if it the Out of memory error, you can try make the png pictures pixel smaller.
if it's the system animation size limit, you can try another method, you can create your view and in your view create a Handler, and handler send message every time(the animation every duration time), when receive the message, change the png.
added:
static final int MSG_ANIMATION = 1, MSG_CANCEL = 2;
static final int ANIMATION_DURATION = 500;
public Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
final int what = msg.what;
if (what == MSG_ANIMATION) {
// here change the animation png
setImageResource(..);
mHandler.sendEmptyMessageDelayed(MSG_ANIMATION, ANIMATION_DURATION);
} else if (what == MSG_CANCEL) {
removeMessages(MSG_ANIMATION);
}
};
};
public void startAnimation() {
// set first animation png
setImageResource(..);
mHandler.sendEmptyMessageDelayed(MSG_ANIMATION, ANIMATION_DURATION);
}
public void stopAnimation() {
mHandler.sendEmptyMessage(MSG_CANCEL);
}
I am trying to put an image on the screen and change it every five seconds. This is not in an Activity class. Sorry if is a stupid question. This is a huge learning curve for me.
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
private Handler mHandler = new Handler();
//stuff
public void doDraw(Canvas canvas) {
int counter = 0;
canvas.drawColor(Color.BLACK);
dot1.doDraw(getResources(), canvas, counter);
mHandler.removeCallbacks(panelDraw);
mHandler.postDelayed(panelDraw, 5000);
}
private Runnable panelDraw = new Runnable() {
public void run() {
}
};
(Panel->)mPanel.doDraw(canvas) in another class calls the doDraw listed in Panel. This is in a loop.
As far as I can tell, the first image isn't stopping for five seconds, and due to other code the cat pic flashes all over the screen. Can I get away with putting nothing in run()? I wanted to put the dot1.doDraw(getResources(), canvas, counter) one in there, but I couldn't/didn't think I could give void run() parameters or Runnable panelDraw resources, canvas, counter parameters.
Any help is appreciated. Let me know if I need to give more code.
A little more about my game: Kitten photos should appear one at random locations on the screen. If one does not click on the current kitty image with an allotted time, the game ends. These pictures last < 1 sec in the end (right now it's more for testing purposes). If you don't click the kitty in that time span, the game ends.
I heard that if you need to do a lot of drawing, you're better off using SurfaceView like in the tutorial I followed very closely: http://www.droidnova.com/2d-tutorial-series-part-v,848.html My Activity class (Scene1) has setContentView(new Panel(this)), so I don't know how to put stuff in the Activity class when it all goes in the Panel class. Like where do I put the ImageView code if the Activity thread doesn't really do much and hands it all off to Panel? Also if I have a ViewThread (public class ViewThread extends Thread) that handles the running:
public void run() {
Canvas canvas = null;
while (mRun1) {
do {
canvas = mHolder.lockCanvas();
if (canvas != null) {
mPanel.doDraw(canvas);
mRun1 = true;
mRun1 = mPanel.get_run();
mHolder.unlockCanvasAndPost(canvas);
}
} while (mRun1 == true);
The dot class has something like:
public void firstRing(Resources res, Canvas canvas, int counter){
Random rand = new Random();
switch(counter) {
case 0: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat1_m);
break;
case 1: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 2: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 3: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 4: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 5: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat1_m);
break;
}
mX = rand.nextInt((int)(Panel.mWidth - mBitmap.getWidth()));
mY = rand.nextInt((int)(Panel.mHeight - mBitmap.getHeight()));
canvas.drawBitmap(mBitmap, mX, mY, null);
and
public void doDraw(Resources res, Canvas canvas, int counter) {
firstRing(res, canvas, counter);
}
Thanks for the info, Barry, although I am a little confused. Do I try ImageView or do I stick with Handler(), although I don't know how to use it and it's not working now? Do I need something in public void run()?
This code should work better than my last answer, but I haven't compiled or tested it. Even if it doesn't work it will point you in the right direction. I assume you have an Activity subclass somewhere so just copy my code into your activity. Your layout should have an <ImageView> tag in it somewhere.
The code below creates a Handler in Activity.onCreate(), which is run in the UI thread, guaranteeing all calls to the Handler will also be run in the UI thread. It then calls showNextKittyImage() to display the first image. showNextKittyImage() sets the Drawable resource id for the ImageView, and then calls Handler.sendEmptyMessageDelayed() to call the handler 5 seconds later. The Handler calls showNextKittyImage() and the cycle repeats.
An ImageView is still preferable to a SurfaceView because it is designed to display images. All you need to do is pass it the Drawable resource id instead of messing with Bitmaps. If you really want or need to do it with a SurfaceView then I cannot help you.
This code will also not make the kittens appear at random locations. Get it working in one location first, then add code to move the ImageView around the screen randomly. If you need help at that point I strongly recommend you post a new question as you've asked A LOT for one posting.
Good luck,
Barry
public class KittyActivity extends Activity {
Handler mHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate your view
setContentView(R.layout.YOUR_LAYOUT);
// Create a handler ON THE UI THREAD
mHandler = new Handler(Message result) {
public void handleMessage(Message result) {
showNextKittyImage();
}
};
// Show the first kitty
showNextKittyImage();
}
public void showNextKittyImage() {
int kitty = getNextKitty();
ImageView iv = findViewById(R.id.YOUR_IMAGE_VIEW_ID);
iv.setImageResource(kitty);
// OPTIONAL: Move the ImageView to a new random location
mHandler.sendEmptyMessageDelayed(0, 5000);
}
private int getNextKitty() {
// Your code to get the next kitty drawable id
}
}
I've got 30+ single bitmaps (320x240 pixels) that I would like to display one after another in full screen on Android devices resulting in an animation. Currently I implemented the animation using an ImageView and a Timer that sets the next frame and then sends a message that will apply the next frame. The resulting frame rate is very low: < 2 fps.
The timer:
animationTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
Drawable frame = getNextFrame();
if (frame != null) {
Message message = animationFrameHandler.obtainMessage(1, frame);
animationFrameHandler.sendMessage(message);
}
}
}, 0, (int) (1000.0d / fps));
The handler:
final Handler animationFrameHandler = new Handler() {
#Override
public void handleMessage(Message message) {
setImageDrawable((Drawable) message.obj);
}
};
Since I want to achieve frame rates up to 30 fps I have to make use of another mechanism and heard of Canvas.drawBitmapMesh() and OpenGL.
If possible I would like to avoid using OpenGL.
Thank you very sharing your experiences!
My now working approach is the following:
Before starting the animation, load every frame into a List<Bitmap>. Important: Call System.gc() if you're getting OutOfMemoryErrors – that really helps loading more bitmaps into the memory. Then have a thread running that posts the next frame to a View instance that then update it's canvas.
Loading the frames and starting the animation
// Loading the frames before starting the animation
List<Bitmap> frames = new ArrayList<Bitmap>();
for (int i = 0; i < 30; i++) {
// Load next frame (e. g. from drawable or assets folder)
frames.add(...);
// Do garbage collection every 3rd frame; really helps loading all frames into memory
if (i %% 3 == 0) {
System.gc();
}
}
// Start animation
frameIndex = 0;
animationThread.start();
Thread that applies the next frame
private final class AnimationThread extends Thread {
#Override
public void run() {
while (!isInterrupted()) {
// Post next frame to be displayed
animationView.postFrame(frames.get(frameIndex));
// Apply next frame (restart if last frame has reached)
frameIndex++;
if (frameIndex >= frames.size()) {
frameIndex = 0;
}
try {
sleep(33); // delay between frames in msec (33 msec mean 30 fps)
} catch (InterruptedException e) {
break;
}
}
}
}
The animation view
class AnimationView extends View {
Bitmap frame = null;
public void postFrame(Bitmap frame) {
Message message = frameHandler.obtainMessage(0, frame);
frameHandler.sendMessage(message);
}
protected final Handler frameHandler = new Handler() {
#Override
public void handleMessage(Message message) {
if (message.obj != null) {
frame = (Bitmap) message.obj;
} else {
frame = null;
}
invalidate();
}
}
#Override
protected void onDraw(Canvas canvas) {
if (frame == null) return;
canvas.drawARGB(0, 0, 0, 0);
canvas.drawBitmap(frame, null, null, null);
}
}
You should look at the FrameAnimation class; http://developer.android.com/guide/topics/graphics/2d-graphics.html#frame-animation to do frame animation with Androids animation.
Though that might still be too slow.
The other alternative if you don't want to use OpenGL ES is to draw to the Canvas as you've mentioned. But just use .drawBitmap, not the drawBitmapMesh. Create a SurfaceView, which has a thread, that thread should draw on your Canvas at whatever interval you want.
It's pretty straightforward, just read the Android docs, the information is all there.
I'll let someone else go into the best way of doing this but one thing that immediately jumps to mind from your post that isn't helping is using TimerTask is a terrible way to do this and is not meant for animation.
Probably won't help with performance, but if those bitmaps are resources you might want to consider using an AnimationDrawable. If not, try to extend Drawable and implement the Animatable interface. Views already have built-in support for animating drawables, no need to use a handler for that.
One way to improve performance might be to match the bit-depth of the drawables to those of your current window. Romain Guy did a keynote on this and animations in general once: http://www.youtube.com/watch?v=duefsFTJXzc