我正在使用ListView来显示与这些图像相关的一些图像和标题。我正在从互联网上获取图像。是否有一种方法可以延迟加载图像,以便在文本显示时,UI不会被阻止,图像在下载时显示?
图像总数不是固定的。
我正在使用ListView来显示与这些图像相关的一些图像和标题。我正在从互联网上获取图像。是否有一种方法可以延迟加载图像,以便在文本显示时,UI不会被阻止,图像在下载时显示?
图像总数不是固定的。
当前回答
使用下面的类来下载和加载listview中的图像。它会在下载后缓存每个图像。还加载图像和延迟加载。
package com.fudiyoxpress.images;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.widget.ImageView;
import com.fudiyoxpress.R;
import com.fudiyoxpress.config.Config;
import com.fudiyoxpress.twitter.ScaleBitmap;
public class ImageLoader {
// Initialize MemoryCache
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
Context C;
// Create Map (collection) to store image and image url in key value pair
private Map<ImageView, String> imageViews = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
// handler to display images in UI thread
Handler handler = new Handler();
public ImageLoader(Context context) {
C = context;
fileCache = new FileCache(context);
// Creates a thread pool that reuses a fixed number of
// threads operating off a shared unbounded queue.
executorService = Executors.newFixedThreadPool(5);
}
// default image show in list (Before online image download)
final int stub_id = R.drawable.restlogoplaceholder;
public void DisplayImage(String url, ImageView imageView, Context context,
boolean header_flag) {
Bitmap largeIcon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.restlogoplaceholder);
header_flag = false;
// Store image and url in Map
imageViews.put(imageView, url);
// Check image is stored in MemoryCache Map or not (see
// MemoryCache.java)
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null) {
// if image is stored in MemoryCache Map then
// Show image in listview row
Bitmap b = ScaleBitmap
.getScaledBitmap(context, bitmap, header_flag);
imageView.setImageBitmap(b);
} else {
// queue Photo to download from url
queuePhoto(url, imageView, header_flag);
// Before downloading image show default image
imageView.setImageBitmap(ScaleBitmap.getScaledBitmap(context,
largeIcon, header_flag));
}
}
private void queuePhoto(String url, ImageView imageView, boolean header_flag) {
// Store image and url in PhotoToLoad object
PhotoToLoad p = new PhotoToLoad(url, imageView, header_flag);
// pass PhotoToLoad object to PhotosLoader runnable class
// and submit PhotosLoader runnable to executers to run runnable
// Submits a PhotosLoader runnable task for execution
executorService.submit(new PhotosLoader(p));
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public boolean b;
public PhotoToLoad(String u, ImageView i, boolean header_flag) {
url = u;
imageView = i;
b = header_flag;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad) {
this.photoToLoad = photoToLoad;
}
@Override
public void run() {
try {
// Check if image already downloaded
if (imageViewReused(photoToLoad))
return;
// download image from web url
Bitmap bmp = getBitmap(photoToLoad.url);
// set image data in Memory Cache
memoryCache.put(photoToLoad.url, bmp);
if (imageViewReused(photoToLoad))
return;
// Get bitmap to display
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
// Causes the Runnable bd (BitmapDisplayer) to be added to the
// message queue.
// The runnable will be run on the thread to which this handler
// is attached.
// BitmapDisplayer run method will call
handler.post(bd);
} catch (Throwable th) {
// th.printStackTrace();
}
}
}
private Bitmap getBitmap(String url) {
File f = fileCache.getFile(url);
// from SD cache
// CHECK : if trying to decode file which not exist in cache return null
Bitmap b = decodeFile(f);
if (b != null)
return b;
// Download image file from web
try {
// // download the image
Bitmap bitmap = null;
URL imageURL = null;
try {
imageURL = new URL(Config.WEB_URL + "/ServeBlob?id=" + url);
HttpURLConnection connection = (HttpURLConnection) imageURL
.openConnection();
connection.setDoInput(true);
connection.connect();
// if(!(new File(imageURL.toString())).exists())
// {
// imageURL=new URL("");
// }
InputStream inputStream = connection.getInputStream();
// Constructs a new FileOutputStream that writes to
// file
// if file not exist then it will create file
OutputStream os = new FileOutputStream(f);
// See Utils class CopyStream method
// It will each pixel from input stream and
// write pixels to output stream (file)
Utils.CopyStream(inputStream, os);
os.close();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
bitmap = BitmapFactory.decodeStream(inputStream, null, options);
} catch (IOException e) {
// e.printStackTrace();
}
// Now file created and going to resize file with defined height
// Decodes image and scales it to reduce memory consumption
bitmap = decodeFile(f);
return bitmap;
} catch (Throwable ex) {
ex.printStackTrace();
if (ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream stream1 = new FileInputStream(f);
BitmapFactory.decodeStream(stream1, null, o);
stream1.close();
// Find the correct scale value. It should be the power of 2.
// Set width/height of recreated image
final int REQUIRED_SIZE = 85;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE
|| height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with current scale values
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
FileInputStream stream2 = new FileInputStream(f);
Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
boolean imageViewReused(PhotoToLoad photoToLoad) {
String tag = imageViews.get(photoToLoad.imageView);
// Check url is already exist in imageViews MAP
if (tag == null || !tag.equals(photoToLoad.url))
return true;
return false;
}
// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
bitmap = b;
photoToLoad = p;
}
public void run() {
if (imageViewReused(photoToLoad))
return;
// Show bitmap on UI
if (bitmap != null) {
photoToLoad.imageView.setImageBitmap(ScaleBitmap
.getScaledBitmap(C, bitmap, photoToLoad.b));
} else {
}
// photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
// Clear cache directory downloaded images and stored data in maps
memoryCache.clear();
fileCache.clear();
}
}
package com.fudiyoxpress.images;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.graphics.Bitmap;
import android.util.Log;
public class MemoryCache {
private static final String TAG = "MemoryCache";
//Last argument true for LRU ordering
private Map<String, Bitmap> cache = Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));
//current allocated size
private long size=0;
//max memory cache folder used to download images in bytes
private long limit = 1000000;
public MemoryCache(){
//use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/4);
}
public void setLimit(long new_limit){
limit=new_limit;
Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}
public Bitmap get(String id){
try{
if(!cache.containsKey(id))
return null;
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
return cache.get(id);
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
public void put(String id, Bitmap bitmap){
try{
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();
}catch(Throwable th){
th.printStackTrace();
}
}
private void checkSize() {
Log.i(TAG, "cache size="+size+" length="+cache.size());
if(size>limit){
Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
while(iter.hasNext()){
Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
}
public void clear() {
try{
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
cache.clear();
size=0;
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
package com.fudiyoxpress.images;
import java.io.InputStream;
import java.io.OutputStream;
public class Utils {
public static void CopyStream(InputStream is, OutputStream os)
{
final int buffer_size=1024;
try
{
byte[] bytes=new byte[buffer_size];
for(;;)
{
//Read byte from input stream
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
//Write byte from output stream
os.write(bytes, 0, count);
}
}
catch(Exception ex){}
}
}
其他回答
这就是使用Jetpack Compose的方法。
implementation("io.coil-kt:coil-compose:1.3.1") // Add the Coil-Compose library
Image(
painter = rememberImagePainter("https://www.example.com/image.jpg"),
contentDescription = "My image description",
modifier = Modifier.size(128.dp)
)
感谢nglauber和Gabriele Mariotti的回答。
嗯,从互联网加载图像有很多解决方案。您也可以使用Android Query库。它将为您提供所有必需的活动。确保您想做什么,并阅读库wiki页面。并解决了图像加载限制。
这是我的代码:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
ImageView imageview = (ImageView) v.findViewById(R.id.icon);
AQuery aq = new AQuery(convertView);
String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";
aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
@Override
public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
iv.setImageBitmap(bm);
}
));
return v;
}
它应该可以解决您的延迟加载问题。
以下是我创建的用于保存应用程序当前显示的图像的内容。请注意,这里使用的“Log”对象是我对Android中最终Log类的自定义包装。
package com.wilson.android.library;
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;
public class DrawableManager {
private final Map<String, Drawable> drawableMap;
public DrawableManager() {
drawableMap = new HashMap<String, Drawable>();
}
public Drawable fetchDrawable(String urlString) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}
Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
if (drawable != null) {
drawableMap.put(urlString, drawable);
Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
} else {
Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
}
return drawable;
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
if (drawableMap.containsKey(urlString)) {
imageView.setImageDrawable(drawableMap.get(urlString));
}
final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}
更新:请注意,这个答案现在非常无效。垃圾回收器对SoftReference和WeakReference进行攻击,因此此代码不适用于新应用。(相反,请尝试其他答案中建议的通用图像加载器之类的库。)
感谢James提供的代码,感谢鲍龙提供的使用SoftReference的建议。我在James的代码上实现了SoftReference更改。不幸的是,SoftReferences导致我的图像被垃圾收集得太快。在我的情况下,没有SoftReference的东西很好,因为我的列表大小有限,图像也很小。
一年前有一场关于谷歌群组上SoftReferences的讨论:链接到线程。作为过早垃圾收集的解决方案,他们建议使用dalvik.system.VMRuntime.setMinimumHeapSize()手动设置VM堆大小,这对我来说不是很有吸引力。
public DrawableManager() {
drawableMap = new HashMap<String, SoftReference<Drawable>>();
}
public Drawable fetchDrawable(String urlString) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null)
return drawable;
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
drawableRef = new SoftReference<Drawable>(drawable);
drawableMap.put(urlString, drawableRef);
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
return drawableRef.get();
} catch (MalformedURLException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null) {
imageView.setImageDrawable(drawableRef.get());
return;
}
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
我一直在使用新的Android Volley Library com.Android.Volley.toolbox.NetworkImageView中的NetworkImageView,它似乎工作得很好。显然,这是Google Play和其他新的Google应用程序中使用的相同视图。绝对值得一看。
Google I/O 2013凌空图像缓存教程开发者Google活动