106 votes

Superposer deux images dans android pour définir un imageview

Je essaie de superposer deux images dans mon application, mais elles semblent planter à ma ligne canvas.setBitmap(). Qu'est-ce que je fais mal ?

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

0 votes

Peut également utiliser un framelayout, comme ceci : stackoverflow.com/a/11658554/586484

241voto

Daniel Lew Points 39063

Vous pouvez éviter la manipulation complexe de Canvas et faire cela entièrement avec des Drawables, en utilisant LayerDrawable. Vous avez le choix entre deux options : soit le définir en XML puis simplement définir l'image, soit configurer un LayerDrawable dynamiquement en code.

Solution #1 (via XML) :

Créez un nouveau fichier XML Drawable, appelons-le layer.xml:

Maintenant définissez l'image en utilisant ce Drawable :

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

Solution #2 (dynamique) :

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

(Je n'ai pas testé ce code, il peut donc y avoir une erreur, mais cette structure générale devrait fonctionner.)

1 votes

Merci, ça a fonctionné! Une faute de frappe cependant, au cas où quelqu'un d'autre utilise le code: LayerDrawable layers2 = new LayerDrawable(layers); testimage.setImageDrawable(layers2);

1 votes

Formidable moyen d'économiser de l'espace et de réutiliser les images. De plus, vous pouvez utiliser android:left, android:right, android:top et android:down pour contrôler la position de l'une des couches dans le fichier .xml.

0 votes

J'ai utilisé ceci pour dessiner un cercle derrière une image, très belle solution!

10voto

dropsOfJupiter Points 1922

Ok juste pour que vous sachiez, il y a un programme appelé DroidDraw qui peut vous aider à dessiner des objets et à les superposer. J'ai essayé votre solution mais j'avais une animation sous l'image plus petite donc cela n'a pas fonctionné. Mais ensuite j'ai essayé de placer une image dans un layout relatif qui est censé être en dessous en premier et ensuite par dessus j'ai dessiné l'autre image qui est censée se superposer et tout a bien fonctionné. Donc RelativeLayout, DroidDraw et vous êtes prêts :) Simple, pas de tour de passe-passe :) et voici un peu de code pour vous :

Le logo va se trouver au-dessus de l'image de fond de shazam.

7voto

Daniel Nyamasyo Points 981

Créez deux fonctions pour gérer chacune.

Tout d'abord, le canvas est dessiné et les images sont superposées à partir du point (0,0)

Sur le clic d'un bouton

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

Fonction pour créer une superposition.

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

2voto

Haris Qureshi Points 1712

C'est une réponse un peu tardive, mais elle couvre la fusion d'images à partir d'URL en utilisant 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 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();
        }
        // annuler les demandes Picasso
        if (picasso != null && AppUtils.ifNotNullEmpty(picassoRequestTag))
            picasso.cancelTag(picassoRequestTag);
        picasso = null;
        bitmaps = null;
    }

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

        boolean isEven = count % 2 == 0;
        // si la taille des URLs n'est pas paire, rendre MIN_IMAGE_SIZE paire
        MIN_IMAGE_SIZE = DEFAULT_IMAGE_SIZE + (isEven ? count / 2 : (count / 2) + 1);
        // définir MAX_WIDTH et MAX_HEIGHT comme le double de MIN_IMAGE_SIZE
        MAX_WIDTH = MAX_HEIGHT = MIN_IMAGE_SIZE * 2;
        // en cas d'URL impairs augmenter MAX_HEIGHT
        if (!isEven) MAX_HEIGHT = MAX_WIDTH + MIN_IMAGE_SIZE;

        // créer une image par défaut
        Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_wallpaper),
                MIN_IMAGE_SIZE, MIN_IMAGE_SIZE, false);

        // changer la hauteur par défaut (wrap_content) en MAX_HEIGHT
        int height = Math.round(AppUtils.convertDpToPixel(MAX_HEIGHT, context));
        setMinimumHeight(height * 2);

        // Démarrer AsyncTask
        for (int index = 0; index < count; index++) {
            // mettre l'image par défaut comme espace réservé
            bitmaps.put(index, bitmap);
            new PicassoLoadImage(index, imageUrls.get(index)).execute();
            // si vous voulez une exécution parallèle utilisez
            // new PicassoLoadImage(index, imageUrls.get(index)).(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    }

    private class PicassoLoadImage extends AsyncTask {

        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 {
                // appel synchrone à Picasso
                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);

            // créer un 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);

            // si la hauteur et la largeur sont égales, nous avons des images paires
            boolean isEven = MAX_HEIGHT == MAX_WIDTH;
            int imageSize = bitmaps.size();
            int count = imageSize;

            // nous avons des images impaires
            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);
            }
            // si les images ne sont pas paires, définir la largeur de la dernière image sur 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);
            }
            // définir l'image
            setImageBitmap(canvasBitmap);
        }
    }
}

xml

Exemple

List urls = new ArrayList<>();
String picassoTag = null;
// ajoutez vos URLs
((MergeImageView)findViewById(R.id.iv_thumb)).
        createMergedBitmap(MainActivity.this, urls,picassoTag);

2voto

Sadeq Nameni Points 15

Voici ma solution:

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

utilisation :

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

ou

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

et les résultats :

Mode superposition : Mode superposition

Mode écran : Mode écran

0 votes

C'est bien. Mais si vous voulez afficher deux images à côté, quelle est la solution ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X