【问题标题】:How to make multiple view type in RecyclerView using two data models?如何使用两个数据模型在 RecyclerView 中创建多个视图类型?
【发布时间】:2020-02-26 12:24:10
【问题描述】:

我有使用 Retrofit 库从 API 获得的数据。 API 运行良好。问题是如何将数据放入列表并使用 RecyclerView 适配器显示它。我尝试使用 HashMap 但它会使应用程序崩溃。有人可以帮我展示这个问题的正确结构和解决方案吗?

来自 API 的数据

报表模型

报表列表中的记录模型

我已经从 API 获得的数据

我要显示的界面

【问题讨论】:

    标签: java android api android-recyclerview retrofit


    【解决方案1】:

    我建议你展平模型。与其让父母带着孩子,不如只有一个层次。此外,为您的 UI 和 Retrofit 使用不同的数据模型。需要时,在模型之间进行转换。这样,事情就不会紧密耦合,您可以在隐蔽时做其他事情,例如如果需要,格式化字符串。

    // This is what the data model will extend
    interface UiBaseModel { }
    
    // monthly detail report
    class UiMonthlyDetailReport implements UiBaseModel {
    
        private String month;
        private String total;
    
        UiMonthlyDetailReport(String month, String total) {
            this.month = month;
            this.total = total;
        }
    
        public String getMonth() {
            return month;
        }
    
        public String getTotal() {
            return total;
        }
    }
    
    // monthly detail record
    class UiMonthlyDetailsRecord implements UiBaseModel {
    
        private String month;
        private String expenseName;
        private String expenseIcon;
        private String expenseTotal;
    
        UiMonthlyDetailsRecord(String month, String expenseName, String expenseIcon, String expenseTotal) {
            this.month = month;
            this.expenseName = expenseName;
            this.expenseIcon = expenseIcon;
            this.expenseTotal = expenseTotal;
        }
    
        public String getMonth() {
            return month;
        }
    
        public String getExpenseName() {
            return expenseName;
        }
    
        public String getExpenseIcon() {
            return expenseIcon;
        }
    
        public String getExpenseTotal() {
            return expenseTotal;
        }
    }
    
    
    
    // In your activity / fragment / viewmodel / presenter:
    
        List<ReportMonthlyDetailsReport> reports = ... // get your data from retrofit
        List<UiBaseModel> uiModels = ArrayList<UiBaseModel>();
        for (ReportMonthlyDetailsReport report : reports) {
            // First records, then report
            List<ReportMonthlyDetailsRecord> records = report.getRecords();
            for (ReportMonthlyDetailsRecord record : records) {
                // Create UI model and add to list
                UiMonthlyDetailsRecord uiRecord = UiMonthlyDetailsRecord(record.getMonth_label(), record.getExpense_type_name(), record.getExpense_type_icon(), record.getExpense_type_total());
                uiModels.add(uiRecord);
            }
            // Lastly, add the report
            UiMonthlyDetailsReport uiReport = UiMonthlyDetailsReport(report.getMonth_label(), report.getMonthly_total());
            uiModels.add(uiReport);
        }
    
        // Use the ui models (for the recycler adapter)
        MonthlyDetailsAdapter adapter = MonthlyDetailsAdapter(uiModels);
        recyclerView.adapter = adapter;
    

    剩下要做的就是适配器。有很多关于如何支持多种视图类型的例子,简单的未完成的例子:

    public class MonthlyDetailsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        private final static int VIEW_TYPE_RECORD = 0;
        private final static int VIEW_TYPE_REPORT = 1;
    
        private List<UiBaseModel> data;
    
        MonthlyDetailsAdapter(List<UiBaseModel> data) {
            this.data = data;
        }
    
        class RecordViewHolder extends RecyclerView.ViewHolder {
            public RecordViewHolder(@NonNull View itemView) {
                // code
                super(itemView);
            }
        }
    
        class ReportViewHolder extends RecyclerView.ViewHolder {
            public ReportViewHolder(@NonNull View itemView) {
                // code
                super(itemView);
            }
        }
    
        @Override
        public int getItemViewType(int position) {
            UiBaseModel item = data.get(position);
            if (item instanceof UiMonthlyDetailsRecord) {
                return VIEW_TYPE_RECORD;
            } else {
                return VIEW_TYPE_REPORT;
            }
        }
    
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            if (holder instanceof RecordViewHolder) {
                UiMonthlyDetailsRecord record = (UiMonthlyDetailsRecord) data.get(position);
                // bind record
            } else if (holder instanceof ReportViewHolder) {
                UiMonthlyDetailsReport report = (UiMonthlyDetailsReport) data.get(position);
                // bind report
            }
        }
    }
    

    【讨论】:

      【解决方案2】:
      1. 覆盖 getItemViewType(position: Int) 并使用您的不同模型返回不同的类型。
      2. 覆盖onCreateViewHolder(parent: ViewGroup, viewType: Int) ,可以根据itemType创建itemViewHolder。
      3. 覆盖onBindViewHolder(holder: BaseViewHolder, position: Int),你可以显示你需要的。记得判断项目的itemType

      一般来说,你需要根据你的model创建不同的itemtype,然后创建不同的viewholder,最后用你的model显示viewholder

      【讨论】:

      • 问题是如何区分getItemViewType中的CHILD和PARENT?私有列表 dataTotal; @Override public int getItemViewType(int position) { if(TextUtils.isEmpty(dataTotal.get(position).getMonthly_total())) { return TYPE_PARENT; } 其他 { 返回 TYPE_CHILD; } } 在 List 中,还有另一个记录列表,它是报告列表的子项
      • #### 您可以创建一个包装实体并创建一个类似List&lt;Wrapper&gt; 的列表。 data class Wrapper( val records : YourEntity, val report : YourEntity, val itemType :Int 1. 将所有记录实体和报告信息放入列表中,如report - records -records -records -report -records -records ,需要在包装实体中设置itemType。 2.使用wrapper重写itemType方法。 3.根据wrapper实体的itemType显示视图,如果itemType是report,则只需要在wrapper实体中获取report数据即可。
      • 已经放了包装。运行 Activity 时应用崩溃
      猜你喜欢
      • 1970-01-01
      • 2016-11-04
      • 1970-01-01
      • 1970-01-01
      • 2018-07-30
      • 1970-01-01
      • 2013-01-12
      • 2014-12-02
      相关资源
      最近更新 更多