我在列表的每一行显示一个ImageView
列表。
为此,我使用Drawable.createFromStream
从另一个AsyncTask中的net下载图像
并将它们存储为ArrayList
中的 Drawable ,我将其传递给扩展BaseAdapter类的Adapter类。
但是图像是用高分辨率相机拍摄的,所以可能是非常大的尺寸。
我收到OutOfMemory
错误。
所以我的问题:
什么更有效,将图像存储为可绘制或位图或任何其他格式?
我是否正确,将所有图像存储在内存中(在数组列表中)。即我想,一旦我得到一个图像,我将在ImageView上显示它,并且不会存储在ArrayList中。
有什么办法,我可以在下载后压缩图片,这样就可以减少内存空间。
我的总代码存在here
答案 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)
适配器
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线程不会通过加载图像来减慢速度。
在示例中,图像是从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。