Background image with moving foreground graphics - android

I would like to know what the best way would be to draw a background image (this would be pretty much static) and a foreground object which would move depending on user input in Android?
Sort of like a side-scroller game (when the foregroudn object needs to animate and change a lot more than the background).
TIA

You could set the background image as main view with setContentView. For drawing foreground image You could use custom class that extends View and do drawing in it "onDraw" method. Something like this:
class ForegroundImage extends View
{
public Foreground(Context ctx) {}
public void onDraw(Canvas c)
{
//here You draw image anything You want on canvas
}
}
ImageView lBackgroundImage = (ImageView)findViewById(R.id.BackgroundView);
setContentView(lBackgrounImage);
ForegroundImage lForegroundImage = new ForegroundImage(this);
addContentView (lForegroundImage);
I hope this helped

Related

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 ?

What would be a better way to create Android animation?

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);
...

ImageView animations in live wallpaper

(Android)
I am working on adapting a full-screen analog clock app to a live wallpaper.
The app use three separate ImageView for hour, min, sec hands and RotateAnimation.
I have been looking around for a method to use ImageView in live wallpaper. this and this indicates that this should be possible with measure() and layout(), but I
don't really know how to use it.
For example, I use the code below to load clock_background.png into a ImageView.
public class MyWallpaperService extends WallpaperService {
#Override
public Engine onCreateEngine() {
return new MyWallpaperEngine();
}
....
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
mContext = getBaseContext();
backgroundImage = new ImageView(mContext);
backgroundImage.setImageDrawable(mContext.getResources().getDrawable(R.drawable.clock_background));
}
}
Later I have:
backgroundImage.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
backgroundImage.layout(0, 0, 150, 150);
I expect the code above would display clock_background.png in the upper-left corner of
the screen. I tried to put the two lines above in OnSurfaceCreated and OnSurfaceChanged. Unfortunately it does not work.
As I understand, live wallpaper gives a Surface (not SurfaceView), from which one can get
canvas and draw things on it.
I apologise for being very new at Android: Am I doing it all wrong? I just don't want to
mess with drawBitmap or likes, and I don't care about battery anyway.
Any help is highly appreciated. Thank you very much!
I short, yes you're doing it wrong.
Use the supplied canvas and its drawX() methods

How does Android's setDrawingCacheEnabled() work?

I have a UI where the root layout is a RelativeLayout. It has a number of Views, such as fields, buttons, etc.
There are also two other panels that are initially invisible. When the user clicks a button, one of these panels slides in from the left, another slides in from the bottom. The problem is the frame rate is pathetic on a Nexus S.
I want to use setDrawingCacheEnabled(true) in order to (I hope) speed up the animation of these two panels. As a simplified example, here is roughly how I slide in one of these panels. I'll call it "detailPanel".
public class MyActivity extends Activity {
// Initially invisible.
private View detailPanel;
// A simple slide animation.
private Animation detailEnterAnimation;
...
public void someButtonClicked(View view) {
detailPanel.startAnimation(detailEnterAnimation);
detailPanel.setVisibility(View.VISIBLE);
detailPanel.setDrawingCacheEnabled(true);
}
}
Now in the DetailPanel I try to use the cache. I was hoping that bitmap rendering via the cache would improve performance:
public class DetailPanel extends LinearLayout {
public void draw(Canvas canvas) {
Bitmap cache = getDrawingCache();
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, null);
} else {
super.draw(canvas);
}
}
}
The Bitmap is non-null and is the right size, but it is completely black.
Why is my bitmap black? That code may be obscenely wrong; I've tried a million different things and just cannot figure it out.
From Android documentation (http://developer.android.com/reference/android/view/View.html#setDrawingCacheEnabled%28boolean%29):
Enabling the drawing cache is similar to setting a layer when hardware acceleration is turned off. When hardware acceleration is turned on, enabling the drawing cache has no effect on rendering because the system uses a different mechanism for acceleration which ignores the flag. If you want to use a Bitmap for the view, even when hardware acceleration is enabled, see setLayerType(int, android.graphics.Paint) for information on how to enable software and hardware layers.
Maybe this is your case?
You may want to build your drawing cache before using it, otherwise It will give you a blank bitmap
public void someButtonClicked(View view) {
detailPanel.startAnimation(detailEnterAnimation);
detailPanel.setVisibility(View.VISIBLE);
detailPanel.setDrawingCacheEnabled(true);
detailPanel.buildDrawingCache(); //may be you need to add this?
}

android gallery view "stutters" with deferred image loading adapter

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.

Categories

Resources