Android ListView通过Volley从数据库动态加载数据但无条目显示问题排查求助
我尝试用Volley从数据库拉取数据动态填充ListView,已经确认Volley返回了正确的数据,但ListView始终没有内容。我的实现步骤如下:
1. OrderRetriever类(获取订单数据)
public class OrderRetriever { private final Context d; String user, token; private OrderListViewAdapter orderAdapter; final ArrayList<Order> myOrders = new ArrayList<>(); public OrderRetriever(Context d, String user, String token) { this.d = d; this.user = user; this.token = token; } public void retrieveOrder(final ListView listView, final KProgressHUD myProgressBar){ myProgressBar.show(); StringRequest serviceStringRequest = new StringRequest(Request.Method.POST, NetworkConstants.URL_MY_ORDERS, response -> { JSONObject jsonObject,photoObject; Order order; try{ JSONObject productObject = new JSONObject(response); JSONArray productArray = productObject.getJSONArray("orders"); JSONArray photoArray = productObject.getJSONArray("photos"); if(productArray.length()>0){ String image = "", name, code,status; int id; double cost; for(int i=0; i<productArray.length();i++){ jsonObject = productArray.getJSONObject(i); id= jsonObject.getInt("id"); name = jsonObject.getString("name"); status = jsonObject.getString("status"); code = jsonObject.getString("code"); cost = jsonObject.getDouble("cost"); if(photoArray.length()>0){ for(int j=0; j<photoArray.length(); j++) { photoObject = photoArray.getJSONObject(j); if(photoObject.getInt("p_id")==jsonObject.getInt("id")) { image = photoObject.getString("path"); } } } order= new Order(name, status, code,NetworkConstants.URL_IMAGE +image,id,cost); Log.d("Products", "retrieveFinalOrder: "+order); myOrders.add(order); } }else{ myProgressBar.dismiss(); Toasty.warning(d,"No Orders found", 5000).show(); } Log.d("TAG", "retrieveOrder: "); orderAdapter = new OrderListViewAdapter(d,myOrders); Log.d("TAG", "retrieveOrder: ading adapter"); listView.setAdapter(orderAdapter); myProgressBar.dismiss(); }catch (JSONException e){ myProgressBar.dismiss(); e.printStackTrace(); } }, error -> { myProgressBar.dismiss(); Log.d("Error", "Failed with error msg:\t" + error.getMessage()); Log.d("Error", "Error StackTrace: \t" + Arrays.toString(error.getStackTrace())); // edited here try { byte[] htmlBodyBytes = error.networkResponse.data; Log.e("Error", new String(htmlBodyBytes), error); } catch (NullPointerException e) { e.printStackTrace(); } Toast.makeText(d,"Error " + error, Toast.LENGTH_SHORT).show(); }){ protected Map<String, String> getParams() { Map<String, String> params = new HashMap<>(); params.put("user",user); params.put("api_token", token); return params; } }; serviceStringRequest.setRetryPolicy(new DefaultRetryPolicy( 1000*5, /*DefaultRetryPolicy.DEFAULT_MAX_RETRIES*/ 3, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); RequestQueue productRequestQue = Volley.newRequestQueue(d); productRequestQue.add(serviceStringRequest); } }
2. OrderListViewAdapter适配器
public class OrderListViewAdapter extends BaseAdapter { Context c; ArrayList<Order> orders; public OrderListViewAdapter(Context c, ArrayList<Order> orders) { this.c = c; this.orders = orders; } @Override public int getCount() { return orders.size(); } @Override public Object getItem(int position) { return orders.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View view, ViewGroup viewGroup) { if(view==null){ view = LayoutInflater.from(c).inflate(R.layout.order_list_layout,null,false); } TextView name = view.findViewById(R.id.name); TextView cost = view.findViewById(R.id.cost); TextView status = view.findViewById(R.id.status); ImageView image = view.findViewById(R.id.image); final Order order = (Order) this.getItem(position); name.setText(order.getName()); cost.setText("KSH " + order.getCost()); status.setText(order.getStatus()); //check if there is an image returned if(order.getImage()!= null && order.getImage().length()>0){ Picasso.with(c).load(order.getImage()).placeholder(R.drawable.drawerback).into(image); }else{ Picasso.with(c).load(R.drawable.drawerback).into(image); } view.setOnClickListener(v -> { Intent intent = new Intent(c, ProductActivity.class); intent.putExtra("string_name","string_to_pass"); c.startActivity(intent); }); return view; } }
3. Order数据模型
public class Order { private String name, status,code,image; private int id; double cost; public Order(String name, String status, String code, String image, int id, double cost) { this.name = name; this.status = status; this.code = code; this.image = image; this.id = id; this.cost = cost; } }
4. 在OrdersActivity中调用
container = findViewById(R.id.orderContainer); new OrderRetriever(this, clientid, new Db(this).getToken()).retrieveOrder(container,myProgressBar);
明明Volley返回了正确数据,可ListView就是没内容,我哪里漏了或者错了?
我帮你梳理几个大概率的问题点,都是Android开发中填充ListView时容易踩的坑:
核心问题:Order类缺少getter方法
你看适配器里调用了order.getName()、order.getCost()这些方法,但你的Order模型只写了构造方法,完全没定义这些getter!适配器根本拿不到数据,自然显示空内容。赶紧给Order类补上所有属性的getter方法:public String getName() { return name; } public String getStatus() { return status; } public String getCode() { return code; } public String getImage() { return image; } public int getId() { return id; } public double getCost() { return cost; }这个应该是最关键的问题,甚至奇怪你编译的时候没报错?可能是IDE的提示被忽略了,补上之后应该就能正常显示了。
次要排查点:ListView布局设置
检查你的order_list_layout布局文件,是不是根布局高度设成wrap_content但内部控件没有正确填充?或者ListView本身在父布局中的高度/权重设置错误,导致它的显示区域为0?另外注意不要把ListView嵌套在ScrollView里,这会导致ListView无法正确计算高度,从而显示为空。优化建议:适配器数据刷新方式
你现在是每次拿到数据后新建一个适配器,虽然理论上可行,但更稳妥的方式是提前初始化适配器并绑定数据源,拿到数据后只更新集合再通知适配器刷新。比如在OrderRetriever里提前初始化orderAdapter = new OrderListViewAdapter(d, myOrders);,然后在循环添加完订单后调用orderAdapter.notifyDataSetChanged(),这样能避免一些线程或上下文导致的刷新问题。调试辅助:确认Order对象数据
你打了Log.d("Products", "retrieveFinalOrder: "+order);,可以看看Log里的输出是不是对象的内存地址(比如com.xxx.Order@xxxx),如果是,说明Order类没重写toString()方法,虽然这不影响ListView显示,但重写后能更直观地看到订单数据是否正确:@Override public String toString() { return "Order{" + "name='" + name + '\'' + ", status='" + status + '\'' + ", cost=" + cost + '}'; }
先把getter方法补上,这应该能解决你的问题,如果还有问题再排查布局和适配器刷新的问题~
内容的提问来源于stack exchange,提问作者The Only Smart Boy

