I have a card a game in which the person chooses the card and drags (done through translate animation) and then when let go it moves to the center of the screen where have an imageView place holder. At the animationEnd I set the image view to that card and can invalidate on each view on the screen. What I dont understand is that when I call invalidate then the card looks like it left some trails (shadows from the translate animation). When I call postInvalidate, it gets drawn properly. Why is that??
AnimationEnd code:
#Override
public void onAnimationEnd(Animation arg0) {
if(card == null){
MyApplication.SWERR(null);
busy = false;
}
card.getCardImage().setVisibility(View.INVISIBLE);
Drawable drawable = card.getCardSuitNumber().getDrawable(mContext);
finalDestIV.setBackgroundDrawable(drawable);
finalDestIV.setVisibility(View.VISIBLE);
invalidateAll();
PlayerStatus p = mGameMonitor.getPlayerStatusFromPlayerEnum(GameConstants.PLAYERS.P1);
p.handleCardChosen(card);
cardReleased = false;
p.setPlayerLock(false);
card = null;
}
InvalidateAll method
private void invalidateAll(){
rootLayout.postInvalidate();
for (int i=0; i<rootLayout.getChildCount();i++){
View v = rootLayout.getChildAt(i);
v.postInvalidate();
}
}
Typical problem of novice Android developers.
Please look into the description of the invalidate method.
According to the Google documentation:
Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().
So, I suppose onAnimationEnd is calling from non-UI thread (probably animation thread).
I think if you avoid the API policy it may lead to unexpected behavior and may differs between different Android versions.
Calling invalidate from non-UI thread can interrupt some UI operations (or brake some logic) in Android system which makes some misbehavior.
Look also here: What does postInvalidate() do?
Related
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 ?
I have a ViewPager which I need to move as a whole on button press. I use an animation for this.
When I press it, I translate the 'x' for it. I use setFillAfter(true) to keep the new position.
But when I change the page of the ViewPager, it jumps back to the original x-position!
I only saw this issue on Android 4.1, with Android 4.0 there is no problem! So it looks like some kind of regression in Android.
I attached a testproject where I could reproduce the issue without all my other stuff around it. I think it is best if you want to help me figure this out to import the project in your Eclipse and see it for yourself.
I also added to video's, one on my HTC One X where I see the issue, and the other on a tablet with Android 4.0, where the issue is not there.
I have been desperately looking to fix this ugly side effect, but no luck till now...
(Sorry for the big movie files...)
Video of Android 4.0 without the side effect
Video Android 4.1 with the side effect
the project where you can reproduce the issue with
Edit:
I added the following:
#Override
public void onAnimationEnd(Animation animation) {
RelativeLayout.LayoutParams lp = (android.widget.RelativeLayout.LayoutParams) myViewPager.getLayoutParams();
if (!i)
lp.setMargins(300,0,0,0);
else
lp.setMargins(0,0,0,0);
myViewPager.setLayoutParams(lp);
}
After that it stays at the correct position, but it 'flickers' quickly, like the animation is still showing at the end and when I change the margin, it still shows the offset it had after animation. Then it jumps to the correct position.
The main problem seems to be incorrect choice of animation type. You see, View Animation as a tool is not intended to be used with complex interactive objects like ViewPager. It offers only low-cost animation of the drawing place of views. The visual behaivior of the animated ViewPager in response to user-actions is undefined and should not be relied on.
Ugly flicks, when you substitute a "gost" with the real object are only natural.
The mechanism, that is intended to use in your case since API 11 is specialized property animator built in Views for optimized performance: ViewPropertyAnimator, or not specialized, but more versatile ObjectAnimator and AnimatorSet.
Property animation makes the View to really change its place and function normally there.
To make project, to use, say, ViewPropertyAnimator, change your listener setting to this:
btn.setOnClickListener(new OnClickListener() {
boolean b = false;
#Override
public void onClick(View v) {
if(b) {
myViewPager.animate().translationX(0f).setDuration(700);
}
else {
myViewPager.animate().translationX(300f).setDuration(700);
}
b=!b;
}
});
If you want to use xml configuration only, stick to |ObjectAnimator and AnimatorSet. Read through the above link for further information.
In case, you are anxious to support pre-Honeycomb devices, you can use Jake Warton's NineOldAndroids project. Hope that helps.
That's because the Animation's setFillAfter(true) doesn't actually change the position or any attributes of the View; all it does is create a Bitmap of the view's drawing cache and leaves it where the animation ends. Once the screen is invalidated again (ie. changing the page in the ViewPager), the bitmap will be removed and it will appear as if the View is returning to it's original position, when in fact it was already there.
If you want the View to retain it's position after the animation has finished, you need to actually adjust the View's LayoutParams to match your desired effect. To achieve this, you can override the onAnimationEnd method of the Animation, and adjust the LayoutParams of the View inside there.
Once you adjust the LayoutParams, you can remove your call to setFillAfter(true) and your View will actually stay where you expect it to.
Regarding the flicker issue:
I have encountered this issue before, and it stems from the possibility of the onAnimationEnd() call not syncing up with the next layout pass. Animation works by applying a transformation to a View, drawing it relative to its current position.
However, it is possible for a View to be rendered after you have moved it in your onAnimationEnd() method. In this case, the Animation's transformation is still being applied correctly, but the Animation thinks the View has not changed its original position, which means it will be drawn relative to its ENDING position instead of its STARTING position.
My solution was to create a custom subclass of Animation and add a method, changeYOffset(int change), which modifies the y translation that is applied during the Animation's applyTransformation method. I call this new method in my View's onLayout() method, and pass the new y-offset.
Here is some of my code from my Animation, MenuAnimation:
/**
* Signal to this animation that a layout pass has caused the View on which this animation is
* running to have its "top" coordinate changed.
*
* #param change
* the difference in pixels
*/
public void changeYOffset(int change) {
fromY -= change;
toY -= change;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float reverseTime = 1f - interpolatedTime;
float dy = (interpolatedTime * toY) + (reverseTime * fromY);
float alpha = (interpolatedTime * toAlpha) + (reverseTime * fromAlpha);
if (alpha > 1f) {
alpha = 1f;
}
else if (alpha < 0f) {
alpha = 0f;
}
t.setAlpha(alpha);
t.getMatrix().setTranslate(0f, dy);
}
And from the View class:
private int lastTop;
// ...
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// the animation is expecting that its View will not be moved by the container
// during its time period. if this does happen, we need to inform it of the change.
Animation anim = getAnimation();
if (anim != null && anim instanceof MenuAnimation) {
MenuAnimation animation = (MenuAnimation) anim;
animation.changeYOffset(top - lastTop);
}
// ...
lastTop = top;
super.onLayout(changed, left, top, right, bottom);
}
Crucero has it right about setFillAfter not adjusting params post invalidation. When the view is re-layed out (which'll happen the pass after it's invalidated), its layout params will be the ones that always applied, so it should go back to the original position.
And Jschools is right about onAnimationEnd. Strongly encourage you to step through the source code with a debugger, where you'll instructively discover that an update is made that affects the drawn position of the view after onAnimationEnd is fired, at which point you've actually applied the layout params, hence the flicker caused by doubled up offset.
But this can be solved quite simply by making sure you relayout at the right time. You want to put your re-positioning logic at the end of the ui message queue at the time of animation end so that it is polled after the animation but before laying out. There's nowhere that suggests doing this, annoyingly, but I've yet find a reason in any release of the SDK reason why (when doing this just once and not incorrectly using ui thread) this shouldn't work.
Also clear the animation due to another issue we found on some older devices.
So, try:
#Override
public void onAnimationEnd(final Animation animation) {
myViewPager.post(new Runnable() {
#Override
public public void run() {
final RelativeLayout.LayoutParams lp = (android.widget.RelativeLayout.LayoutParams) myViewPager.getLayoutParams();
if (!someBooleanIPresume)
lp.setMargins(300,0,0,0);
else
lp.setMargins(0,0,0,0);
myViewPager.setLayoutParams(lp);
myViewPager.clearAnimation();
}
}
I'm trying to make flying balloons (up and down, infinitely) and by clicking on one of them, it will changes its direction. I tried to use Animation, but it don't support click on a view at a current position at a current time. I know, that there are two methods, for at least:
use an OpenGL
and use a Canvas.
I don't want to use OpenGL for so quit simple animation. And I don't understand how to do it, using Canvas(were read this, google docs and some google search links).
I don't need a code, all I need it's the way how to do it and the algorithm.
Alright, this is what I've set up for one of my projects that would work exactly as you need...
You need to create 4 main aspects:
- An Activity (extend Activity)
- A SurfaceView (extend SurfaceView implement SurfaceHolder.Callback)
- A Thread (Extend Thread)
- Objects that interact (extend Drawable)
The Activity starts and generates a SurfaceView that creates a Thread. The View or the Thread can contain the objects that you draw/interact with, its up to you how you want to store the data at this point but its easiest if you come up with a design that has the least coupling between classes.
Once started, the Thread runs constantly until stopped (setting isRunning to false):
/**
* Processing loop
*/
public void run() {
while (isRunning) {
try {
sleep(25);
} catch (InterruptedException e) {
Log.e("Thread","Thread Interruption");
e.printStackTrace();
}
update();
}
Log.d("Thread", "Game Loop Stopped");
}
/**
* Main processing of the game loop.
*/
private void update() {
handleInput();
updatePhysics();
updateUI();
updateState();
updateAI();
updateAnimations();
updateSound();
updateVideo();
}
In this class you see the update() method that does a bunch of actions each iteration of the thread.
The methods that will interest you mostly are the handleInput, updateUI and updatePhysics methods.
In the handleInput you'll do a callback to the SurfaceView to get the data that you need for processing information. My handleInput is fairly simple in that it just grabs the MotionEvent from the SurfaceView class's onTouchEvent (this gets implemented when you use the SurfaceHolder.OnCallBack interface).
The updateUI method calls the SurfaceView's update where it re-draws the Canvas with each object, or balloon
public void update(List<Actor> actors) {
Canvas canvas = null;
try {
canvas = surface.lockCanvas();
synchronized (surface) {
//Blank the Canvas
for (int i = 0; i < actors.size(); i++) {
actors.get(i).draw(canvas);
}
}
} catch (NullPointerException e) {
e.printStackTrace();
} finally {
if (canvas != null) {
surface.unlockCanvasAndPost(canvas);
}
}
}
Here we can see that it calls the draw method passing the canvas object to each one of your interactable objects. I've called mine "Actors" in the code above. How you choose to implement draw is up to you but it sounds like you'll be using a lot of canvas.drawBitmap() functions.
Going back to the Thread's updatePhysics method you'll want to use it to update the objects before or after they're drawn to the screen. For you you'll want to have them change their X and/or Y positions on the screen or change the direction its moving after the handleInput decides you've hit a balloon.
Its very hard to explain this well in a StackOverflow reply. If you really want I can try to put something more informative together this weekend and post it externally for you (I can provide my UML class diagrams among other information such as picture examples, structures and data flows.
Please let me know if I can help you further! Please note that this isn't all my work. I used a lot of tutorials online for basic Android Game structure but don't have the links available right now to provide references.
I would like to create an deferred loading adapter for use with a Gallery widget.
That is to say getView() returns an ImageView immediately, and later some other mechanism will asynchronously call its setImageBitmap() method. I did this by creating a "lazy" ImageView that extends ImageView.
public class GalleryImageView extends ImageView {
// ... other stuff here ...
public void setImage(final Looper looper, final int position) {
final Uri uri = looper.get(position);
final String path = looper.sharePath(position);
new Thread(new Runnable() {
#Override
public void run() {
GalleryBitmap gbmp = new GalleryBitmap(context, uri, path);
final Bitmap bmp = gbmp.getBitmap(); // all the work is here
handler.post(new Runnable() {
#Override
public void run() {
if (GalleryImageView.this.getTag().equals(uri)) {
setImageBitmap(bmp);
}
}
});
}
}).start();
}
}
When I scroll slowly in the Gallery, the center image keeps popping into the center. It's hard to explain, exactly, but it's really annoying. I also tried the same approach for a spinner adapter and it works perfectly there.
Any ideas?
The solution is to implement a more intelligent method of when to fetch thumbnails - it is pointless fetching thumbnails while the user is flinging through the list. Essentially you want something like that implemented in Romain Guy's Shelves application.
To get the most responsive Gallery you'll need to implement some form of in-memory cache and do the following:
Only set an image if it exists in the in-memory cache from your getView. Set a flag indicating whether the image was set or whether a download is required. You could also maintain a memory in a cache on the SD card and internal memory, and if a fling is not currently ongoing then show a low res (inSampleSize set to 16 or 8) version which will be visible when just scrolling through - the high res version will load when the user lets go and settles on an image.
Add an OnItemSelectedListener (and make sure to call setCallbackDuringFling(false) when initializing) that downloads new thumbnails for all the visible items that require a download only if the users finger is up (you can use getFirstVisiblePosition and getLastVisiblePosition to find the range of views visible)
Also when the user lifts their finger check to see 1. if the selected position changed since the user put their finger down and if so 2. whether a download was initiated due to your OnItemSelectedListener - if it wasn't then initiate one. This is to catch the case where no flinging occurs, and thus OnItemSelected never does anything because it is always called with the finger down in this situation. I'd use a Handler to delay starting the downloading by the animation time of your gallery (make sure to clear any delayed messages posted to this handler whenever onItemSelected is called or when you get an ACTION_DOWN event.
After an image is downloaded check if any visible views requested this image then and update those views
Also be aware that the default Gallery component does not properly implement View recycling (it assumes each position in the adapter has a unique view, and also clears the recycler of these items when they go offscreen making it pretty pointless). Edit: on more looking it isn't pointless - but it's not a recycler in terms of next/previous views, rather it serves to avoid having to call getView for the current views during layout changes.
This means the convertView parameter passed to your getView method will more often that not be null, meaning you'll be inflating a lot of views (which is expensive) - see my answer to Does a replacement for Gallery with View recycling exist? for some hints on that. (PS: I have since modified that code - I would use a different recycle bin for layout phases and scroll phases, in the layout phase place and retrieve the views in the layout recycle bin according to their position, and DO NOT call getView if the view you get from the bin is non-null since it will be exactly the same view; also clear the layout recycle bin after the layout phase -- this makes things a bit more snappier)
PS: Also be very careful with what you do in OnItemSelected - namely unless it's in the places mentioned above then try to do as little as possible. For instance I was setting some text in a TextView above my Gallery in OnItemSelected. Just moving this call into the same points as where I updated thumbnails made a noticable difference.
I have an answer for you!
When any of the setImage... methods are called on ImageView in internally a layout pass is requested, for example, setImageBitmap() as above is defined as such
public void setImageBitmap(Bitmap bm) {
setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}
which calls
public void setImageDrawable(Drawable drawable) {
if (mDrawable != drawable) {
mResource = 0;
mUri = null;
updateDrawable(drawable);
requestLayout(); //layout requested here!
invalidate();
}
}
which has the effect of the gallery 'snapping' to the center of the image thats currently closest to the center of the gallery.
What I have done to prevent this is have the View thats loading into the Gallery have a explicit height and width (in dips) and using an ImageView subclass that ignores layout requests. This works as the gallery still has a layout pass initially but does not bother doing this every time an image in the gallery changes, which I imagine would only need to happen if the gallery views had their width and height set to WRAP_CONTENT, which we dont. Note that as invalidate() is still called in setImageDrawable() the image will still be drawn when set.
My very simple ImageView subclass below!
/**
* This class is useful when loading images (say via a url or file cache) into
* ImageView that are contained in dynamic views (Gallerys and ListViews for
* example) The width and height should be set explicitly instead of using
* wrap_content as any wrapping of content will not be triggered by the image
* drawable or bitmap being set (which is normal behaviour for an ImageView)
*
*/
public class ImageViewNoLayoutRefresh extends ImageView
{
public ImageViewNoLayoutRefresh(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public ImageViewNoLayoutRefresh(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ImageViewNoLayoutRefresh(Context context)
{
super(context);
}
#Override
public void requestLayout()
{
// do nothing - for this to work well this image view should have its dims
// set explicitly
}
}
edit: i should mention that the onItemSelected approaches can also work, but as I needed to hook into that while flinging was taking place I came up with the above, which I think is more flexible approach
This might be a bug in the Gallery's onLayout method. Check out http://code.google.com/p/android/issues/detail?id=16171 for a possible workaround.
I'm just trying to figure out the best approach for running a scolling background on an android device. The method I have so far.... its pretty laggy. I use threads, which I believe is not the best bet for android platforms
#Override
public void run() {
// Game Loop
while(runningThread){
//Scroll background down
bgY += 1;
try {
this.postInvalidate();
t.sleep(10);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
where postinvalidate in the onDraw function simply pushings the background image down
canvas.drawBitmap(backgroundImage, bgX, bgY, null);
Thanks in advance
UPDATE
I've identified the problem. And it is the fact that my player updates the same rate as the background scrolls (making it look choppy). from top to bottom. This is because both get drawn in the same function. I'm not really sure how to tackle this and would be grateful for any help. i.e so that player movement is handled separately from the map scrolling
Also how can I control the speed at which onDraw(canvas) get called?
Thanks in advance.
However, I have patched together a different run loop for anyone having the same problem. This is partially from the jetboy example on google.
Below is my inner class in my surfaceview
class MapThread extends Thread{
private Map map;
private SurfaceHolder holder;
private boolean run = false;
public MapThread(Map map, SurfaceHolder holder){
this.holder = holder;
this.map = map;
setRunning(true);
}
public void setRunning(boolean run){
this.run = run;
}
#Override
public void run(){
while(run){
try{
Canvas c = null;
try {
c = holder.lockCanvas(null);
synchronized (holder) {
map.onDraw(c);
}
} finally {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
}
}
SOLUTION
https://gamedev.stackexchange.com/questions/8127/android-game-scrolling-background
Use the SurfaceView implementation draw on the screen. It allows you more control of what to draw and when.
The SurfaceView is a special subclass of View that offers a dedicated drawing surface within the View hierarchy. The aim is to offer this drawing surface to an application's secondary thread, so that the application isn't required to wait until the system's View hierarchy is ready to draw.
The basic design is to have a surfaceview that draws continuously in a while loop. Then add an if-statement whose condition is to be true if a timer thread tells you its time to draw. Say, every 30ms, draw the bitmap. This will give you about 33 fps.
Now you may also have another timer thread that tells you when to update the the bgX or bgY values. Say at every 60ms, it will set a boolean updateFlag = true; Then in your main thread, you have an if-statement check this flag, set it to false, and update your bgX and bgY values. By accurately controlling the timer and the bgX/bgY increments, you should be able to produce smooth animations.
It would be a good idea to look at the LunarLander source code provided by Google.
One thing to keep in mind is that sleep is very inaccurate. To work around this, you can keep track of exactly how much time passed during the sleep and update how much you move things accordingly.
Its not clear from you code, but you need to make sure that all of your UI updates happen in the UI thread.
You do need to do your timing outside of the UI thread, because otherwise the UI will never update. There are other methods of timing, like using a Handler that can be a little bit cleaner, but I think the overhead on them might be a bit much for what you are trying to do. I think a simple thread has the least amount of overhead.
I am using this method on the second level of my SpaceQuestAlpha game. This makes a seemless scroll.
I used the 2 lines below to set original position.
moony=0;
moon2y=-(heighty);
Then these lines increment both versions of the background image. One starts at 0 and one starts at negative screen height. Every time one of the images goes below the bottom of the screen it is moved up twice the height to move it back into position. I am using surface view with no latency issues.
moony+=5;
moon2y+=5;
if(moon2y>=heighty) {moon2y=moon2y-(heighty*2);}
canvas.drawBitmap(lavabackground, 0, moon2y, null);
if(moony>=heighty){moony=moony-(heighty*2);}
canvas.drawBitmap(lavabackground, 0, moony, null);