如何通过Volley从JSON获取并排序分类与子分类数据并展示于RecyclerView
解决主分类与子分类的RecyclerView展示问题
首先,你的核心问题出在数据模型设计不合理和数据解析逻辑错误上,导致子分类无法正确关联到主分类,同时列表结构混乱。咱们一步步来解决:
1. 重构数据模型
把主分类和子分类拆成两个独立的类,让主分类类包含子分类列表,这样能清晰建立层级关系:
Category.java(主分类类)
public class Category { private String categoryId; private String categoryName; private String categoryImage; private List<Subcategory> subcategories; public Category() { subcategories = new ArrayList<>(); // 初始化子分类列表 } // 带参数构造器 public Category(String categoryId, String categoryName, String categoryImage) { this.categoryId = categoryId; this.categoryName = categoryName; this.categoryImage = categoryImage; this.subcategories = new ArrayList<>(); } // Getter & Setter public String getCategoryId() { return categoryId; } public void setCategoryId(String categoryId) { this.categoryId = categoryId; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } public String getCategoryImage() { return categoryImage; } public void setCategoryImage(String categoryImage) { this.categoryImage = categoryImage; } public List<Subcategory> getSubcategories() { return subcategories; } public void addSubcategory(Subcategory subcategory) { subcategories.add(subcategory); } }
Subcategory.java(子分类类)
public class Subcategory { private String subcategoryId; private String subcategoryName; public Subcategory() {} public Subcategory(String subcategoryId, String subcategoryName) { this.subcategoryId = subcategoryId; this.subcategoryName = subcategoryName; } // Getter & Setter public String getSubcategoryId() { return subcategoryId; } public void setSubcategoryId(String subcategoryId) { this.subcategoryId = subcategoryId; } public String getSubcategoryName() { return subcategoryName; } public void setSubcategoryName(String subcategoryName) { this.subcategoryName = subcategoryName; } }
2. 修正数据解析逻辑
原来的代码把子分类直接加到全局列表,还因为索引问题覆盖了主分类。现在我们要把每个子分类关联到对应的主分类,再统一管理主分类列表:
final JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.POST, url, jsonObject, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.d(TAG, response.toString()); try { JSONArray jArray = response.getJSONArray("data"); List<Category> categoryList = new ArrayList<>(); // 主分类列表 for (int i = 0; i < jArray.length(); i++) { JSONObject json_data = jArray.getJSONObject(i); // 创建主分类对象 Category category = new Category(); category.setCategoryId(json_data.getString("category_id")); category.setCategoryName(json_data.getString("category_name")); category.setCategoryImage(json_data.getString("category_image")); // 解析子分类并关联到主分类 JSONArray jArray1 = json_data.getJSONArray("subcategory"); for (int j = 0; j < jArray1.length(); j++) { JSONObject json_data1 = jArray1.getJSONObject(j); Subcategory subcategory = new Subcategory(); subcategory.setSubcategoryId(json_data1.getString("category_id")); subcategory.setSubcategoryName(json_data1.getString("category_name")); category.addSubcategory(subcategory); // 关联到主分类 } categoryList.add(category); // 把主分类加入列表 } // 解决数据降序问题:按分类名称升序排序(也可以按ID排序) Collections.sort(categoryList, new Comparator<Category>() { @Override public int compare(Category c1, Category c2) { return c1.getCategoryName().compareToIgnoreCase(c2.getCategoryName()); // 按ID排序用这个:return c1.getCategoryId().compareTo(c2.getCategoryId()); } }); // 初始化Adapter并绑定RecyclerView CategoryAdapter adapter = new CategoryAdapter(categoryList, getApplicationContext()); RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); recyclerView.setLayoutManager(mLayoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter); } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { VolleyLog.d(TAG, "Error: " + error.getMessage()); } });
3. 实现支持层级展示的RecyclerView Adapter
这里我们用多类型Item的方式,区分主分类和子分类的布局,也可以实现展开收起功能:
首先创建一个辅助类标记Item类型:
public class ListItem { public static final int TYPE_CATEGORY = 0; public static final int TYPE_SUBCATEGORY = 1; private int type; private Category category; private Subcategory subcategory; // 主分类Item构造 public ListItem(int type, Category category) { this.type = type; this.category = category; } // 子分类Item构造 public ListItem(int type, Subcategory subcategory) { this.type = type; this.subcategory = subcategory; } // Getter public int getType() { return type; } public Category getCategory() { return category; } public Subcategory getSubcategory() { return subcategory; } }
然后写Adapter:
public class CategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<ListItem> combinedList; private Context context; // 记录主分类的展开状态(默认都展开) private Map<String, Boolean> expandStatusMap = new HashMap<>(); public CategoryAdapter(List<Category> categoryList, Context context) { this.context = context; this.combinedList = new ArrayList<>(); // 把主分类和子分类合并到一个列表 for (Category category : categoryList) { combinedList.add(new ListItem(ListItem.TYPE_CATEGORY, category)); expandStatusMap.put(category.getCategoryId(), true); // 默认展开 // 如果展开,就添加子分类 if (expandStatusMap.get(category.getCategoryId())) { for (Subcategory sub : category.getSubcategories()) { combinedList.add(new ListItem(ListItem.TYPE_SUBCATEGORY, sub)); } } } } @Override public int getItemViewType(int position) { return combinedList.get(position).getType(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(context); if (viewType == ListItem.TYPE_CATEGORY) { View view = inflater.inflate(R.layout.item_category, parent, false); return new CategoryViewHolder(view); } else { View view = inflater.inflate(R.layout.item_subcategory, parent, false); return new SubcategoryViewHolder(view); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ListItem item = combinedList.get(position); if (item.getType() == ListItem.TYPE_CATEGORY) { CategoryViewHolder categoryHolder = (CategoryViewHolder) holder; Category category = item.getCategory(); categoryHolder.tvName.setText(category.getCategoryName()); // 加载图片用Glide/Picasso Glide.with(context).load(category.getCategoryImage()).into(categoryHolder.ivImage); // 展开收起点击事件 categoryHolder.itemView.setOnClickListener(v -> { boolean isExpanded = expandStatusMap.get(category.getCategoryId()); expandStatusMap.put(category.getCategoryId(), !isExpanded); // 更新列表 updateCombinedList(); notifyDataSetChanged(); }); } else { SubcategoryViewHolder subHolder = (SubcategoryViewHolder) holder; Subcategory subcategory = item.getSubcategory(); subHolder.tvSubName.setText(subcategory.getSubcategoryName()); } } @Override public int getItemCount() { return combinedList.size(); } // 更新合并列表,根据展开状态决定是否显示子分类 private void updateCombinedList() { combinedList.clear(); // 先重新获取所有主分类(这里可以优化,比如单独存主分类列表) List<Category> categoryList = new ArrayList<>(); for (ListItem item : combinedList) { if (item.getType() == ListItem.TYPE_CATEGORY) { categoryList.add(item.getCategory()); } } // 重新构建合并列表 for (Category category : categoryList) { combinedList.add(new ListItem(ListItem.TYPE_CATEGORY, category)); if (expandStatusMap.get(category.getCategoryId())) { for (Subcategory sub : category.getSubcategories()) { combinedList.add(new ListItem(ListItem.TYPE_SUBCATEGORY, sub)); } } } } // 主分类ViewHolder static class CategoryViewHolder extends RecyclerView.ViewHolder { TextView tvName; ImageView ivImage; public CategoryViewHolder(View itemView) { super(itemView); tvName = itemView.findViewById(R.id.tv_category_name); ivImage = itemView.findViewById(R.id.iv_category_image); } } // 子分类ViewHolder static class SubcategoryViewHolder extends RecyclerView.ViewHolder { TextView tvSubName; public SubcategoryViewHolder(View itemView) { super(itemView); tvSubName = itemView.findViewById(R.id.tv_subcategory_name); } } }
4. 布局文件示例
item_category.xml(主分类布局)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:orientation="horizontal" android:background="#f5f5f5"> <ImageView android:id="@+id/iv_category_image" android:layout_width="48dp" android:layout_height="48dp" android:scaleType="centerCrop"/> <TextView android:id="@+id/tv_category_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_gravity="center_vertical" android:textSize="18sp" android:textStyle="bold"/> </LinearLayout>
item_subcategory.xml(子分类布局)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:paddingStart="32dp"> <TextView android:id="@+id/tv_subcategory_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp"/> </LinearLayout>
这样修改后,你就能正确展示主分类和关联的子分类,同时解决数据降序的问题,还能实现展开收起的交互效果。
内容的提问来源于stack exchange,提问作者MohammadAli
相关产品推荐
相关产品推荐

