是否有必要将图像存储在内存中以显示在android中的列表视图上

时间:2013-01-11 07:45:00

标签: android image listview memory-management drawable

我在列表的每一行显示一个ImageView列表。

为此,我使用Drawable.createFromStream从另一个AsyncTask中的net下载图像 并将它们存储为ArrayList中的 Drawable ,我将其传递给扩展BaseAdapter类的Adapter类。

但是图像是用高分辨率相机拍摄的,所以可能是非常大的尺寸。 我收到OutOfMemory错误。

所以我的问题:

  1. 什么更有效,将图像存储为可绘制或位图或任何其他格式?

  2. 我是否正确,将所有图像存储在内存中(在数组列表中)。即我想,一旦我得到一个图像,我将在ImageView上显示它,并且不会存储在ArrayList中。

  3. 有什么办法,我可以在下载后压缩图片,这样就可以减少内存空间。

  4. 我的总代码存在here

3 个答案:

答案 0 :(得分:1)

有很好的库调用AQuery。你可以使用它,并通过只写2行代码简单地获取内存和文件缓存等所有内容。所以你甚至不需要准备一个drawable,你可以直接从Adapter.getView()回调中调用它。

AQuery aq = new AQuery(rowView);
aq.id(R.id.image).image(url, false, true);

希望对你有所帮助!

来自AQuery docs:

  

向下采样(处理巨大的图像)   我们正在从网络加载一个巨大的图像,但我们只需要图像大于200像素宽。传递目标宽度为200将下采样图像以节省内存.Aquery将仅使用2(2,4,8 ...)的功率下采样,以获得良好的图像质量和   效率。得到的图像宽度将在200到399像素之间

     

String imageUrl = "http://farm6.static.flickr.com/5035/5802797131_a729dac808_b.jpg";
aq.id(R.id.image1).image(imageUrl, true, true, 200, 0);

答案 1 :(得分:1)

  1. 我没有看到将高密度图像存储到内存中的任何效率 - 完全不建议将大量图像作为位图存储在内存中(对你来说你有一个好的设备;)
  2. 见第1页
  3. 尝试缩小图像以适应设备的需求 - 尽管这不是一项简单的工作。另请参阅View.setTag(Object tag)
  4. 适配器

    public class MyImageListAdapter extends BaseAdapter implements ImageLoadingNotifier {
    
    private LayoutInflater inflater = null;
    
    public MyImageListAdapter() {
        inflater = LayoutInflater)HomeActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    
    public int getCount() {
        return listImageInfo.size();
    }
    
    public Object getItem(int position) {
        return listImageInfo.get(position);
    }
    
    public long getItemId(int position) {
        return position;
    }
    
    public View getView(int position, View convertView, ViewGroup parent) {
        View vi = convertView;
        if (convertView == null) {
            vi = inflater.inflate(R.layout.list_row, null);
        }
    
        TextView tvName = (TextView) vi.findViewById(R.id.tv_name);
        TextView tvTime = (TextView) vi.findViewById(R.id.tv_time);
        ImageView image = (ImageView) vi.findViewById(R.id.iv_image);
        final Button btnDelete = (Button) vi.findViewById(R.id.btn_delete);
    
    
        image.setImageDrawable(R.drawable.default_placeholder);//set default place-holder
        new GetDrawableFromUrl(listImageInfo.get(position), vi).execute();
    
        tvName.setText("Name: " + listImageInfo.get(position).getImage_name());
        tvTime.setText("Date: " + listImageInfo.get(position).getDate_created());
    
        btnDelete.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                final int position = listView.getPositionForView((View) v.getParent());
                positionOgBtnToDelete = position;
                Log.v("delete btn clicked", "delete btn no: " + position);
                Toast.makeText(HomeActivity.this, "Btn delete position: " + position, Toast.LENGTH_LONG).show();
                showAlertToConfirmDelete();
            }
        });
        return vi;
    }
    
    }
    

    AsyncTask GetDrawableFromUrl

    public class GetDrawableFromUrl extends AsyncTask<Void, Void, Drawable> {
    public ImageInfo imageInfoObj;
    private ImageView view;
    
    
    GetDrawableFromUrl(ImageInfo imageInfo, ImageView view) {
        imageInfoObj = imageInfo;
        this.view = view;
    }
    
    @Override
    protected Drawable doInBackground(Void... params) {
        try {
            return Drawable.createFromStream(((java.io.InputStream) new java.net.URL(imageInfoObj.getImageUrl()).getContent()), "src_name");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    protected void onPostExecute(Drawable drawable) {
        if (drawable != null) {
            //imageInfoObj.setImage(drawable);
            this.view.setImageDrawable(drawable);
            //listImageInfo.add(imageInfoObj); //this one is called when the json is parsed
            showImagesInList(); //don't know what it does (??)
        }
    }
    

    }

    JSON解析

    JSONArray jsonArray = jsonObj.getJSONArray("result");
                for (int i = 0; i < jsonArray.length(); i++) {
                    JSONObject jsonObjInner = jsonArray.getJSONObject(i);
    
                    ImageInfo imageInfo = new ImageInfo();
                    imageInfo.setImageUrl("http://www.dvimaytech.com/markphoto/" + jsonObjInner.getString("image"));
    
                    //new GetDrawableFromUrl(imageInfo).execute(); //don't needed here
    
                    imageInfo.setEmail(jsonObjInner.getString("emailid"));
                    imageInfo.setImage_id(jsonObjInner.getString("image_id"));
                    imageInfo.setImage_name(jsonObjInner.getString("image_name"));
                    imageInfo.setAmount(jsonObjInner.getString("amount"));
                    imageInfo.setImage_description(jsonObjInner.getString("image_description"));
                    imageInfo.setDate_created(jsonObjInner.getString("date_created"));
    
                    listImageInfo.add(imageInfo);
                }
    

    并且,使用任何类型的List图像都是不必要的:)

    在解析json对象时,您可以在GetDrawableFromUrl方法中启动任务,而不是启动异步任务(getView(...))。这样,您就不会将drawable存储到ArrayList中,因为您将在下载图像后修改ImageView。并且,默认情况下,您可以放置​​占位符,直到下载图像(或者在出现网络错误的情况下)。

    这样,只有在为特定项目调用getView方法时,图像才会开始下载。

    最重要的是,ListView中的每个视图都会保留对其特定drawable的引用(使用vi.setTag(image)设置。

    如果这有所帮助,你知道该怎么做;)

答案 2 :(得分:1)

Android文档提供了一个非常好的示例,展示了如何在Android应用中处理位图。该示例使用磁盘和内存缓存并在后台加载图像。通过这样做,主UI线程不会通过加载图像来减慢速度。

Loading Bitmaps effectively

在示例中,图像是从picasa加载的。然而,调整示例很容易,因此使用本地存储的图片。您只需编写自己的ImageLoader,即可从&#39; ImageResizer&#39;:

开始
public class ImageLoader extends ImageResizer {
    public ImageLoader(Context context, int imageWidth, int imageHeight) {
        super(context, imageWidth, imageHeight);
    }
    public ImageLoader(Context context, int imageSize) {
        super(context, imageSize);
    }
    @Override
    protected Bitmap processBitmap(Object data) {
        return decodeSampledBitmapFromFile((String)data, imageWidth, imageHeight);
    }
}

但是直接回答你的问题:将图像加载为位图是可以的。但是你必须使用缓存和弱引用,这样图像就可以被垃圾收集,以防它们在屏幕上看不到。缓存它们并使用后台任务进行加载可以实现流畅的UI。