Android中ListView图片懒加载的实现方法
技术背景
在Android开发中,当ListView需要显示大量图片时,一次性加载所有图片会导致内存占用过高,甚至出现OOM(OutOfMemory)错误,同时也会影响列表的滚动流畅性。图片懒加载技术可以在需要显示图片时再进行加载,从而优化内存使用和提升用户体验。
实现步骤
1. 自定义DrawableManager类
可以创建一个DrawableManager
类来管理图片的加载和缓存,以下是示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| import java.io.IOException; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.widget.ImageView; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient;
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); }
try { InputStream is = fetch(urlString); Drawable drawable = Drawable.createFromStream(is, "src");
if (drawable != null) { drawableMap.put(urlString, drawable); } return drawable; } catch (MalformedURLException e) { e.printStackTrace(); return null; } catch (IOException e) { e.printStackTrace(); 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() { Drawable drawable = fetchDrawable(urlString); Message message = handler.obtainMessage(1, drawable); handler.sendMessage(message); } }; thread.start(); }
private InputStream fetch(String urlString) throws MalformedURLException, IOException { HttpClient httpClient = new DefaultHttpClient(); HttpGet request = new HttpGet(urlString); HttpResponse response = httpClient.execute(request); return response.getEntity().getContent(); } }
|
2. 使用第三方库
- Picasso:
- 添加依赖:在
build.gradle
中添加以下代码:
1
| implementation 'com.squareup.picasso:picasso:(insert latest version)'
|
- **使用方法**:
1
| Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
|
- Glide:
- 添加依赖:在
build.gradle
中添加以下代码:
1 2 3 4 5 6 7 8 9
| repositories { mavenCentral() google() }
dependencies { implementation 'com.github.bumptech.glide:glide:4.11.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' }
|
- **使用方法**:
1
| Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);
|
- Fresco:
- 添加依赖:按照Fresco文档进行配置。
- 使用方法:参考Fresco的官方文档。
3. 手动实现懒加载
可以在ListView的适配器的getView
方法中,通过开启线程的方式在后台下载图片,并在图片下载完成后更新UI。以下是示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @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); final String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";
Thread pics_thread = new Thread(new Runnable() { @Override public void run() { Bitmap bitmap = getPicture(imageUrl); if(bitmap != null) { runOnUiThread(new Runnable() { @Override public void run() { imageview.setImageBitmap(bitmap); adapter.notifyDataSetChanged(); } }); } } }); pics_thread.start();
return v; }
|
最佳实践
- 使用缓存:将已经下载的图片进行缓存,避免重复下载,减少网络流量和加载时间。可以使用内存缓存(如
LruCache
)和磁盘缓存。 - 控制线程数量:避免创建过多的线程,以免影响性能。可以使用线程池来管理线程。
- 处理图片回收:在ListView滚动时,及时回收不再显示的图片,释放内存。
常见问题
- OOM错误:由于图片占用内存过大导致。可以通过压缩图片、使用缓存和及时回收图片等方式来解决。
- 图片加载闪烁:可能是由于图片重复加载或ListView滚动时图片回收不及时导致。可以通过在下载图片时检查ImageView是否已经被复用,以及使用占位图来解决。
- 网络问题:网络不稳定或图片地址错误可能导致图片加载失败。可以在代码中添加错误处理机制,如显示默认图片或重试下载。