Überlagern Sie zwei Bilder in Android, um eine Bildansicht festzulegen

100

Ich versuche, zwei Bilder in meiner App zu überlagern, aber sie scheinen in meiner canvas.setBitmap()Zeile abzustürzen . Was mache ich falsch?

private void test() {
    Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.t);
    Bitmap mBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.tt);
    Bitmap bmOverlay = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), mBitmap.getConfig());
    Canvas canvas = new Canvas();
    canvas.setBitmap(bmOverlay);
    canvas.drawBitmap(mBitmap, new Matrix(), null);
    canvas.drawBitmap(mBitmap2, new Matrix(), null);
    testimage.setImageBitmap(bmOverlay);
}
John
quelle
Kann auch ein Framelayout verwenden, wie folgt
Lysogen

Antworten:

234

Sie können die komplexe Canvas-Manipulation überspringen und dies vollständig mit Drawables tun LayerDrawable. Sie haben zwei Möglichkeiten: Sie können es entweder in XML definieren und dann einfach das Image festlegen oder einen LayerDrawabledynamischen Code konfigurieren .

Lösung Nr. 1 (über XML):

Erstellen Sie eine neue Drawable-XML-Datei. Nennen wir sie layer.xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/t" />
    <item android:drawable="@drawable/tt" />
</layer-list>

Stellen Sie nun das Bild mit diesem Drawable ein:

testimage.setImageDrawable(getResources().getDrawable(R.layout.layer));

Lösung 2 (dynamisch):

Resources r = getResources();
Drawable[] layers = new Drawable[2];
layers[0] = r.getDrawable(R.drawable.t);
layers[1] = r.getDrawable(R.drawable.tt);
LayerDrawable layerDrawable = new LayerDrawable(layers);
testimage.setImageDrawable(layerDrawable);

(Ich habe diesen Code nicht getestet, daher liegt möglicherweise ein Fehler vor, aber diese allgemeine Gliederung sollte funktionieren.)

Dan Lew
quelle
1
Danke, es hat funktioniert! Ein Tippfehler, falls jemand anderes den Code verwendet: LayerDrawable layer2 = new LayerDrawable (Ebenen); testage.setImageDrawable (Ebenen2);
John
1
Gute Möglichkeit, Platz zu sparen und Bilder wiederzuverwenden. Sie können auch Android: links, Android: rechts, Android: oben und Android: unten verwenden, um die Position einer der Ebenen in der XML-Datei zu steuern.
Zabawaba99
Ich habe dies verwendet, um eine Kreisform zu zeichnen, die hinter ein Bild gezeichnet werden kann, eine sehr schöne Lösung!
Daniel Wilson
1
Gibt es eine Möglichkeit, die Ebenen [1] allein programmgesteuert auszublenden? Ich möchte eine Bildebene mit Ajax Loader-Ebene oben anzeigen. Nach einiger Zeit möchte ich den Ajax-Lader alleine verstecken. Irgendein Vorschlag?
Harishannam
1
Funktioniert gut für meine Bedürfnisse (ich habe den Code-Ansatz verwendet). Ich habe eine Reihe von "Kacheln", auf die der Benutzer tippt, um in der App zu navigieren. Mit diesem Ansatz konnte ich ein einzelnes Hintergrundbild (allen Kacheln gemeinsam) und zahlreiche Vordergrundbilder (mit transparentem Hintergrund) erstellen, die ich zur Laufzeit laden kann. Hätte mir das LayerDrawable ohne diese Antwort nicht angesehen :-)
DilbertDave
10

OK, nur damit Sie wissen, dass es ein Programm namens DroidDraw gibt. Es kann Ihnen helfen, Objekte zu zeichnen und übereinander zu versuchen. Ich habe Ihre Lösung ausprobiert, aber ich hatte eine Animation unter dem kleineren Bild, sodass dies nicht funktionierte. Aber dann habe ich versucht, ein Bild in einem relativen Layout zu platzieren, das sich vermutlich zuerst darunter befindet, und dann habe ich das andere Bild gezeichnet, das vermutlich überlagert wird, und alles hat großartig funktioniert. Also RelativeLayout, DroidDraw und du kannst loslegen :) Einfach, keine Art von Jiggery Pockery :) und hier ist ein bisschen Code für dich:

