I'm trying to create an Android app that will include many (sometimes up to about 200) small images that will be animated (relocate and change size) about 20 at a time. After that they'll remain still.
Should I use the simple solution of the View class animation or should I use Drawable animation?
EDIT: I should be more specific..
There are a lot of tutorial out there and a lot of different ways to do the same thing. I'm looking for the best way to implement the next scenario:
Say I have 50 different small (30x30) images currently drawn on the screen.
I need to animate them so they will translate to a different DYNAMIC position. And while they are moving I need the image to be resized up and down (so I get kind of a jump effect if looking from top).
They need to move within a specific timeframe. For example: After the first image starts to move, the second will begin moving 50ms after the last and so on (wave effect)...
After one group of images is translated, another group will be formed, but the last group will still be on screen.
So what I'm asking is a little specifics about the best way to do this. For example: Should I create a XML file for each Image or should I just load them in code? Should I load all the images (there could be up to 200 small images, maybe more) at application start or will it be ok to load them on demand? What would be the best animation technique? Stuff like that.
The easiest solution I found: (API 16+)
Runnable endAction = new Runnable() {
public void run() {
tv.animate().x(400).y(1400).scaleX(1).scaleY(1);
}
};
tv.animate().x(600).y(100).scaleX(3).scaleY(3).withEndAction(endAction);
I would use Drawable animation but it doesn´t matter so much. The important thing you should do if the app runs very slow, is to use diferents threads using this code for example:
Handler mHandler = new Handler();
mHandler.post(new Runnable() {
public void run() {
//YOUR ANIMATION HERE
}
});
In this way, you will be able to process the animation of a lot of images at the same time because the phone will execute the code in different computing threads.
You can use too AsyncTask like that (adding the class into your activity class):
private class doAnimation extends AsyncTask<ImageView, Void, Void>{
#Override
protected Void doInBackground(ImageView... image) {
image.startAnimation(animation);
return null;
}
}
And calling it using:
new doAnimation().execute(image1);
new doAnimation().execute(image2);
new doAnimation().execute(image3);
...
Related
I have an activity that show some full screen images in crossfade. There are a total of 6 images. To do this I used 2 ImageViews and 2 animation playing at the same time, one that fades out the first image and one that fades in the second. I used this video as a reference https://www.youtube.com/watch?v=9XbKMUtVnJA
Because I need this to run continuously, I used a timer to schedule the animation every 4 seconds. Everything works, but it uses a huge quantity of memory. To load the images I tried:
the basic not recommended way used in the video, i.e loading all images in a drawable array
setting the images using imageview.setImageResource
loading the images from assets using Picasso
All this method are memory intensive a cause an out of memory exception on older devices (like a Galaxy S2). The Picasso approach isn't working properly.
I'm sure there's a better way of doing this, but I don't know it, any suggestions?
Here's the relevant code:
private void animateImageview(){
prevImageView.animate().alpha(0);
nextImageView.animate().alpha(1).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mCurrentDrawable =
(mCurrentDrawable + 1) % imagesToShow.length;
int nextDrawableIndex =
(mCurrentDrawable + 1) % imagesToShow.length;
prevImageView.setImageResource(imagesToShow[mCurrentDrawable]);
nextImageView.setImageResource(imagesToShow[nextDrawableIndex]);
nextImageView.setAlpha(0f);
prevImageView.setAlpha(1f);
}
});
protected void onCreate(Bundle savedInstanceState) {
...
prevImageView.setImageResource(imagesToShow[0]);
nextImageView.setImageResource(imagesToShow[1]);
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
animateImageview();
}
}, 5000, 4000);
}
Edit:
the largeHeap option in the manifest seems to solve the problem, but I'm not convinced that I'm doing this thing right anyway.
Edit2:
A gist with the solution I used https://gist.github.com/alexmazza/003e3449c02fe58848a9
Please remove the largeHeap option from the manifest. It's not fixing the problem, it's sweeping the problem under the rug.
This is the problem
The images are 1536x2048 loaded in an imageview
You shouldn't just try to load the image at maximum resolution, because, as you said, in a S2 for instance, which has a display of 800 x 480, what's the point?
Generally speaking, what you should do instead is load a bitmap into memory as big as you need it to be (in your case it should be as big as the ImageView). You can follow a very good tutorial for this in the official docs.
To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it, unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.
[....]
For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.
Hope this helps.
I've got a problem with creating gameloop for my first game. I've read a lot about it but still can't figure it out. It's based on OpenGL so I've used onDrawFrame as a game loop and it works fine on my phone. Problem is that onDrawFrame is refresh time depends on hardware so it runs way too fast on some devices. So what I want is adding a separate game loop that will refresh itself at constant period of time on all smartphones. (and onDrawFrame will only take care of graphics as it should)
As for now I have:
myGameRenderer class with all openGl stuff an onDrawFrame
myGLSurfaceView that supports touch events
myGameActivity
onDrawFrame activates myGameUpdate function that controls changing positions of all objects in game depending on info from myGLSurfaceView
I've tried with creating new Runnable but it doesn't seem to work, I can't figure out how to start that runnable and where i should place it (I've tried to place it in myGameRenderer class, but it didn't seem to work, nothing was moving:
private final Runnable mUpdateDisplay = new Runnable() {
#Override
public void run() {
update();
}};
private void update() {
//some update stuff blablabla
//some update stuff blablabla
mHandler.postDelayed(mUpdateDisplay,40); //to refresh at 25 fps
}
but I guess I don't get the idea of it - I mean I create this runnable.
I've tried to place it in onCreateSurface to start it but no effect.
So - is the generall idea ok? And how to start the loop? Where to place it? Or should I use any other way?
Ok it was simple - I was just missing r.run();
But as allways there's something. Now it works as i wanted - I mean frames doesn't depend on hardware, but everything is not as smooth as it was - and part of objects in 3d are flickering. Seems like some objects visibly are drawn faster, some later and it looks ugly.
So what am I doing wrong? Is there a better way?
The strangest thing is happening. In my onClick(View ...) I have blocks of code, mostly 'if/else' blocks. The sequence is (in pseudo-code, to save you lengthy code):
I have an image that needs setting using imageButton.setImageResource(randomImage); then
I have a MediaPlayer associated with this image that needs playing, then
I place a sleep(1800) here otherwise the MediaPlayer runs into the next MediaPlayer (this works fine)
This image is compare to another image. If its not the same, then another MediaPlayer named 'boo' is played, and the image that should have been displayed in code 1. above, is replaced by 'flag' image. else //if its the right image then
Switch to 'another' image, play it's corresponding MeidaPlayer, Play a 'cheers' MediaPlayer and change image to a 'tick' image, then do some other logic stuff.
But what is happening is that the code 2. is playing, code 3. is happening. code 4. only the 'boo' mediaplayer is happening and I notice the imagebutton flashing. The image that was supposed to be set in code 1. is delayed and is happening only AFTER the 'boo' mediaplayer. The 'flag' image that replaces the first image setting in code 1. is happening but because code 1. is in delay, what is happening is that the image is not changing (although it is, it's just in delay and then it flashes so quickly that it appears not to change). So code 1. is in delay.
Also, if it goes into the else and the right image is clicked, then the image in code 5. DOES switch to 'another', but the corresponding MeidaPlayer doesn't play, the 'cheers' MediaPlayer doesn't play, the 'tick' image doesn't display, and the other logic doesn't occur. Its as if it is ignoring the code.
What's happening here? Is this a multi-threading situation here? Grouping different parts of the code in their own threads and starting them at once only throws exceptions. Any help will be greatly appreciated!
If you want to delay your code, use Handler.postDelayed(Runnable, long). This won't block the UI thread, and will execute when you want it to.
Whitout your code, it's really unclear what are you trying to achive, and how are you trying to achieve it.
Just to explain Adam's idea.
Put this in your Activity class :
final Handler handler = new Handler();
Runnable _rnbl = new Runnable() {
public void run() {
// Your code
}
};
And call it like this
handler.postDelayed(_rnbl , 5000);
I have a set of 10 images, and I want to create an animation where I cross fade between them. I've been looking into built-in Drawable to achieve such a thing, but no luck on that part.
There is the AnimationDrawable, that switch between pictures, but it doesn't animate the switch.
There is the TransitionDrawable, that cross fade between two pictures, but no more than two.
Hell.
I looked for some solution on Google, but no luck on that part. So I'm thinking about implementing my own drawable to achieve such a thing. Would any of you have any pointers ?
Thanks in advance.
Not sure if you found an answer to this, but I had the same problem and ended up building my own class based on TransitionDrawable.
Usage:
CyclicTransitionDrawable ctd = new CyclicTransitionDrawable(new Drawable[] {
drawable1,
drawable2,
drawable3,
...
});
imageView.setImageDrawable(ctd);
ctd.startTransition(1000, 3000) // 1 second transition, 3 second pause between transitions.
The code for the CyclicTransitionDrawable is available on Github.
Well. Long time has passed, and you probably fixed the issue, but you got setEnterFaceDuration() for AnimationDrawable. Example:
mBackgroundAnimation = new AnimationDrawable();
mBackgroundAnimation.addFrame(getResources().getDrawable(R.drawable.background1), 5000);
// ... rest of the frames
mBackgroundAnimation.addFrame(getResources().getDrawable(R.drawable.background6), 5000);
mBackgroundAnimation.setEnterFadeDuration(1000);
mBackgroundAnimation.setOneShot(false);
With this code you have an easy cycling through 1..N images, each one remains 5s (5000ms) with a fade-in animation. Now, what i do is setting the background of my root RelativeLayout
mLayoutRoot.setBackground(mBackgroundAnimation);
mLayoutRoot.post(new AnimationStarterThread());
And the AnimationStarterThread class
private class AnimationStarterThread implements Runnable {
public void run() {
if(mBackgroundAnimation != null)
mBackgroundAnimation.start();
}
}
I've created an application that show around 250 images in ImageView. Images are loaded one after another, 15-30 images per second. Basically the whole thing gives an illusion of a rotating 3D object, at least it should.
The problem is next, app hangs when loading certain images(i.e. I see a few seconds of fluid animation and then animation hangs, jump 10-15 frames(images) ahead and continues. It always happens at the same places in animation cycle.
I though that Android might not have enough resources to handle something like this, so I've resized images to half their size, but it did't help. I've tried buffering images but that did't help either(actually, maybe a little, I think that animation looks a little bit smoother).
And now the weirdest thing. I use the touch screen to allow users to "rotate" the 3D object on those images, and while rotating I again experience those hangs at exactly the same places as with the animation.
All images are in .png format and their size vary from 15kB to 40kB.
I use the following code for the animation:
new Thread(new Runnable() {
#Override
public void run() {
while (!stopStartupAnimation && li < images_360.length) {
final int fli = li;
handler.post(new Runnable() {
#Override
public void run() {
//Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
//imageCanvas.setImageResource(images_360[fli]);
imageCanvas.setImageBitmap(imageStackNext.pop());
System.out.println("rawX = " + fli);
}
});
int ti = fli +25;
if(ti > images_360.length-1){
ti = ti - images_360.length;
}
imageStackNext.push(BitmapFactory.decodeResource(getResources(), images_360[ti]));
synchronized (this) {
try {
wait(1000 / 25);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
li++;
li++;
if (li >= images_360.length) {
li = 0;
}
}
}
}).start();
First, 15-40KB is their compressed form. Uncompressed, as Bitmaps, they are probably substantially larger. 250 of them may be using many MB of RAM, which is not a good idea.
Second, given a choice between using OpenGL for 3D (which is its purpose), or the 2D drawing primitives on the Canvas, or using ImageView, you chose the worst-performing option.
Third, postRunnable() does not take effect immediately, but rather puts things on a message queue for the main application thread to process when it gets a chance. If it gets tied up -- say, handling touch events -- it may well skip over some seemingly redundant ImageView redraws, or have them go by so fast they appear to not happen. All your 40ms wait() does is ensure that you are only raising events every 40ms, not that they will paint every 40ms. Besides, you could have more easily just used postDelayed() for your 40ms timing.
Bitmaps should be loaded efficiently.
Refer example on official page: https://developer.android.com/training/displaying-bitmaps/index.html