java - ImageView 显示透明 base64 字符串图像的黑色/白色背景

标签 java android image bitmap

我正在使用 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/

相关文章:

Java Servlet 为 POST 请求返回错误 405 (Method Not Allowed)

android - 如何从在android中没有名称的嵌套json中获取数据?

java - libgdx android 启动失败

android - setRetainInstance(true) 与后台 Activity

android - 如何在 Android imageView 中限制图像平移边界

php如何获取以kb为单位的网页图像大小?

java - Java中从大文件中读取字节导致Java堆空间错误

java - 使用 MySQL 存储和检索 Word 文档

java - 在JAVAFX中将字符串行添加到TableView

php - 使用 imagick 驱动程序调整为小图像的图像会产生大文件大小