Das Logo befindet sich oben auf dem Shazam-Hintergrundbild.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/widget30"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<ImageView
android:id="@+id/widget39"
android:layout_width="219px"
android:layout_height="225px"
android:src="@drawable/shazam_bkgd"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
>
</ImageView>
<ImageView
android:id="@+id/widget37"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/shazam_logo"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
>
</ImageView>
</RelativeLayout>
Tropfen von Jupiter
quelle
6

Sie können den folgenden Code verwenden, um das Problem zu lösen, oder die Demo hier herunterladen

Erstellen Sie jeweils zwei Funktionen.

Zuerst wird die Leinwand gezeichnet und die Bilder werden ab Punkt (0,0) übereinander gezeichnet.

Klicken Sie auf die Schaltfläche

public void buttonMerge(View view) {

        Bitmap bigImage = BitmapFactory.decodeResource(getResources(), R.drawable.img1);
        Bitmap smallImage = BitmapFactory.decodeResource(getResources(), R.drawable.img2);
        Bitmap mergedImages = createSingleImageFromMultipleImages(bigImage, smallImage);

        img.setImageBitmap(mergedImages);
    }

Funktion zum Erstellen eines Overlays.

private Bitmap createSingleImageFromMultipleImages(Bitmap firstImage, Bitmap secondImage){

    Bitmap result = Bitmap.createBitmap(firstImage.getWidth(), firstImage.getHeight(), firstImage.getConfig());
    Canvas canvas = new Canvas(result);
    canvas.drawBitmap(firstImage, 0f, 0f, null);
    canvas.drawBitmap(secondImage, 10, 10, null);
    return result;
}

Weiterlesen

Daniel Nyamasyo
quelle
2

Es ist eine etwas späte Antwort, aber es behandelt das Zusammenführen von Bildern aus URLs mit Picasso

MergeImageView

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;

import java.io.IOException;
import java.util.List;

public class MergeImageView extends ImageView {

    private SparseArray<Bitmap> bitmaps = new SparseArray<>();
    private Picasso picasso;
    private final int DEFAULT_IMAGE_SIZE = 50;
    private int MIN_IMAGE_SIZE = DEFAULT_IMAGE_SIZE;
    private int MAX_WIDTH = DEFAULT_IMAGE_SIZE * 2, MAX_HEIGHT = DEFAULT_IMAGE_SIZE * 2;
    private String picassoRequestTag = null;

    public MergeImageView(Context context) {
        super(context);
    }

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

    public MergeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public MergeImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean isInEditMode() {
        return true;
    }

    public void clearResources() {
        if (bitmaps != null) {
            for (int i = 0; i < bitmaps.size(); i++)
                bitmaps.get(i).recycle();
            bitmaps.clear();
        }
        // cancel picasso requests
        if (picasso != null && AppUtils.ifNotNullEmpty(picassoRequestTag))
            picasso.cancelTag(picassoRequestTag);
        picasso = null;
        bitmaps = null;
    }

