5

Suppose I have multiple Images that I need to put one on top of the other, some might have some kind of animation appearing and some might even be draggable.

the largest one which usually takes the whole screen would be the bottom in the Z-coordinate (let's call it the backgroundImageView ), while all of the rest appear on top of it (and on top of others).

like so:

  • backgroundImageView
  • imageView1 , centered.
  • imageView2 , at 60%,60% of the top left corner
  • ...

If I use a FrameLayout (which seems like the best solution), the backgroundImageView would have its size fit nicely, but how can I force the other layers resize themselves accordingly?

I think I need to somehow get where to put the other layers and how to set their sizes.

The easy way is to make sure all layers have the exact same size, but that could take a lot of memory and become very slow when animating or dragging views. It would be a huge waste if some of the layers have a very small content.

android developer
  • 114,585
  • 152
  • 739
  • 1,270

2 Answers2

17

this is a class that displays an image with additional layers:

import java.util.ArrayList;
import java.util.Iterator;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
import android.widget.ImageView;

public class LayeredImageView extends ImageView {
    private final static String TAG = "LayeredImageView";

    private ArrayList<Layer> mLayers;
    private Matrix mDrawMatrix;

    private Resources mResources;

    public LayeredImageView(Context context) {
        super(context);
        init();
    }

    public LayeredImageView(Context context, AttributeSet set) {
        super(context, set);
        init();

        int[] attrs = {
                android.R.attr.src
        };
        TypedArray a = context.obtainStyledAttributes(set, attrs);
        TypedValue outValue = new TypedValue();
        if (a.getValue(0, outValue)) {
            setImageResource(outValue.resourceId);
        }
        a.recycle();
    }

    private void init() {
        mLayers = new ArrayList<Layer>();
        mDrawMatrix = new Matrix();
        mResources = new LayeredImageViewResources();
    }

    @Override
    protected boolean verifyDrawable(Drawable dr) {
        for (int i = 0; i < mLayers.size(); i++) {
            Layer layer = mLayers.get(i);
            if (layer.drawable == dr) {
                return true;
            }
        }
        return super.verifyDrawable(dr);
    }

    @Override
    public void invalidateDrawable(Drawable dr) {
        if (verifyDrawable(dr)) {
            invalidate();
        } else {
            super.invalidateDrawable(dr);
        }
    }

    @Override
    public Resources getResources() {
        return mResources;
    }

    @Override
    public void setImageBitmap(Bitmap bm) throws RuntimeException {
        String detailMessage = "setImageBitmap not supported, use: setImageDrawable() " +
                "or setImageResource()";
        throw new RuntimeException(detailMessage);
    }

    @Override
    public void setImageURI(Uri uri) throws RuntimeException {
        String detailMessage = "setImageURI not supported, use: setImageDrawable() " +
                "or setImageResource()";
        throw new RuntimeException(detailMessage);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Matrix matrix = getImageMatrix();
        if (matrix != null) {
            int numLayers = mLayers.size();
            boolean pendingAnimations = false;
            for (int i = 0; i < numLayers; i++) {
                mDrawMatrix.set(matrix);
                Layer layer = mLayers.get(i);
                if (layer.matrix != null) {
                    mDrawMatrix.preConcat(layer.matrix);
                }
                if (layer.animation == null) {
                    draw(canvas, layer.drawable, mDrawMatrix, 255);
                } else {
                    Animation a = layer.animation;
                    if (!a.isInitialized()) {
                        Rect bounds = layer.drawable.getBounds();
                        Drawable parentDrawable = getDrawable();
                        if (parentDrawable != null) {
                            Rect parentBounds = parentDrawable.getBounds();
                            a.initialize(bounds.width(), bounds.height(), parentBounds.width(), parentBounds.height());
                        } else {
                            a.initialize(bounds.width(), bounds.height(), 0, 0);
                        }
                    }
                    long currentTime = AnimationUtils.currentAnimationTimeMillis();
                    boolean running = a.getTransformation(currentTime, layer.transformation);
                    if (running) {
                        // animation is running: draw animation frame
                        Matrix animationFrameMatrix = layer.transformation.getMatrix();
                        mDrawMatrix.preConcat(animationFrameMatrix);

                        int alpha = (int) (255 * layer.transformation.getAlpha());
//    Log.d(TAG, "onDraw ********** [" + i + "], alpha: " + alpha + ", matrix: " + animationFrameMatrix);
                        draw(canvas, layer.drawable, mDrawMatrix, alpha);
                        pendingAnimations = true;
                    } else {
                        // animation ended: set it to null
                        layer.animation = null;
                        draw(canvas, layer.drawable, mDrawMatrix, 255);
                    }
                }
            }
            if (pendingAnimations) {
                // invalidate if any pending animations
                invalidate();
            }
        }
    }

    private void draw(Canvas canvas, Drawable drawable, Matrix matrix, int alpha) {
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.concat(matrix);
        drawable.setAlpha(alpha);
        drawable.draw(canvas);
        canvas.restore();
    }

    public Layer addLayer(Drawable d, Matrix m) {
        Layer layer = new Layer(d, m);
        mLayers.add(layer);
        invalidate();
        return layer;
    }

    public Layer addLayer(Drawable d) {
        return addLayer(d, null);
    }

    public Layer addLayer(int idx, Drawable d, Matrix m) {
        Layer layer = new Layer(d, m);
        mLayers.add(idx, layer);
        invalidate();
        return layer;
    }

    public Layer addLayer(int idx, Drawable d) {
        return addLayer(idx, d, null);
    }

    public void removeLayer(Layer layer) {
        layer.valid = false;
        mLayers.remove(layer);
    }

    public void removeAllLayers() {
        Iterator<Layer> iter = mLayers.iterator();
        while (iter.hasNext()) {
            LayeredImageView.Layer layer = iter.next();
            layer.valid = false;
            iter.remove();
        }
        invalidate();
    }

    public int getLayersSize() {
        return mLayers.size();
    }

    public class Layer {
        private Drawable drawable;
        private Animation animation;
        private Transformation transformation;
        private Matrix matrix;
        private boolean valid;

        private Layer(Drawable d, Matrix m) {
            drawable = d;
            transformation = new Transformation();
            matrix = m;
            valid = true;
            Rect bounds = d.getBounds();
            if (bounds.isEmpty()) {
                if (d instanceof BitmapDrawable) {
                    int right = d.getIntrinsicWidth();
                    int bottom = d.getIntrinsicHeight();
                    d.setBounds(0, 0, right, bottom);
                } else {
                    String detailMessage = "drawable bounds are empty, use d.setBounds()";
                    throw new RuntimeException(detailMessage);
                }
            }
            d.setCallback(LayeredImageView.this);
        }

        public void startLayerAnimation(Animation a) throws RuntimeException {
            if (!valid) {
                String detailMessage = "this layer has already been removed";
                throw new RuntimeException(detailMessage);
            }
            transformation.clear();
            animation = a;
            if (a != null) {
                a.start();
            }
            invalidate();
        }

        public void stopLayerAnimation(int idx) throws RuntimeException {
            if (!valid) {
                String detailMessage = "this layer has already been removed";
                throw new RuntimeException(detailMessage);
            }
            if (animation != null) {
                animation = null;
                invalidate();
            }
        }
    }

    private class LayeredImageViewResources extends Resources {

        public LayeredImageViewResources() {
            super(getContext().getAssets(), new DisplayMetrics(), null);
        }

        @Override
        public Drawable getDrawable(int id) throws NotFoundException {
            Drawable d = super.getDrawable(id);
            if (d instanceof BitmapDrawable) {
                BitmapDrawable bd = (BitmapDrawable) d;
                bd.getBitmap().setDensity(DisplayMetrics.DENSITY_DEFAULT);
                bd.setTargetDensity(DisplayMetrics.DENSITY_DEFAULT);
            }
            return d;
        }
    }
}

and how it can be used:

    final LayeredImageView v = new LayeredImageView(this);
    Resources res = v.getResources();

    v.setImageResource(R.drawable.background);

    Matrix m;

    m = new Matrix();
    m.preTranslate(81, 146); // pixels to offset
    final Layer layer1 = v.addLayer(res.getDrawable(R.drawable.layer1), m);

    m = new Matrix();
    m.preTranslate(62, 63); // pixels to offset
    final Layer layer0 = v.addLayer(0, res.getDrawable(R.drawable.layer0), m);


    final AnimationDrawable ad = new AnimationDrawable();
    ad.setOneShot(false);
    Drawable frame1, frame2;
    frame1 = res.getDrawable(R.drawable.layer0);
    frame2 = res.getDrawable(R.drawable.layer1);
    ad.addFrame(frame1, 3000);
    ad.addFrame(frame2, 1000);
    ad.addFrame(frame1, 250);
    ad.addFrame(frame2, 250);
    ad.addFrame(frame1, 250);
    ad.addFrame(frame2, 250);
    ad.addFrame(frame1, 250);
    ad.addFrame(frame2, 250);
    ad.setBounds(200, 20, 300, 120);
    v.addLayer(1, ad);
    v.post(new Runnable() {
        @Override
        public void run() {
            ad.start();
        }
    });

    int[] colors = {
            0xeeffffff,
            0xee0038a8,
            0xeece1126,
    };
    GradientDrawable gd = new GradientDrawable(Orientation.TOP_BOTTOM, colors);
    gd.setBounds(0, 0, 100, 129);
    gd.setCornerRadius(20);
    gd.setStroke(5, 0xaa666666);
    final Matrix mm = new Matrix();
    mm.preTranslate(200, 69); // pixels to offset
    mm.preRotate(20, 50, 64.5f);
    final Layer layer2 = v.addLayer(2, gd, mm);

    final Animation as = AnimationUtils.loadAnimation(this, R.anim.anim_set);

    final Runnable action1 = new Runnable() {
        @Override
        public void run() {
            Animation a;
            Interpolator i;

            i = new Interpolator() {
                @Override
                public float getInterpolation(float input) {
                    return (float) Math.sin(input * Math.PI);
                }
            };
            as.setInterpolator(i);
            layer0.startLayerAnimation(as);

            a = new TranslateAnimation(0, 0, 0, 100);
            a.setDuration(3000);
            i = new Interpolator() {
                @Override
                public float getInterpolation(float input) {
                    float output = (float) Math.sin(Math.pow(input, 2.5f) * 12 * Math.PI);
                    return (1-input) * output;
                }
            };
            a.setInterpolator(i);
            layer1.startLayerAnimation(a);

            a = new AlphaAnimation(0, 1);
            i = new Interpolator() {
                @Override
                public float getInterpolation(float input) {
                    return (float) (1 - Math.sin(input * Math.PI));
                }
            };
            a.setInterpolator(i);
            a.setDuration(2000);
            layer2.startLayerAnimation(a);
        }
    };
    OnClickListener l1 = new OnClickListener() {
        @Override
        public void onClick(View view) {
            action1.run();
        }
    };
    v.setOnClickListener(l1);
    v.postDelayed(action1, 2000);

//    final float[] values = new float[9];
//    final float[] pts = new float[2];
//    final Matrix inverse = new Matrix();;
//    OnTouchListener l = new OnTouchListener() {
//        @Override
//        public boolean onTouch(View view, MotionEvent event) {
//            int action = event.getAction();
//            if (action != MotionEvent.ACTION_UP) {
//                if (inverse.isIdentity()) {
//                    v.getImageMatrix().invert(inverse);
//                    Log.d(TAG, "onTouch set inverse");
//                }
//                pts[0] = event.getX();
//                pts[1] = event.getY();
//                inverse.mapPoints(pts);
//
//                mm.getValues(values);
//                // gd's bounds are (0, 0, 100, 129);
//                values[Matrix.MTRANS_X] = pts[0] - 100 / 2;
//                values[Matrix.MTRANS_Y] = pts[1] - 129 / 2;
//                mm.setValues(values);
//                v.invalidate();
//            }
//            return false;
//        }
//    };
//    v.setOnTouchListener(l);
    setContentView(v);

anim_set.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
>
    <rotate
        android:fromDegrees="0"
        android:toDegrees="30"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="2500"
    />
    <scale
        android:fromXScale="1"
        android:toXScale="1.8"
        android:fromYScale="1"
        android:toYScale="1.8"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="2500"
    />
</set>

with the following images:

background.png: enter image description here

layer0.png: enter image description here

layer1.png: enter image description here

the result is: enter image description here

IMPORTANT in order to prevent resources from auto OS scaling when loading from different drawable-* folders you have to use Resources object obtained from LayeredImageView.getResources() method

have fun!

pskink
  • 23,874
  • 6
  • 66
  • 77
  • This is indeed a very good sample. i wonder though, how do i decide by how much to move the layers (or rotate, or anything) ? does it depend only on the size of the bitmap or do i have to consider the density of the screen too compared to the image file as well? – android developer May 28 '13 at 21:49
  • you mean: m.preTranslate(62, 63)? those values are pixels, see background.png, pixel (62, 63) is a point where left-top edge of a layer will be drawn – pskink May 28 '13 at 21:53
  • i also think the view has a null pointer exception when i don't set it an image. – android developer May 28 '13 at 21:53
  • about the positions, i meant how do i decide on the values. suppose the image is 200x200 and the background is 400x400, and i wish to move the layer to the center, will it always be ok to use preTranslate(100,100) ? or do i have to consider other factors? – android developer May 28 '13 at 21:54
  • if your background.png is 200x200 and layer.png is 40x40 you have to translate 80, 80 to place it on center no matter how big is your screen (phone, tablet erc) – pskink May 28 '13 at 22:01
  • i asked about the density and not the screen size, but i guess you mean that it should work in any case. wonder though how to handle cases where there is no background , and i need to set the size based on the surroundings . i think your code is perfect to answer the original question. – android developer May 28 '13 at 22:06
  • i've tried now to make 2 layers of images that show in the center of the view, and keep rotating. it works great and i don't think it has frame skips. – android developer May 28 '13 at 22:22
  • besides the bug, this works so great you deserve a "V" and a "+" . thank you for making it ! I would even appreciate it more (but can't give you more) if you fix possible bugs. – android developer May 28 '13 at 22:25
  • didn't test the bug fix, but i have to say you are awesome ! thank you. – android developer May 28 '13 at 22:35
  • i also asked about catching events on the layers, but i think it should be fine for now. – android developer May 28 '13 at 23:10
  • can i ask you some more questions? does it auto-pause/stop the animation when going to another activity/app ? does it respect the limitations of the surrounding views ? can you put a link to where you got the matrix&animations from... ? – android developer May 29 '13 at 06:41
  • ad 1) yes/no yes because when you go to another activity/app onDraw is not called but if you return long animation continues – pskink May 29 '13 at 07:05
  • ad 2) i hope so (didnt check it though) ad 3) i had to ask uncle Google/read ViewGroup code, last remark for layout translations to work images have to be placed in drawable-nodpi folder, otherwise OS scales them and coordinates have to be scaled accordingly – pskink May 29 '13 at 07:10
  • 1) i don't understand. you mean the animation will resume as the activity resumes, and pause when it pauses ? 2) thanks. 3) in which parts of it? I think that just like many classes of android, it's probably a very large file. 4) so all images should be put in the nodpi folder? but then how can i use WRAP_CONTENT on the view? or maybe you meant all of the images that are not the "src" attribute of the view ? – android developer May 29 '13 at 07:20
  • 1) yes 3) its just one method: drawChild() 4) ok you can place them in drawable-mdpi but if your device is ldpi you have scale m.preTranslate coords by 120/160 since OS scaled them by this factor – pskink May 29 '13 at 08:03
  • hi, 4) you can now place resources everywhere: no more auto OS scaling, see note at the bottom of the answer, also now uses more powerful Drawables instead of Bitmaps – pskink May 31 '13 at 08:27
  • 1) so it's a good thing. 4) yes, but i could also just put dimensions values instead, no? also, do you mean you've updated the code and now you support any drawable ? – android developer May 31 '13 at 08:56
  • yes indeed you could use dimensions, but now you dont have since there is no OS scaling, yes any static Drawable suporrted – pskink May 31 '13 at 09:05
  • 4) i don't get it. suppose i wish to set the size of a layer to be half the size of its containing LayeredImageView (which its size is bound to its surroundings) it, should i set it to be half the bitmap size (if it's in the mdpi folder) ? would it be in pixels or in dp ? what about location ? if i wanted to put the layer at the center or one of the corners? won't i need to check the size of the image as it is shown on the LayeredImageView ? – android developer May 31 '13 at 11:32
  • now if yiu have back.png 300px x 200px located anywhere in your drawable-* folder and icon.png 20px x 20px also located anywhere and if you want to put it centered you have to translate it by (300-20)/2, (200-20)/2, no matter how big is target LayeredImageView - the same for full screen or for preview with dimensions like 32 x 32 – pskink May 31 '13 at 12:05
  • i see. but what if i don't use a background (in the src attribute)? would i need to use the largest layer that is being used? in fact, if i use wrap_content for the width&height, and only use layers, would it use the largest one as the size of the view ? or maybe a combination because some layers might move away? – android developer May 31 '13 at 20:20
  • hmm, good question, but i think this simple code assumes that views bounds are determined by background image, while layers are only additions/decorators to the background – pskink Jun 01 '13 at 10:55
  • well what if i set the "src" attribute to a color instead of an image ? i would need to check the size of the view itself in this case , right? – android developer Jun 01 '13 at 10:57
  • you mean android:src="#ff0" ? i am even not sure if its allowed since it refers to sizeless drawable... – pskink Jun 01 '13 at 11:04
  • it allows you to use it: http://developer.android.com/reference/android/widget/ImageView.html#attr_android:src . even if it didn't you can always use @color/your_color . what does it mean in case you use wrap_content , now that's a different question... – android developer Jun 01 '13 at 12:30
  • @pskink I have been trying to catch events on a layer. I tried the following but it did not work. I changed the class Layer to extend from ImageButton. I then tried setting an onclick listener on the ImageButton. I knew I was trying my luck but I thought it could save me some headache in trying to work out what was touched. I will have many layers you see and did not want to go through each one. Is what I tried crazy ? How do you check for events ? – Ryan Heitner Oct 06 '13 at 08:20
  • so you want to add not Drawables but ordinary Views? – pskink Oct 06 '13 at 08:34
  • Yes That is what I want ! Should I just change all the drawables to View ? – Ryan Heitner Oct 06 '13 at 09:00
  • in this case you said your. base class is a FrameLayout so you need addView() to that FrameLayout using its LayoutParameters – pskink Oct 06 '13 at 09:27
  • Thanks I will try see what I can do, but it does not sound like I will be able to place them precisely. – Ryan Heitner Oct 06 '13 at 09:43
  • Hi so far it is going great. I would like to point out a small bug in your code. I was doing remove layer and it was crashing with index out of bounds. I discovered it was because you calculate numLayers outside the loop in onDraw, but the remove can occur when it is already in the loop. See the change needed below. Sorry about the formatting int numLayers = mLayers.size(); for (int i = 0; i < numLayers; i++) { for (int i = 0; i < mLayers.size(); i++) { – Ryan Heitner Oct 09 '13 at 15:30
  • I wanted to ask you the reason that fillAfter does not work for animations, is this something which could be corrected ? I have been moving my image manually to get around this. – Ryan Heitner Oct 13 '13 at 14:08
  • filling is not supported, also no support for animatio repeating nor animation listeners, but imho it should not be hard to add them – pskink Oct 13 '13 at 14:35
  • I have discovered another bug this was difficult to find, it was causing me a problem where Android started using the wrong configuration and pulling resources from the wrong folders. Change super(getContext().getAssets(), new DisplayMetrics(), null); TO super(getContext().getAssets(), new DisplayMetrics(), getContext().getResources().getConfiguration()); The Null was the problem. – Ryan Heitner Nov 12 '13 at 15:30
  • @Ramkiran a little enhanced version is here: https://github.com/pskink/LayeredImageView – pskink Nov 29 '13 at 07:12
4

just extend ImageView and override its onDraw method in order to draw additional layers

this is a minimal varsion (enhanced version with animations is in the second answer):

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;

public class LayeredImageView extends ImageView {
    private final static String TAG = "LayeredImageView";

    ArrayList<Bitmap> mLayers;

    public LayeredImageView(Context context) {
        super(context);
        init();
    }

    public LayeredImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mLayers = new ArrayList<Bitmap>();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Matrix matrix = getImageMatrix();
        if (matrix != null) {
            int numLayers = mLayers.size();
            for (int i = 0; i < numLayers; i++) {
                Bitmap b = mLayers.get(i);
                canvas.drawBitmap(b, matrix, null);
            }
        }
    }

    public void addLayer(Bitmap b) {
        mLayers.add(b);
        invalidate();
    }

    public void addLayer(int idx, Bitmap b) {
        mLayers.add(idx, b);
        invalidate();
    }

    public void removeLayer(int idx) {
        mLayers.remove(idx);
    }

    public void removeAllLayers() {
        mLayers.clear();
        invalidate();
    }

    public int getLayersSize() {
        return mLayers.size();
    }
}

and how its used in your Activity:

LayeredImageView v = new LayeredImageView(this);
v.setImageResource(R.drawable.background);
Resources res = getResources();
v.addLayer(BitmapFactory.decodeResource(res, R.drawable.layer0));
v.addLayer(0, BitmapFactory.decodeResource(res, R.drawable.layer1));
setContentView(v);

here you have 3 images:

background.png enter image description here

layer0.png enter image description here

layer1.png enter image description here

and three above combined enter image description here

and finally the captured screen from the emulator enter image description here

pskink
  • 23,874
  • 6
  • 66
  • 77
  • this is also a possible solution, but then how would you handle animations of different layers, dragging, drawing of all images in the correct aspect ratio,... – android developer May 24 '13 at 08:37
  • see ImageView.getImageMatrix() – pskink May 24 '13 at 08:42
  • i don't understand what it is (link here: http://developer.android.com/reference/android/widget/ImageView.html#getImageMatrix() ) and how it can help . – android developer May 24 '13 at 08:45
  • you can use this Matrix when drawing additional Bitmaps wirh the same scale/aspect ratio as your background Birmap was drawn – pskink May 24 '13 at 08:53
  • can you please show some code? i've never done such a thing. also, how would i make some of the images there have an animation or take touch events for dragging them? – android developer May 24 '13 at 09:22
  • override onDraw, call super impl, get image matrix, call canvas.setMatrix(), draw additional Bitmaps. for dragging you should overrude onTouch method but i cannot see how you want to select which layer you want to drag... – pskink May 24 '13 at 09:49
  • but what about animations? and how does it fix the aspect ratio issues? – android developer May 24 '13 at 10:13
  • what kind of animations do you want? aspect ratio will be preserved when. using original Matrix – pskink May 24 '13 at 10:26
  • any kind of animation that is provided by android, for example translate animation. i can't set it to the imageView since it will take all of the layers instead of the one i wish to set it to. – android developer May 24 '13 at 13:01
  • you can set animarions on layer level using http://developer.android.com/reference/android/view/animation/Transformation.html, so for example each layer could have its own animation – pskink May 24 '13 at 13:14
  • i'm quite a newb at this. can you please post a sample or point to a sample website that shows how to do it? – android developer May 24 '13 at 20:17
  • ok, i made a simple version of working code, see my updated answer – pskink May 25 '13 at 09:03
  • how would you use your code to put images at a certain position (for example the center, or as i've written, at a proportional location) ? also, how would you use it for animating the layers ? – android developer May 25 '13 at 09:43
  • everything is Matrix, Matrix is everywhere :), see docs about Matrix. and learn what you can do with it – pskink May 25 '13 at 09:54
  • the API link you've given doesn't show how to use it. all animations examples work on a view and not on a bitmap, no? – android developer May 25 '13 at 10:08
  • see sources of ViewGroup.java, find method "protected boolean drawChild" and see how they draw a child during animation phase, there should be a line: "final Animation a = child.getAnimation();", see what they do if a != null – pskink May 25 '13 at 10:26
  • wait, are you saying that the parents of the views are responsible for their animations? – android developer May 25 '13 at 13:23
  • sure, check ViewGroup.java – pskink May 25 '13 at 13:37
  • was hoping I'd have a clearer knowledge of how to do it. guess i will have to read more about it. – android developer May 25 '13 at 16:34
  • it should be as easy as 10-20 additional lines of code, the most important is Animation.getTransformation() – pskink May 25 '13 at 17:17
  • as i thought, it took ~20 lines of code to support per layer animations – pskink May 26 '13 at 07:36
  • as your problem interested me a bit i wrote more robust implementation, see my other answer – pskink May 28 '13 at 20:05
  • what other answer? also, i really appreciate your help, even though i might not return to this question (because i was told at the office to let it be and move to another task ) – android developer May 28 '13 at 20:09
  • sorry i had to re-submit new answer due to network issues, see it now – pskink May 28 '13 at 20:33