Android Retrofit2实现分类子分类可展开RecyclerView问题求助
解决可展开RecyclerView的子分类显示问题
我帮你排查了代码里的几个关键问题,咱们一步步梳理并解决:
问题核心分析
从你的代码和当前效果来看,主要有三个问题导致子分类显示异常:
- 子分类循环时的下标错误,导致取到错误数据甚至可能触发数组越界
- 实体类设计混淆了父、子分类的职责,字段冗余且逻辑混乱
- 数据存储没有建立父分类与对应子分类的关联,Adapter无法正确映射父-子关系
分步解决方案
1. 先修复最紧急的下标错误
在Activity的子分类循环中,你错误地用了父分类的循环变量i来获取子分类,应该用当前子分类的循环变量j:
// 错误代码 categories.setId(sub.get(i).getSub_id()); categories.setSub_title(sub.get(i).getSub_title()); categories.setSub_img(sub.get(i).getSub_img()); // 正确代码 categories.setId(sub.get(j).getSub_id()); categories.setSub_title(sub.get(j).getSub_title()); categories.setSub_img(sub.get(j).getSub_img());
2. 重构实体类,分离父/子分类职责
同一个Categories类同时承载父、子分类字段会导致逻辑混乱,建议拆分成三个清晰的类:
父分类实体类 ParentCategory
public class ParentCategory { @SerializedName("id") @Expose private String id; @SerializedName("title") @Expose private String title; @SerializedName("img") @Expose private String img; @SerializedName("sub_cats") @Expose private List<SubCategory> subCats = null; // 生成对应的getter和setter方法 }
子分类实体类 SubCategory
public class SubCategory { @SerializedName("sub_id") @Expose private String subId; @SerializedName("sub_title") @Expose private String subTitle; @SerializedName("sub_img") @Expose private String subImg; // 生成对应的getter和setter方法 }
根响应实体类 CategoryResponse
public class CategoryResponse { @SerializedName("cats") @Expose private List<ParentCategory> cats = null; @SerializedName("message") @Expose private String message; @SerializedName("success") @Expose private Integer success; // 生成对应的getter和setter方法 }
同时更新Retrofit的API方法:
@GET("cats.php") Call<CategoryResponse> getCategories();
3. 调整数据处理逻辑,建立父-子关联
不再把父、子分类存到两个独立列表,而是直接使用包含子分类的父分类列表,让Adapter能直接获取每个父项对应的子项:
Call<CategoryResponse> callCats = RetrofitBuilder.getInstance().getApi().getCategories(); callCats.enqueue(new Callback<CategoryResponse>() { @Override public void onResponse(Call<CategoryResponse> call, Response<CategoryResponse> response) { CategoryResponse categoryResponse = response.body(); if (categoryResponse != null && categoryResponse.getSuccess() == 1){ List<ParentCategory> parentCategories = categoryResponse.getCats(); // 直接传入包含子分类的父分类列表 ExpandableRecyclerViewAdapter adapter = new ExpandableRecyclerViewAdapter(getApplicationContext(), parentCategories); expanderRecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext())); expanderRecyclerView.setAdapter(adapter); } } @Override public void onFailure(Call<CategoryResponse> call, Throwable t) { Toast.makeText(BuyerDashboard.this, t.getMessage(), Toast.LENGTH_SHORT).show(); } });
4. 适配可展开RecyclerView的Adapter逻辑
调整ExpandableRecyclerViewAdapter,使其接收父分类列表,并管理展开/折叠状态:
public class ExpandableRecyclerViewAdapter extends RecyclerView.Adapter<ExpandableRecyclerViewAdapter.ParentViewHolder> { private Context context; private List<ParentCategory> parentCategoryList; // 记录每个父分类的展开状态 private Map<Integer, Boolean> expandStatusMap = new HashMap<>(); public ExpandableRecyclerViewAdapter(Context context, List<ParentCategory> parentCategoryList) { this.context = context; this.parentCategoryList = parentCategoryList; // 初始化所有父分类为折叠状态 for (int i = 0; i < parentCategoryList.size(); i++) { expandStatusMap.put(i, false); } } @NonNull @Override public ParentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.cat_items, parent, false); return new ParentViewHolder(view); } @Override public void onBindViewHolder(@NonNull ParentViewHolder holder, int position) { ParentCategory parent = parentCategoryList.get(position); holder.tvParentTitle.setText(parent.getTitle()); // 根据状态切换子RecyclerView显示/隐藏 boolean isExpanded = expandStatusMap.get(position); holder.subRecyclerView.setVisibility(isExpanded ? View.VISIBLE : View.GONE); // 初始化子分类Adapter InnerRecyclerViewAdapter subAdapter = new InnerRecyclerViewAdapter(context, parent.getSubCats()); holder.subRecyclerView.setLayoutManager(new LinearLayoutManager(context)); holder.subRecyclerView.setAdapter(subAdapter); // 父项点击事件:切换展开状态 holder.itemView.setOnClickListener(v -> { expandStatusMap.put(position, !isExpanded); notifyItemChanged(position); }); } @Override public int getItemCount() { return parentCategoryList.size(); } public static class ParentViewHolder extends RecyclerView.ViewHolder { TextView tvParentTitle; RecyclerView subRecyclerView; public ParentViewHolder(@NonNull View itemView) { super(itemView); tvParentTitle = itemView.findViewById(R.id.tv_cat_title); subRecyclerView = itemView.findViewById(R.id.sub_recycler_view); } } }
对应的子分类Adapter也要适配SubCategory类:
public class InnerRecyclerViewAdapter extends RecyclerView.Adapter<InnerRecyclerViewAdapter.SubViewHolder> { private Context context; private List<SubCategory> subCategoryList; public InnerRecyclerViewAdapter(Context context, List<SubCategory> subCategoryList) { this.context = context; this.subCategoryList = subCategoryList; } @NonNull @Override public SubViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.sub_cat_items, parent, false); return new SubViewHolder(view); } @Override public void onBindViewHolder(@NonNull SubViewHolder holder, int position) { SubCategory sub = subCategoryList.get(position); holder.tvSubTitle.setText(sub.getSubTitle()); // 后续可以添加Glide/Picasso加载图片的逻辑 } @Override public int getItemCount() { return subCategoryList.size(); } public static class SubViewHolder extends RecyclerView.ViewHolder { TextView tvSubTitle; public SubViewHolder(@NonNull View itemView) { super(itemView); tvSubTitle = itemView.findViewById(R.id.tv_sub_cat_title); } } }
额外优化建议
- 确保
cat_items.xml中的子RecyclerView初始状态设为android:visibility="gone" - 可以给父分类标题添加展开/折叠图标(比如向上/向下箭头),提升用户体验
- 图片加载建议使用Glide或Picasso库,避免直接加载网络图片导致的性能问题
内容的提问来源于stack exchange,提问作者Hamza Afzal
相关产品推荐
相关产品推荐