    public void createMergedBitmap(Context context, List<String> imageUrls, String picassoTag) {
        picasso = Picasso.with(context);
        int count = imageUrls.size();
        picassoRequestTag = picassoTag;

        boolean isEven = count % 2 == 0;
        // if url size are not even make MIN_IMAGE_SIZE even
        MIN_IMAGE_SIZE = DEFAULT_IMAGE_SIZE + (isEven ? count / 2 : (count / 2) + 1);
        // set MAX_WIDTH and MAX_HEIGHT to twice of MIN_IMAGE_SIZE
        MAX_WIDTH = MAX_HEIGHT = MIN_IMAGE_SIZE * 2;
        // in case of odd urls increase MAX_HEIGHT
        if (!isEven) MAX_HEIGHT = MAX_WIDTH + MIN_IMAGE_SIZE;

        // create default bitmap
        Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_wallpaper),
                MIN_IMAGE_SIZE, MIN_IMAGE_SIZE, false);

        // change default height (wrap_content) to MAX_HEIGHT
        int height = Math.round(AppUtils.convertDpToPixel(MAX_HEIGHT, context));
        setMinimumHeight(height * 2);

        // start AsyncTask
        for (int index = 0; index < count; index++) {
            // put default bitmap as a place holder
            bitmaps.put(index, bitmap);
            new PicassoLoadImage(index, imageUrls.get(index)).execute();
            // if you want parallel execution use
            // new PicassoLoadImage(index, imageUrls.get(index)).(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    }

    private class PicassoLoadImage extends AsyncTask<String, Void, Bitmap> {

        private int index = 0;
        private String url;

        PicassoLoadImage(int index, String url) {
            this.index = index;
            this.url = url;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                // synchronous picasso call
                return picasso.load(url).resize(MIN_IMAGE_SIZE, MIN_IMAGE_SIZE).tag(picassoRequestTag).get();
            } catch (IOException e) {
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap output) {
            super.onPostExecute(output);
            if (output != null)
                bitmaps.put(index, output);

            // create canvas
            Bitmap.Config conf = Bitmap.Config.RGB_565;
            Bitmap canvasBitmap = Bitmap.createBitmap(MAX_WIDTH, MAX_HEIGHT, conf);
            Canvas canvas = new Canvas(canvasBitmap);
            canvas.drawColor(Color.WHITE);

            // if height and width are equal we have even images
            boolean isEven = MAX_HEIGHT == MAX_WIDTH;
            int imageSize = bitmaps.size();
            int count = imageSize;

            // we have odd images
            if (!isEven) count = imageSize - 1;
            for (int i = 0; i < count; i++) {
                Bitmap bitmap = bitmaps.get(i);
                canvas.drawBitmap(bitmap, bitmap.getWidth() * (i % 2), bitmap.getHeight() * (i / 2), null);
            }
            // if images are not even set last image width to MAX_WIDTH
            if (!isEven) {
                Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmaps.get(count), MAX_WIDTH, MIN_IMAGE_SIZE, false);
                canvas.drawBitmap(scaledBitmap, scaledBitmap.getWidth() * (count % 2), scaledBitmap.getHeight() * (count / 2), null);
            }
            // set bitmap
            setImageBitmap(canvasBitmap);
        }
    }
}

xml

<com.example.MergeImageView
    android:id="@+id/iv_thumb"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Beispiel

List<String> urls = new ArrayList<>();
String picassoTag = null;
// add your urls
((MergeImageView)findViewById(R.id.iv_thumb)).
        createMergedBitmap(MainActivity.this, urls,picassoTag);
Haris Qureshi
quelle
1

Das ist meine Lösung:

    public Bitmap Blend(Bitmap topImage1, Bitmap bottomImage1, PorterDuff.Mode Type) {

        Bitmap workingBitmap = Bitmap.createBitmap(topImage1);
        Bitmap topImage = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);

        Bitmap workingBitmap2 = Bitmap.createBitmap(bottomImage1);
        Bitmap bottomImage = workingBitmap2.copy(Bitmap.Config.ARGB_8888, true);

        Rect dest = new Rect(0, 0, bottomImage.getWidth(), bottomImage.getHeight());
        new BitmapFactory.Options().inPreferredConfig = Bitmap.Config.ARGB_8888;
        bottomImage.setHasAlpha(true);
        Canvas canvas = new Canvas(bottomImage);
        Paint paint = new Paint();

        paint.setXfermode(new PorterDuffXfermode(Type));

        paint.setFilterBitmap(true);
        canvas.drawBitmap(topImage, null, dest, paint);
        return bottomImage;
    }

Verwendung :

imageView.setImageBitmap(Blend(topBitmap, bottomBitmap, PorterDuff.Mode.SCREEN));

oder

imageView.setImageBitmap(Blend(topBitmap, bottomBitmap, PorterDuff.Mode.OVERLAY));

und die Ergebnisse:

Überlagerungsmodus: Überlagerungsmodus

Bildschirmmodus: Bildschirmmodus

Sadeq Nameni
quelle
Das ist gut. Aber wenn Sie zwei Bilder in der Nähe anzeigen möchten, was ist die Lösung?
Maniaq