วางซ้อนภาพสองภาพใน Android เพื่อตั้งค่ามุมมองภาพ


100

ฉันพยายามวางซ้อนภาพสองภาพในแอปของฉัน แต่ดูเหมือนว่าจะขัดข้องที่canvas.setBitmap()เส้นของฉัน ผมทำอะไรผิดหรือเปล่า?

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

สามารถใช้ framelayout ได้เช่นกันดังนี้ stackoverflow.com/a/11658554/586484
Lysogen

คำตอบ:


235

คุณสามารถข้ามการจัดการผ้าใบที่ซับซ้อนและทำเช่นนี้อย่างสิ้นเชิงกับ Drawables LayerDrawableใช้ คุณมีหนึ่งในสองทางเลือก: คุณสามารถกำหนดเป็น XMLจากนั้นตั้งค่ารูปภาพหรือคุณสามารถกำหนดค่าไฟล์LayerDrawableโค้ดแบบไดนามิกก็ได้

โซลูชัน # 1 (ผ่าน XML):

สร้างไฟล์ XML Drawable ใหม่เรียกว่า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>

ตอนนี้ตั้งค่าภาพโดยใช้ Drawable:

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

โซลูชัน # 2 (ไดนามิก):

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

(ฉันยังไม่ได้ทดสอบโค้ดนี้ดังนั้นอาจมีข้อผิดพลาด แต่โครงร่างทั่วไปนี้ควรใช้งานได้)


1
ขอบคุณมันได้ผล! พิมพ์ผิดหนึ่งครั้งในกรณีที่มีคนอื่นใช้รหัส: LayerDrawable Layer2 = LayerDrawable ใหม่ (เลเยอร์); testimage.setImageDrawable (เลเยอร์ 2);
จอห์น

1
วิธีที่ยอดเยี่ยมในการประหยัดพื้นที่และนำรูปภาพมาใช้ซ้ำ นอกจากนี้คุณสามารถใช้ android: left, android: right, android: top และ android: down เพื่อควบคุมตำแหน่งของเลเยอร์ใดเลเยอร์หนึ่งในไฟล์. xml
zabawaba99

ฉันใช้สิ่งนี้เพื่อวาดรูปวงกลมที่วาดได้ด้านหลังภาพเป็นทางออกที่ดีมาก!
Daniel Wilson

1
มีวิธีซ่อนเลเยอร์ [1] เพียงอย่างเดียวโดยทางโปรแกรมหรือไม่? ฉันต้องการแสดงเลเยอร์รูปภาพที่มีเลเยอร์ตัวโหลด ajax อยู่ด้านบน หลังจากเวลาผ่านไปฉันต้องการซ่อนตัวโหลด ajax เพียงอย่างเดียว ข้อเสนอแนะใด ๆ ?
harishannam

1
ทำงานได้ดีสำหรับความต้องการของฉัน (ฉันใช้วิธีการโค้ด) ฉันมี 'กระเบื้อง' จำนวนหนึ่งที่ผู้ใช้แตะเพื่อนำทางไปรอบ ๆ แอป ด้วยวิธีนี้ฉันสามารถมีภาพพื้นหลังเดียว (ทั่วไปสำหรับไทล์ทั้งหมด) และภาพเบื้องหน้าจำนวนมาก (พร้อมพื้นหลังโปร่งใส) ซึ่งฉันสามารถโหลดได้ที่รันไทม์ คงไม่ได้ดู LayerDrawable ถ้าไม่มีคำตอบนี้ :-)
DilbertDave

10

ตกลงเพื่อให้คุณรู้ว่ามีโปรแกรมที่เรียกว่า DroidDraw สามารถช่วยคุณวาดวัตถุและลองใช้วัตถุอื่น ๆ ฉันลองใช้วิธีแก้ปัญหาของคุณแล้ว แต่ฉันมีภาพเคลื่อนไหวใต้ภาพที่เล็กกว่าจึงไม่ได้ผล แต่แล้วฉันก็พยายามวางภาพหนึ่งในเลย์เอาต์แบบสัมพัทธ์ที่สมมติว่าอยู่ข้างใต้ก่อนจากนั้นฉันก็วาดภาพอื่นที่คิดว่าจะซ้อนทับและทุกอย่างก็ทำงานได้ดี ดังนั้น RelativeLayout, DroidDraw และคุณพร้อมที่จะไป :) ง่าย ๆ ไม่มี pockery ตลก ๆ :) และนี่คือรหัสเล็กน้อยสำหรับคุณ:

โลโก้จะอยู่ด้านบนของภาพพื้นหลัง shazam

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

6

คุณสามารถใช้โค้ดด้านล่างเพื่อแก้ปัญหาหรือดาวน์โหลดการสาธิตได้ที่นี่

สร้างสองฟังก์ชันเพื่อจัดการแต่ละฟังก์ชัน

ขั้นแรกให้วาดผืนผ้าใบและภาพจะวาดทับกันจากจุด (0,0)

เมื่อคลิกปุ่ม

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

ฟังก์ชั่นสร้างภาพซ้อนทับ

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

อ่านเพิ่มเติม


2

คำตอบที่ช้าไปหน่อย แต่ครอบคลุมการรวมรูปภาพจาก URL โดยใช้ 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" />

ตัวอย่าง

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

1

นี่คือทางออกของฉัน:

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

การใช้งาน:

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

หรือ

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

และผลลัพธ์:

โหมดซ้อนทับ: โหมดซ้อนทับ

โหมดหน้าจอ: โหมดหน้าจอ


ดี. แต่ถ้าต้องการแสดงภาพสองภาพใกล้ ๆ วิธีแก้คืออะไร?
maniaq
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.