如何使用Bundle在Android活动之间传递图像(位图)?

57

假设我有一个活动从图库中选择图像并将其作为位图检索,就像这个例子:here

现在,我想将此位图传递给另一个活动中的ImageView使用。 我知道可以在活动之间传递bundle,但是我应该如何将此位图存储到bundle中?

还是我应该采取另一种方法?

10个回答

56

我非常推荐采用不同的方法。

如果你真的想这样做,虽然有可能,但需要大量的内存并且速度也很慢。如果你的手机比较老并且位图比较大,它可能无法正常工作。例如,你可以将它作为额外项传递,intent.putExtra("data", bitmap)。Bitmap实现了Parcelable接口,因此你可以将它放入额外项中。同样,Bundle也有putParcelable

如果你想在活动之间传递它,我建议你将它存储在文件中。这更有效率,也更少工作。你可以使用MODE_PRIVATE在数据文件夹中创建私有文件,这些文件对其他应用程序不可访问。


5
如果我只是将图像路径传递到另一个活动中,会怎样? - damonkashu
2
完全正确!我以为Bitmap是由您的活动临时创建的,这就是为什么您必须将其传递的原因。(如果您确实是这样创建的,您可能希望将其保存到本地文件夹中,然后重新加载它)。 - EboMike
2
顺便说一句,关于它的不好之处,我是指全尺寸图像。如果您有较小的位图,则将它们作为Parcelable传递肯定没问题。 - EboMike
我可能最终会这样做,因为我想复制联系人图像选择器,选择一张图片并裁剪它。但我会在另一个问题中发布,谢谢! - damonkashu
帮忙一下..谢谢 - Hoo
显示剩余3条评论

45

如果您将它作为Parcelable传递,那么您就有可能会得到一个JAVA BINDER FAILURE错误。所以,解决方案是:如果位图比较小,比如说缩略图,把它作为字节数组传递,并在下一个活动中构建位图进行显示。例如:

在您的调用活动中...

Intent i = new Intent(this, NextActivity.class);
Bitmap b; // your bitmap
ByteArrayOutputStream bs = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 50, bs);
i.putExtra("byteArray", bs.toByteArray());
startActivity(i);

...并在您的接收活动中

if(getIntent().hasExtra("byteArray")) {
    ImageView previewThumbnail = new ImageView(this);
    Bitmap b = BitmapFactory.decodeByteArray(
        getIntent().getByteArrayExtra("byteArray"),0,getIntent().getByteArrayExtra("byteArray").length);        
    previewThumbnail.setImageBitmap(b);
}

8
您能解释一下这如何帮助避免 Java Binder 失败吗? - Cigogne Eveillée
42
如果位图不是非常小的话? - clocksmith
1
不知道位图的大小就不要通过 Binder 传递它。你很可能会遇到 Binder 失败。 - Jorge
1
有人知道位图必须有多大才能出现“内存不足”错误吗? - SoftDesigner
4
当位图较小的时候,我会使用Parcelable。但当在bundle中传递超过1MB的数据时就会出问题。 - sziraqui
显示剩余4条评论

24

如@EboMike所建议,我将位图保存在名为myImage的文件中,该文件存储在我的应用程序内部存储中,其他应用程序无法访问。这是该部分的代码:

如@EboMike所建議,我將位圖保存在名為myImage的檔案中,該檔案存儲在我的應用程式內部存儲中,其他應用程式無法訪問。這是該部分的代碼:

public String createImageFromBitmap(Bitmap bitmap) {
    String fileName = "myImage";//no .png or .jpg needed
    try {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
        FileOutputStream fo = openFileOutput(fileName, Context.MODE_PRIVATE);
        fo.write(bytes.toByteArray());
        // remember close file output
        fo.close();
    } catch (Exception e) {
        e.printStackTrace();
        fileName = null;
    }
    return fileName;
}

接下来,在下一个活动中,您可以使用以下代码将文件 myImage 解码为位图:

然后在下一个活动中,您可以使用以下代码将文件 myImage 解码为位图:

Bitmap bitmap = BitmapFactory.decodeStream(context
                    .openFileInput("myImage"));//here context can be anything like getActivity() for fragment, this or MainActivity.this

注意:省略了很多检查 null 和缩放位图的步骤。


我需要在之后删除这个文件吗? - alfo888_ibg
2
@alfo888_ibg 我认为你不需要删除图片,因为每次使用该函数时,上一个图片都会被覆盖。但是如果你想的话,可以删除它。 - Illegal Argument
1
最好删除它。 - Sakiboy
这需要 android.permissions.WRITE_EXTERNAL_STORAGE 权限吗? - Alan Nelson
1
@AlanNelson 如果您将图像保存在应用程序的私有存储中,则无需获得许可。如果您将其保存在公共文件夹(如图库)中,则需要获得许可。 - Illegal Argument

9

活动

在活动之间传递位图

Intent intent = new Intent(this, Activity.class);
intent.putExtra("bitmap", bitmap);

在Activity类中

Bitmap bitmap = getIntent().getParcelableExtra("bitmap");

片段

在片段之间传递位图

SecondFragment fragment = new SecondFragment();
Bundle bundle = new Bundle();
bundle.putParcelable("bitmap", bitmap);
fragment.setArguments(bundle);

接收来自SecondFragment内部的消息

Bitmap bitmap = getArguments().getParcelable("bitmap");

传输大位图(压缩位图)

如果你遇到绑定器事务失败,这说明你正在通过从一个活动传输大元素来超出绑定器事务缓冲区。

