如何使用Volley在ListView中显示带标题描述的数据库图片?
用Volley实现ListView展示Base64图片、标题与描述的完整方案
嘿,我来帮你搞定这个需求!Volley本身就很适合处理这种网络请求+UI展示的场景,比你之前手动处理JSONObject更规范,还能帮你管理请求队列,下面是一步步的实现方法:
1. 先搞定基础准备
首先得确保你的项目配置正确:
- 给Manifest加上网络权限:
<uses-permission android:name="android.permission.INTERNET" />
- 在Gradle里添加Volley依赖(如果用AndroidX的话):
implementation 'com.android.volley:volley:1.2.1'
2. 创建数据模型类
之前你用HashMap存数据也能行,但用自定义模型类会让代码更清晰、易维护,比如定义一个Product类来封装每个条目的数据:
public class Product { private String title; private String description; private String imageBase64; public Product(String title, String description, String imageBase64) { this.title = title; this.description = description; this.imageBase64 = imageBase64; } // Getter方法,方便后续取数据 public String getTitle() { return title; } public String getDescription() { return description; } public String getImageBase64() { return imageBase64; } }
3. 用Volley发起网络请求获取数据
接下来用Volley的JsonObjectRequest去请求你的接口,解析返回的JSON数据成List<Product>,然后给ListView设置Adapter:
// 初始化Volley的请求队列,全局只需要初始化一次哦 RequestQueue requestQueue = Volley.newRequestQueue(this); // 替换成你的实际接口地址 String apiUrl = "https://your-api-url.com/products"; JsonObjectRequest jsonRequest = new JsonObjectRequest( Request.Method.GET, apiUrl, null, response -> { List<Product> productList = new ArrayList<>(); try { // 假设接口返回的JSON里,产品列表在"products"这个数组里,根据你的实际接口调整 JSONArray productsArray = response.getJSONArray("products"); for (int i = 0; i < productsArray.length(); i++) { JSONObject unProduit = productsArray.getJSONObject(i); // 取出标题、描述、Base64图片字符串 String title = unProduit.getString("title"); String description = unProduit.getString("description"); String imageBase64 = unProduit.getString("image"); // 添加到列表里 productList.add(new Product(title, description, imageBase64)); } // 数据解析完成,初始化Adapter并设置给ListView ProductAdapter adapter = new ProductAdapter(YourActivity.this, productList); yourListView.setAdapter(adapter); } catch (JSONException e) { e.printStackTrace(); Toast.makeText(YourActivity.this, "数据解析出错啦", Toast.LENGTH_SHORT).show(); } }, error -> { error.printStackTrace(); Toast.makeText(YourActivity.this, "网络请求失败了", Toast.LENGTH_SHORT).show(); } ); // 把请求加入队列,Volley会自动处理 requestQueue.add(jsonRequest);
4. 自定义ListView的Adapter
创建ProductAdapter,用ViewHolder模式优化性能,同时处理Base64图片的解码和展示:
public class ProductAdapter extends BaseAdapter { private Context context; private List<Product> productList; private LayoutInflater inflater; // 可选:添加图片缓存,避免重复解码,提升性能 private LruCache<String, Bitmap> imageCache; public ProductAdapter(Context context, List<Product> productList) { this.context = context; this.productList = productList; this.inflater = LayoutInflater.from(context); // 初始化图片缓存,用内存的1/8来存 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; imageCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount() / 1024; } }; } @Override public int getCount() { return productList.size(); } @Override public Object getItem(int position) { return productList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; // 复用convertView,避免重复创建视图 if (convertView == null) { convertView = inflater.inflate(R.layout.list_item_product, parent, false); holder = new ViewHolder(); holder.productImage = convertView.findViewById(R.id.iv_product_image); holder.productTitle = convertView.findViewById(R.id.tv_product_title); holder.productDesc = convertView.findViewById(R.id.tv_product_desc); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } Product currentProduct = productList.get(position); // 设置标题和描述 holder.productTitle.setText(currentProduct.getTitle()); holder.productDesc.setText(currentProduct.getDescription()); // 处理Base64图片:先从缓存取,没有再解码 String base64Image = currentProduct.getImageBase64(); Bitmap cachedBitmap = imageCache.get(base64Image); if (cachedBitmap != null) { holder.productImage.setImageBitmap(cachedBitmap); } else { try { byte[] decodedBytes = Base64.decode(base64Image, Base64.DEFAULT); Bitmap decodedBitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length); imageCache.put(base64Image, decodedBitmap); holder.productImage.setImageBitmap(decodedBitmap); } catch (IllegalArgumentException e) { e.printStackTrace(); // 解码失败时设置默认图片 holder.productImage.setImageResource(R.drawable.default_product_image); } } return convertView; } // ViewHolder类,缓存视图控件 static class ViewHolder { ImageView productImage; TextView productTitle; TextView productDesc; } }
5. 编写ListView的条目布局(list_item_product.xml)
最后写一个布局文件,定义每个列表条目的样式:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp" android:gap="12dp"> <ImageView android:id="@+id/iv_product_image" android:layout_width="80dp" android:layout_height="80dp" android:scaleType="centerCrop" android:background="@android:color/darker_gray" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" android:gap="8dp"> <TextView android:id="@+id/tv_product_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/tv_product_desc" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="@android:color/darker_gray" /> </LinearLayout> </LinearLayout>
额外小提示
- 如果你的Base64图片很大,在主线程解码可能会卡顿,建议用异步任务(比如Coroutines或者AsyncTask)来解码图片,避免阻塞UI线程。
- 记得在Activity销毁时,调用
requestQueue.cancelAll(this)来取消未完成的请求,防止内存泄漏。
内容的提问来源于stack exchange,提问作者sadi44
相关产品推荐
相关产品推荐

