我正在使用 DisplayingBitmap.zip从开发人员 android 站点下载以将图像异步加载到 imageview 中。我从网络服务接收 base64 字符串。所以修改了代码,将 base64 转换为 ImageFetcher.class 中的位图(DisplayingBitmaps),而不是从 url 下载图像。
注意:我正在接收 base64 字符串形式的 gif 图像。
将 base64 转换为位图
public Bitmap convertBase64ToImage(String mBase64String) {
Bitmap bitmap = null;
try {
String imageDataBytes = mBase64String.substring(mBase64String.indexOf(",") + 1);
InputStream stream = new ByteArrayInputStream(Base64.decode(imageDataBytes.getBytes(), Base64.DEFAULT));
bitmap = BitmapFactory.decodeStream(stream);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
将 base64 位图转换为文件,以便使用 ImageFetcher.class(DisplayingBitmaps) 中的 processBitmap 方法获得解码后调整大小的位图:
/**
* The main process method, which will be called by the ImageWorker in the AsyncTask background
* thread.
*
* @param data The data to load the bitmap, in this case, a regular http URL
* @return The downloaded and resized bitmap
*/
private Bitmap processBitmap(String data, String imageID) {
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "processBitmap --- imageID " + imageID);
}
Bitmap bitmap = null;
bitmap = convertBase64ToImage(data);
if (bitmap != null) {
File f = null;
try {
//create a file to write bitmap data
f = new File(mContext.getFilesDir(), imageID);
f.createNewFile();
//Convert bitmap to byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100/*ignored for PNG*/, bos);
byte[] bitmapdata = bos.toByteArray();
//write the bytes in file
FileOutputStream fos = new FileOutputStream(f);
fos.write(bitmapdata);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
bitmap = decodeSampledBitmapFromFile(f.getAbsolutePath(), mImageWidth, mImageHeight, getImageCache());
}
return bitmap;
}
@Override
protected Bitmap processBitmap(Object data, String imageID) {
return processBitmap(String.valueOf(data), imageID);
}
来自 ImageResizer.class 的 decodeSampledBitmapFromFile 方法
public static Bitmap decodeSampledBitmapFromFile(String filename, int reqWidth, int reqHeight, ImageCache cache) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// If we're running on Honeycomb or newer, try to use inBitmap
if (DeviceUtils.hasHoneycomb()) {
addInBitmapOptions(options, cache);
}
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filename, options);
}
在我的类中实现 ImageFetcher.class(DisplayingBitmaps.zip)
private static final String IMAGE_CACHE_DIR = "clubsCategoryIcons";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ImageCache.ImageCacheParams cacheParams = new ImageCache.ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);
cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 10% of app memory
// The ImageFetcher takes care of loading images into our ImageView children asynchronously
mImageFetcher = new ImageFetcher(getActivity(), getResources().getDimensionPixelSize(R.dimen.image_icon_size));
mImageFetcher.setLoadingImage(R.drawable.settings_clubs);
mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);
}
将此 mImageFetcher 对象传递给适配器类以异步加载每个项目的图像。
ClubsCategoryAdapter clubsAdapter = new ClubsCategoryAdapter(getActivity(), new ArrayList<ClubsCategoryParser.ClubsCategory>(), mImageFetcher);
recyclerView.setAdapter(clubsAdapter);
ClubsCategoryAdapter.class
public class ClubsCategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ImageFetcher mImageFetcher;
public ClubsCategoryAdapter(Context context, ArrayList<ClubsCategoryParser.ClubsCategory> clubsCategoryList, ImageFetcher mImageFetcher ) {
this.context = context;
this.clubsCategoryList = clubsCategoryList;
this.mImageFetcher = mImageFetcher;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
final ClubsCategoryParser.ClubsCategory singleItem = clubsCategoryList.get(position);
if (!TextUtils.isNullOrEmpty(singleItem.image_url)) {
mImageFetcher.loadImage(singleItem.image_url, String.valueOf(singleItem.ID), viewHolder.imgCategoryIcon);
}
ImageWorker.class(DisplayingBitmaps) 中的 loadImage 方法
public void loadImage(Object data, String imageID, ImageView imageView) {
if (data == null) {
return;
}
BitmapDrawable value = null;
if (mImageCache != null) {
value = mImageCache.getBitmapFromMemCache(imageID);
}
if (value != null) {
// Bitmap found in memory cache
imageView.setImageDrawable(value);
} else if (cancelPotentialWork(data, imageView)) {
//BEGIN_INCLUDE(execute_background_task)
final BitmapWorkerTask task = new BitmapWorkerTask(data, imageID, imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, mLoadingBitmap, task);
imageView.setImageDrawable(asyncDrawable);
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
// framework and slightly modified. Refer to the docs at the top of the class
// for more info on what was changed.
task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR);
//END_INCLUDE(execute_background_task)
}
}
异步处理图像的实际异步任务
/**
* The actual AsyncTask that will asynchronously process the image.
*/
private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
private Object mData;
private String imageID;
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(Object data, String imageID, ImageView imageView) {
mData = data;
this.imageID = imageID;
imageViewReference = new WeakReference<ImageView>(imageView);
}
/**
* Background processing.
*/
@Override
protected BitmapDrawable doInBackground(Void... params) {
//BEGIN_INCLUDE(load_bitmap_in_background)
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "doInBackground - starting work");
}
final String dataString = String.valueOf(mData);
Bitmap bitmap = null;
BitmapDrawable drawable = null;
// Wait here if work is paused and the task is not cancelled
synchronized (mPauseWorkLock) {
while (mPauseWork && !isCancelled()) {
try {
Log.e("pauseWork", "iswaiting -------------");
mPauseWorkLock.wait();
} catch (InterruptedException e) {
}
}
}
Log.e("pauseWork", "iswaiting end -------------");
// If the image cache is available and this task has not been cancelled by another
// thread and the ImageView that was originally bound to this task is still bound back
// to this task and our "exit early" flag is not set then try and fetch the bitmap from
// the cache
if (mImageCache != null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly) {
bitmap = mImageCache.getBitmapFromDiskCache(imageID);
}
// If the bitmap was not found in the cache and this task has not been cancelled by
// another thread and the ImageView that was originally bound to this task is still
// bound back to this task and our "exit early" flag is not set, then call the main
// process method (as implemented by a subclass)
if (bitmap == null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly) {
bitmap = processBitmap(mData, imageID);
}
// If the bitmap was processed and the image cache is available, then add the processed
// bitmap to the cache for future use. Note we don't check if the task was cancelled
// here, if it was, and the thread is still running, we may as well add the processed
// bitmap to our cache as it might be used again in the future
if (bitmap != null) {
if (DeviceUtils.hasHoneycomb()) {
// Running on Honeycomb or newer, so wrap in a standard BitmapDrawable
drawable = new BitmapDrawable(mResources, bitmap);
} else {
// Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable
// which will recycle automagically
drawable = new RecyclingBitmapDrawable(mResources, bitmap);
}
if (mImageCache != null) {
mImageCache.addBitmapToCache(imageID, drawable);
}
}
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "doInBackground - finished work");
}
return drawable;
//END_INCLUDE(load_bitmap_in_background)
}
/**
* Once the image is processed, associates it to the imageView
*/
@Override
protected void onPostExecute(BitmapDrawable value) {
//BEGIN_INCLUDE(complete_background_work)
// if cancel was called on this task or the "exit early" flag is set then we're done
if (isCancelled() || mExitTasksEarly) {
value = null;
}
final ImageView imageView = getAttachedImageView();
if (value != null && imageView != null) {
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "onPostExecute - setting bitmap");
}
setImageDrawable(imageView, value);
}
//END_INCLUDE(complete_background_work)
}
@Override
protected void onCancelled(BitmapDrawable value) {
super.onCancelled(value);
synchronized (mPauseWorkLock) {
mPauseWorkLock.notifyAll();
}
}
/**
* Returns the ImageView associated with this task as long as the ImageView's task still
* points to this task as well. Returns null otherwise.
*/
private ImageView getAttachedImageView() {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask) {
return imageView;
}
return null;
}
}
第一次安装应用时图像显示正常,但在终止应用并加载同一页面后,图像显示为黑色/白色背景。
我试过很多例子和文章..但没有任何帮助。我不知道为什么它在终止/退出应用程序后会出现黑色/白色背景。
最佳答案
您的应用程序第一次可以正常显示图像,但在重新打开应用程序后显示黑色背景,因为“DisplayingBitmap”应用程序使用 JPEG 格式将图像缓存到文件系统。如您所知,JPEG 不支持透明模式。
请打开ImageCache类并查看#68行:
private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG;
为了避免黑色背景,我将此值更改为 PNG 格式:
private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.PNG;
更新:
此外,您可以将压缩格式设置为 JPEG:
ImageCache.ImageCacheParams cacheParams = new ImageCache.ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);
cacheParams.setMemCacheSizePercent(0.25f);
cacheParams.compressFormat = Bitmap.CompressFormat.JPEG;
它对我有用,希望对你有帮助。
关于java - ImageView 显示透明 base64 字符串图像的黑色/白色背景,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36197904/