Wie kann man mehrere Ebenen von ImageViews verwalten und ihr Seitenverhältnis basierend auf dem größten beibehalten?

Lesezeit: 10 Minuten

Wie kann man mehrere Ebenen von ImageViews verwalten und ihr
Android-Entwickler

Angenommen, ich habe mehrere Bilder, die ich übereinander legen muss, bei einigen wird möglicherweise eine Art Animation angezeigt und bei einigen können sie sogar gezogen werden.

Das größte, das normalerweise den gesamten Bildschirm einnimmt, wäre das untere Ende der Z-Koordinate (nennen wir es backgroundImageView ), während alle anderen darüber (und über anderen) erscheinen.

so:

  • backgroundImageView
  • imageView1 , zentriert.
  • imageView2 , bei 60%,60% der oberen linken Ecke

Wenn ich ein FrameLayout verwende (was die beste Lösung zu sein scheint), würde die Größe von backgroundImageView gut passen, aber wie kann ich die Größe der anderen Ebenen entsprechend ändern?

Ich denke, ich muss irgendwie herausfinden, wo ich die anderen Ebenen platzieren und wie ich ihre Größe einstellen kann.

Der einfache Weg besteht darin, sicherzustellen, dass alle Ebenen genau dieselbe Größe haben, aber das könnte viel Speicherplatz beanspruchen und beim Animieren oder Ziehen von Ansichten sehr langsam werden. Es wäre eine große Verschwendung, wenn einige der Schichten einen sehr kleinen Inhalt hätten.

Wie kann man mehrere Ebenen von ImageViews verwalten und ihr
pskink

Dies ist eine Klasse, die ein Bild mit zusätzlichen Ebenen anzeigt:

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;
        }
    }
}

und wie es verwendet werden kann:

    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 sieht so aus:

<?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>

mit folgenden Bildern:

hintergrund.png: Geben Sie hier die Bildbeschreibung ein

layer0.png: Geben Sie hier die Bildbeschreibung ein

Schicht1.png: Geben Sie hier die Bildbeschreibung ein

Das Ergebnis ist: Geben Sie hier die Bildbeschreibung ein

WICHTIG Um zu verhindern, dass Ressourcen beim Laden aus verschiedenen drawable-*-Ordnern automatisch skaliert werden, müssen Sie das Resources-Objekt verwenden, das von der LayeredImageView.getResources()-Methode erhalten wurde

habe Spaß!

  • Dies ist in der Tat ein sehr gutes Beispiel. Ich frage mich jedoch, wie ich entscheide, um wie viel die Ebenen verschoben (oder gedreht oder so) werden sollen? hängt es nur von der größe der bitmap ab oder muss ich auch die dichte des bildschirms im vergleich zur bilddatei berücksichtigen?

    – Android-Entwickler

    28. Mai ’13 um 21:49 Uhr

  • meinst du: m.preTranslate(62, 63)? diese Werte sind Pixel, siehe background.png, Pixel (62, 63) ist ein Punkt, an dem der linke obere Rand einer Ebene gezeichnet wird

    – Pskink

    28. Mai 13 um 21:53 Uhr

  • Ich denke auch, dass die Ansicht eine Nullzeiger-Ausnahme hat, wenn ich ihr kein Bild setze.

    – Android-Entwickler

    28. Mai 13 um 21:53 Uhr

  • Zu den Positionen meinte ich, wie entscheide ich mich für die Werte. Angenommen, das Bild ist 200 x 200 und der Hintergrund 400 x 400, und ich möchte die Ebene in die Mitte verschieben. Ist es immer in Ordnung, preTranslate (100,100) zu verwenden? oder muss ich andere faktoren berücksichtigen?

    – Android-Entwickler

    28. Mai ’13 um 21:54 Uhr


  • Wenn Ihr background.png 200×200 und layer.png 40×40 ist, müssen Sie 80, 80 übersetzen, um es in der Mitte zu platzieren, egal wie groß Ihr Bildschirm ist (Telefon, Tablet erc).

    – Pskink

    28. Mai ’13 um 22:01 Uhr

Wie kann man mehrere Ebenen von ImageViews verwalten und ihr
pskink

Erweitern Sie einfach ImageView und überschreiben Sie die onDraw-Methode, um zusätzliche Ebenen zu zeichnen

Dies ist eine minimale Variante (erweiterte Version mit Animationen ist in der zweiten Antwort):

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();
    }
}

und wie es in Ihrer Aktivität verwendet wird:

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

hier hast du 3 bilder:

hintergrund.png Geben Sie hier die Bildbeschreibung ein

layer0.png Geben Sie hier die Bildbeschreibung ein

Schicht1.png Geben Sie hier die Bildbeschreibung ein

und drei oben kombiniert Geben Sie hier die Bildbeschreibung ein

und schließlich der aufgenommene Bildschirm vom Emulator Geben Sie hier die Bildbeschreibung ein

  • Dies ist auch eine mögliche Lösung, aber wie würden Sie dann mit Animationen verschiedener Ebenen umgehen, ziehen, alle Bilder im richtigen Seitenverhältnis zeichnen, …

    – Android-Entwickler

    24. Mai ’13 um 8:37 Uhr


  • siehe ImageView.getImageMatrix()

    – Pskink

    24. Mai ’13 um 8:42 Uhr

  • Ich verstehe nicht, was es ist (Link hier: developer.android.com/reference/android/widget/… ) und wie es helfen kann .

    – Android-Entwickler

    24. Mai ’13 um 8:45 Uhr


  • Sie können diese Matrix verwenden, wenn Sie zusätzliche Bitmaps mit dem gleichen Skalierungs-/Seitenverhältnis zeichnen, mit dem Ihre Hintergrund-Birmap gezeichnet wurde

    – Pskink

    24. Mai ’13 um 8:53 Uhr

  • kannst du bitte einen Code zeigen? Ich habe so etwas noch nie gemacht. Außerdem, wie würde ich einige der Bilder dort mit einer Animation versehen oder Touch-Ereignisse zum Ziehen nehmen?

    – Android-Entwickler

    24. Mai ’13 um 9:22 Uhr

.

811220cookie-checkWie kann man mehrere Ebenen von ImageViews verwalten und ihr Seitenverhältnis basierend auf dem größten beibehalten?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy