【发布时间】:2011-01-28 09:46:20
【问题描述】:
在我的活动中,我创建了一个Bitmap 对象,然后我需要启动另一个Activity,
如何从子活动(即将启动的活动)中传递这个Bitmap 对象?
【问题讨论】:
标签: java android bitmap parcelable
在我的活动中,我创建了一个Bitmap 对象,然后我需要启动另一个Activity,
如何从子活动(即将启动的活动)中传递这个Bitmap 对象?
【问题讨论】:
标签: java android bitmap parcelable
Bitmap 实现了Parcelable,因此您始终可以按意图传递它:
Intent intent = new Intent(this, NewActivity.class);
intent.putExtra("BitmapImage", bitmap);
并在另一端检索它:
Intent intent = getIntent();
Bitmap bitmap = (Bitmap) intent.getParcelableExtra("BitmapImage");
【讨论】:
URI 或ResourceID,而不是位图本身。传递整个位图需要大量内存。传递 URL 只需要很少的内存,并允许每个活动根据需要加载和缩放位图。
android.os.TransactionTooLargeException 抛出这个。
实际上,将位图作为 Parcelable 传递会导致“JAVA BINDER FAILURE”错误。尝试将位图作为字节数组传递并构建它以在下一个活动中显示。
我在这里分享了我的解决方案:
how do you pass images (bitmaps) between android activities using bundles?
【讨论】:
由于 Parceable(1mb) 的大小限制,在活动之间将位图作为 parceable 传递不是一个好主意。您可以将位图存储在内部存储中的文件中,并在多个活动中检索存储的位图。这是一些示例代码。
将位图存储在内部存储中的文件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 解码为位图:
//here context can be anything like getActivity() for fragment, this or MainActivity.this
Bitmap bitmap = BitmapFactory.decodeStream(context.openFileInput("myImage"));
注意省略了很多检查 null 和缩放位图的操作。
【讨论】:
openFileOutput。
Bitmap
当Bitmap 太大时,接受的答案会崩溃。我相信这是 1MB 的限制。 Bitmap 必须被压缩成不同的文件格式,例如由ByteArray 表示的JPG,然后才能通过Intent 安全地传递。
该函数包含在使用 Kotlin Coroutines 的单独线程中,因为 Bitmap 压缩是在从 URL String 创建 Bitmap 之后链接的。 Bitmap 创建需要单独的线程以避免 Application Not Responding (ANR) 错误。
toBitmap() 是一个 Kotlin extension function,要求将该库添加到应用依赖项中。Bitmap 压缩为JPG ByteArray。Repository.kt
suspend fun bitmapToByteArray(url: String) = withContext(Dispatchers.IO) {
MutableLiveData<Lce<ContentResult.ContentBitmap>>().apply {
postValue(Lce.Loading())
postValue(Lce.Content(ContentResult.ContentBitmap(
ByteArrayOutputStream().apply {
try {
BitmapFactory.decodeStream(URL(url).openConnection().apply {
doInput = true
connect()
}.getInputStream())
} catch (e: IOException) {
postValue(Lce.Error(ContentResult.ContentBitmap(ByteArray(0), "bitmapToByteArray error or null - ${e.localizedMessage}")))
null
}?.compress(CompressFormat.JPEG, BITMAP_COMPRESSION_QUALITY, this)
}.toByteArray(), "")))
}
}
ViewModel.kt
//Calls bitmapToByteArray from the Repository
private fun bitmapToByteArray(url: String) = liveData {
emitSource(switchMap(repository.bitmapToByteArray(url)) { lce ->
when (lce) {
is Lce.Loading -> liveData {}
is Lce.Content -> liveData {
emit(Event(ContentResult.ContentBitmap(lce.packet.image, lce.packet.errorMessage)))
}
is Lce.Error -> liveData {
Crashlytics.log(Log.WARN, LOG_TAG,
"bitmapToByteArray error or null - ${lce.packet.errorMessage}")
}
}
})
}
Intent 将图像作为ByteArray 传递。在此示例中,它从 Fragment 传递到 Service。如果在两个 Activity 之间共享,这是同一个概念。
Fragment.kt
ContextCompat.startForegroundService(
context!!,
Intent(context, AudioService::class.java).apply {
action = CONTENT_SELECTED_ACTION
putExtra(CONTENT_SELECTED_BITMAP_KEY, contentPlayer.image)
})
ByteArray 转换回Bitmap。Utils.kt
fun ByteArray.byteArrayToBitmap(context: Context) =
run {
BitmapFactory.decodeByteArray(this, BITMAP_OFFSET, size).run {
if (this != null) this
// In case the Bitmap loaded was empty or there is an error I have a default Bitmap to return.
else AppCompatResources.getDrawable(context, ic_coinverse_48dp)?.toBitmap()
}
}
【讨论】:
如果图像太大并且您无法将其保存并加载到存储中,您应该考虑只使用对位图的全局静态引用(在接收活动内部),它将在 onDestory 上重置为 null,仅当"isChangingConfigurations" 返回 true。
【讨论】:
因为 Intent 有大小限制。 我使用公共静态对象将位图从服务传递到广播 ....
public class ImageBox {
public static Queue<Bitmap> mQ = new LinkedBlockingQueue<Bitmap>();
}
传递我的服务
private void downloadFile(final String url){
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap b = BitmapFromURL.getBitmapFromURL(url);
synchronized (this){
TaskCount--;
}
Intent i = new Intent(ACTION_ON_GET_IMAGE);
ImageBox.mQ.offer(b);
sendBroadcast(i);
if(TaskCount<=0)stopSelf();
}
});
}
我的广播接收器
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
LOG.d(TAG, "BroadcastReceiver get broadcast");
String action = intent.getAction();
if (DownLoadImageService.ACTION_ON_GET_IMAGE.equals(action)) {
Bitmap b = ImageBox.mQ.poll();
if(b==null)return;
if(mListener!=null)mListener.OnGetImage(b);
}
}
};
【讨论】:
可能会晚,但可以提供帮助。 在第一个片段或活动上声明一个类...例如
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
description des = new description();
if (requestCode == PICK_IMAGE_REQUEST && data != null && data.getData() != null) {
filePath = data.getData();
try {
bitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), filePath);
imageView.setImageBitmap(bitmap);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
constan.photoMap = bitmap;
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static class constan {
public static Bitmap photoMap = null;
public static String namePass = null;
}
然后在第二个类/片段上执行此操作..
Bitmap bm = postFragment.constan.photoMap;
final String itemName = postFragment.constan.namePass;
希望对你有帮助。
【讨论】:
以上所有解决方案都不适合我,以parceableByteArray 发送位图也会产生错误android.os.TransactionTooLargeException: data parcel size。
解决方案
public String saveBitmap(Bitmap bitmap) {
String fileName = "ImageName";//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;
}
putExtra(String) 的身份发送Intent intent = new Intent(ActivitySketcher.this,ActivityEditor.class);
intent.putExtra("KEY", saveBitmap(bmp));
startActivity(intent);
if(getIntent() != null){
try {
src = BitmapFactory.decodeStream(openFileInput("myImage"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
【讨论】:
您可以创建位图传输。试试这个....
第一堂课:
1) 创建:
private static Bitmap bitmap_transfer;
2) 创建getter和setter
public static Bitmap getBitmap_transfer() {
return bitmap_transfer;
}
public static void setBitmap_transfer(Bitmap bitmap_transfer_param) {
bitmap_transfer = bitmap_transfer_param;
}
3) 设置图片:
ImageView image = (ImageView) view.findViewById(R.id.image);
image.buildDrawingCache();
setBitmap_transfer(image.getDrawingCache());
那么,在第二节课中:
ImageView image2 = (ImageView) view.findViewById(R.id.img2);
imagem2.setImageDrawable(new BitmapDrawable(getResources(), classe1.getBitmap_transfer()));
【讨论】:
就我而言,上述方法对我不起作用。每次我将位图放入意图中时,第二个活动都没有开始。当我将位图作为字节 [] 传递时,也发生了同样的情况。
我关注了这个link,它的运行速度非常快:
package your.packagename
import android.graphics.Bitmap;
public class CommonResources {
public static Bitmap photoFinishBitmap = null;
}
在我的第一个活动中:
Constants.photoFinishBitmap = photoFinishBitmap;
Intent intent = new Intent(mContext, ImageViewerActivity.class);
startActivity(intent);
这是我的第二个活动的 onCreate():
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bitmap photo = Constants.photoFinishBitmap;
if (photo != null) {
mViewHolder.imageViewerImage.setImageDrawable(new BitmapDrawable(getResources(), photo));
}
}
【讨论】:
CommonResources.photoFinishBitmap 而不是 Constants.photoFinishBitmap。