Android:listview滚动期间的setImageBitmap vs setImageURI

时间:2015-04-08 18:32:38

标签: android listview android-listview

我有一个包含未知数量自定义项目的列表视图,并且在滚动期间 - 在getView内部)我检查aConvertView的相关图像是否在本地存在并将其设置为listview项目(aConvertView):

    // out of getView(...)

    Bitmap mBitmap;
    BitmapFactory.Options mOptions = new BitmapFactory.Options();
    mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;


    // Inside getView(...)

    File file = new File(mContext.getFilesDir(), picture_file_name);
    if(file.exists())
    {
        **// Which one has less impact on the UI thread?**

        mBitmap = BitmapFactory.decodeFile(file.getPath(), mOptions);
        aImageView.setImageBitmap(mBitmap);

        // OR

        aImageView.setImageURI(Uri.fromFile(file));
    }

当然我有滚动延迟的已知问题,我想知道在滚动期间设置项目的ImageView的最佳方法/方法。

setImageBitmap对UI线程的影响小于setImageURI?,或者两者都在UI上工作并导致延迟。

使用新文件,File.exists()会对UI线程产生影响吗?

毋庸置疑,我为listview项目使用了一个持有者,并使用以下参数进行了我的列表视图:

        android:smoothScrollbar="true"
        android:scrollingCache="false"
        android:animationCache="false"

3 个答案:

答案 0 :(得分:3)

两者都在UI线程上运行。我会说第一个可能比第二个快一点。第二个真正取决于Uri资源的来源(例如,uri可能指向甚至没有存储在手机上的远程文件)。

我认为这里的问题是否应该在UI线程上加载位图,答案是否定的,尤其是在listview中。 Google在developers.android.com上发布了一个关于从UI线程加载位图的精彩教程:http://developer.android.com/training/displaying-bitmaps/process-bitmap.html

答案 1 :(得分:1)

您有延迟问题,因为您在UI线程上执行文件IO,主要是decodeFile这里是一个问题,您实际访问光盘上的文件数据(尽管没有理由调用File.exists()来自UI线程)。

Quanturium的答案是正确的

示例解释了如何在后台加载位图并使用WeakReferenceImageView将位图指定为背景可绘制。请记住,所有文件IO工作都应该在doInBackground内,onPostExecute实际上发生在UI线程上。

编辑:如何在AsyncTask中使用File.Exists()

// Decode image in background. 
    @Override 
    protected Bitmap doInBackground(Integer... params) {
        File file = new File(mContext.getFilesDir(), picture_file_name);
        if(file.exists())
        { 
           return BitmapFactory.decodeFile(file.getPath(), mOptions);
        }
        else
        {
           return null;
        }
   }

注意:我无法访问我的开发机器,所以我不确定这是否会构建。

要回答关于从另一个启动一个AsyncTask的问题 - 我从来没有这样做过,我想这是可能的。就我而言,我将所有流量都放在一个doInBackground

  1. 首先下载数据(我猜你正在使用一些REST调用来执行此操作)。
  2. 从您的回复中获取内容
  3. 如果文件存在,则覆盖
  4. 如果文件不存在,请创建
  5. 对我来说,最重要的是在后台线程中运行REST调用和文件IO - 我不在乎它是一个还是两个任务。

答案 2 :(得分:0)

之前我遇到过同样的问题。我试图在Uri中加载6个本地大图像(使用ListView)。为了让您的应用顺利运行,您可能需要做两件事。

  1. 不要在主线程上加载图像。这是一个如何在单独的线程上加载图像的android文档:link。有很多库已经为你实现了这个。如果您要从互联网,服务器等加载图像,我建议Picasso
  2. 2.缩放图像大小以使图像更适合ImageView。这是一个如何缩放图像大小的android文档。 link

    毕竟这是我的AsyncTask类,它在新线程上加载图像并根据imageview缩放图像:

    class BitmapWorkerTask extends AsyncTask<Uri, Void, Bitmap> {
        private final WeakReference<ImageView> imageViewReference;
        private Uri data = null;
    
        public BitmapWorkerTask(ImageView imageView) {
            // Use a WeakReference to ensure the ImageView can be garbage collected
            imageViewReference = new WeakReference<ImageView>(imageView);
        }
    
        // Decode image in background.
        @Override
        protected Bitmap doInBackground(Uri... params) {
            data = params[0];
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            Bitmap bitmap = BitmapFactory.decodeFile(data.toString(),options);
            options.inSampleSize= calculateInSampleSize(options,300,300);
            options.inJustDecodeBounds = false;
            return  BitmapFactory.decodeFile(data.toString(),options);
    
        }
    
        // Once complete, see if ImageView is still around and set bitmap.
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (imageViewReference != null && bitmap != null) {
                final ImageView imageView = imageViewReference.get();
                if (imageView != null) {
                    imageView.setImageBitmap(bitmap);
                }
            }
        }
    }
    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
    
        if (height > reqHeight || width > reqWidth) {
    
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
    
            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
    
        return inSampleSize;
    }
    

    300 * 300是我的图片视图尺寸