Android使用Picasso加载网络图片到ListView时程序崩溃问题
问题核心原因
- 你将
lv.setAdapter(adapter)写在了遍历JSON数组的for循环内部,每添加一条数据就会触发ListView全量重绘,性能极差且会导致视图状态混乱。 - 你在Activity中直接调用
findViewById(R.id.imageViewLogoNews)查找列表项内的ImageView,这个方法只会找到当前布局树中第一个匹配ID的控件,并非ListView每一行对应的图片控件,此时列表项可能还未完成渲染,直接操作就会触发空指针或视图状态不匹配的崩溃。 - 原生
SimpleAdapter默认仅支持绑定本地drawable资源、资源ID类型的图片,无法直接识别网络URL字符串并自动加载图片,你把URL传入默认绑定逻辑必然会触发类型匹配错误。 - 你之前单独给非列表的普通ImageView用Picasso加载正常,是因为那个ImageView直接属于Activity布局层级,不属于ListView动态渲染的子项,不存在视图复用和查找错位的问题。
修复方案
- 把适配器设置逻辑移出for循环,等所有接口数据全部解析添加到列表集合后,再一次性绑定适配器。
- 废弃原生
SimpleAdapter,自定义继承自BaseAdapter的列表适配器,通过ViewHolder模式做列表项视图复用,在适配器的getView方法中绑定每一行的数据、加载对应图片。 - 不要在Activity层直接查找列表项内部的控件,所有列表项的控件查找、数据绑定逻辑都放到自定义适配器内部完成。
核心实现代码
自定义列表适配器
public class AnnonceAdapter extends BaseAdapter { private Context mContext; private ArrayList<HashMap<String, String>> mDataList; public AnnonceAdapter(Context context, ArrayList<HashMap<String, String>> dataList) { this.mContext = context; this.mDataList = dataList; } @Override public int getCount() { return mDataList.size(); } @Override public Object getItem(int position) { return mDataList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; // 复用列表项视图,避免反复inflate造成性能损耗 if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.listview_layout, parent, false); holder = new ViewHolder(); holder.titreTv = convertView.findViewById(R.id.titre_de); holder.descTv = convertView.findViewById(R.id.description_de); holder.refTv = convertView.findViewById(R.id.reference); holder.logoIv = convertView.findViewById(R.id.imageViewLogoNews); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 绑定当前行对应的数据 HashMap<String, String> itemData = mDataList.get(position); holder.titreTv.setText(itemData.get(titret)); holder.descTv.setText(itemData.get(descriptionet)); holder.refTv.setText(itemData.get(reft)); // 加载当前行对应网络图片 String imgUrl = itemData.get(url_imaget); Picasso.get() .load(imgUrl) .placeholder(R.mipmap.ic_launcher) // 可替换为你自己的加载占位图 .error(R.mipmap.ic_launcher) // 可替换为你自己的加载失败提示图 .into(holder.logoIv); return convertView; } // ViewHolder缓存控件实例,避免反复findViewById static class ViewHolder { TextView titreTv; TextView descTv; TextView refTv; ImageView logoIv; } }
修改后的接口回调逻辑
@Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { if (response.isSuccessful()) { myResponse = response.body().string(); Annonces.this.runOnUiThread(() -> { try { JSONObject reader = new JSONObject(myResponse); JSONArray eglises = reader.getJSONArray("Annonces_adv"); arrayList.clear(); for (int i = 0; i < eglises.length(); i++) { JSONObject eglise = eglises.getJSONObject(i); String titre = eglise.getString(titret); String description = eglise.getString(descriptionet); int ref = Integer.parseInt(eglise.getString(reft)); String url_image = eglise.getString(imaget); HashMap<String, String> data = new HashMap<>(); data.put(titret, titre); data.put(descriptionet, description); data.put(reft, String.valueOf(ref)); data.put(url_imaget, url_image); arrayList.add(data); } // 所有数据解析完成后再绑定适配器 AnnonceAdapter adapter = new AnnonceAdapter(Annonces.this, arrayList); lv.setAdapter(adapter); } catch (JSONException e) { e.printStackTrace(); } }); } }
额外检查项:
- 确认
AndroidManifest.xml中已经声明网络权限:<uses-permission android:name="android.permission.INTERNET" />- 如果你的图片URL是HTTP而非HTTPS,Android 9及以上系统需要配置网络安全策略允许明文流量,否则会被系统拦截导致图片加载失败。
内容的提问来源于stack exchange,提问作者tidave971
相关产品推荐
相关产品推荐