在这种情况下,你需要将位图压缩为字节数组,然后在另一个活动中解压缩它,就像这样:

在第一个活动中:

Intent intent = new Intent(this, SecondActivity.class);

ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPG, 100, stream);
byte[] bytes = stream.toByteArray(); 
intent.putExtra("bitmapbytes",bytes);

在SecondActivity中:
byte[] bytes = getIntent().getByteArrayExtra("bitmapbytes");
Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

以上代码应该在FirstActivity的哪里使用?它应该放在init中还是作为一个单独的方法(并在SecondActivity中调用此方法)? - nish

2

2

在first.java文件中

Intent i = new Intent(this, second.class);
                    i.putExtra("uri",uri);
                    startActivity(i);

在 second.java 中

Bundle bd = getIntent().getExtras();
        Uri uri = bd.getParcelable("uri");
        Log.e("URI", uri.toString());
        try {
            Bitmap bitmap = Media.getBitmap(this.getContentResolver(), uri);
            imageView.setImageBitmap(bitmap);

        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

imageShow是imageView的对象吗? - Nitesh Verma

1

在您想要意图进入下一个活动的位置编写此代码。

    yourimageView.setDrawingCacheEnabled(true); 
    Drawable drawable = ((ImageView)view).getDrawable(); 
    Bitmap bitmap = imageView.getDrawingCache();
    Intent intent = new Intent(getBaseContext(), NextActivity.class);
    intent.putExtra("Image", imageBitmap);

在NextActivity类的onCreate函数中。
Bitmap hotel_image;
Intent intent = getIntent();
hotel_image= intent.getParcelableExtra("Image");

1

您可以通过以下方式简短地传递图像,而无需使用bundle

这是发送器的.class文件的代码

Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher;
Intent intent = new Intent();
Intent.setClass(<Sender_Activity>.this, <Receiver_Activity.class);
Intent.putExtra("Bitmap", bitmap);
startActivity(intent);

这是接收器类文件的代码。

Bitmap bitmap = (Bitmap)this.getIntent().getParcelableExtra("Bitmap");
ImageView viewBitmap = (ImageView)findViewById(R.id.bitmapview);
viewBitmap.setImageBitmap(bitmap);

不需要压缩。 就是这样。


1

我不得不对位图进行一些缩放,以避免超过事务绑定器的1mb限制。您可以根据自己的屏幕调整400或使其动态,这只是一个示例。 它运行良好,质量也很好。 它也比保存图像并在之后加载要快得多,但您有尺寸限制。

public void loadNextActivity(){
    Intent confirmBMP = new Intent(this,ConfirmBMPActivity.class);

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    Bitmap bmp = returnScaledBMP();
    bmp.compress(Bitmap.CompressFormat.JPEG, 100, stream);

    confirmBMP.putExtra("Bitmap",bmp);
    startActivity(confirmBMP);
    finish();

}
public Bitmap returnScaledBMP(){
    Bitmap bmp=null;
    bmp = tempBitmap;
    bmp = createScaledBitmapKeepingAspectRatio(bmp,400);
    return bmp;

}

在您的下一个Activity中使用以下代码恢复bmp后:
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_confirmBMP);
    Intent intent = getIntent();
    Bitmap bitmap = (Bitmap) intent.getParcelableExtra("Bitmap");

}

我希望我的回答在某种程度上对您有所帮助。 问候


0

最好将文件保存在temp/cache文件夹中,并像我一样通过意图数据传递文件路径。以下是示例代码:

从按钮点击事件开始(这里的bitmapFullScreen是我从Live Server收集的位图数据)

Intent intent = new Intent(context, FullscreenActivity.class);
String fPath = CreateTempFile(bitmapFullScreen);
if(!StringUtils.isEmpty(fPath)) {
    intent.putExtra("image", fPath);
    startActivity(intent);
}

创建在Temp/Cache文件夹中生成文件的函数。
public String CreateTempFile(Bitmap mBitmap) {
    File f3 = new File(Environment.getExternalStorageDirectory() + "/inpaint/");
    if (!f3.exists())
        f3.mkdirs();

    OutputStream outStream = null;
    SimpleDateFormat dateFormatter;
    dateFormatter = new SimpleDateFormat("MMddyyyyhhmmss", Locale.US);
    String FN = Environment.getExternalStorageDirectory() + "/inpaint/" + dateFormatter.format(Calendar.getInstance().getTime()) + ".png";
    File file = new File(FN);
    try {
        outStream = new FileOutputStream(file);
        mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
        outStream.close();
        return file.getAbsolutePath();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}

我们需要文件的第二个位置

全局变量

String FileName = "";

接收意图字符串数据作为文件路径以在ImageView上查看图像 此代码将在onCreate中

ImageView imgUpload = findViewById(R.id.imgUpload);
try {
    if (getIntent().hasExtra("image")) {
        FileName = getIntent().getStringExtra("image");
        Bitmap b = BitmapFactory.decodeFile(FileName);
        imgUpload.setImageBitmap(b);
    }
} catch (Exception ex) {
    Toast.makeText(context, "Error: " + ex.getMessage(), Toast.LENGTH_LONG).show();
}

显示图像后,我们需要删除临时文件。
@Override
public void onBackPressed() {
    super.onBackPressed();
    try {
        File file = new File(FileName);
        file.delete();
    } catch (Exception ex) {
        Toast.makeText(context, "Error: " + ex.getMessage(), Toast.LENGTH_LONG).show();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接