5

我正在尝试使用 Room-Paging-LiveData-ViewModel 显示通话记录列表。无需分页,我的代码就可以完美运行。我也想使用分页。

在我的数据库中,我总共有 25 条通话记录。前 9 个通话记录显示在列表中。

通过调试,我发现在通过 读取视图模型中的数据时Dao,它返回大小为 25 的列表。但其中只有前 9 个是非空的。列表中的所有其他条目为空。

我希望空数据会尽快刷新,因为这是一个分页列表。但问题是 null 永远不会用有效数据刷新。

并且视图模型的观察方法只被调用一次,只有第一次。

我想我做错了什么。

这是下面的代码

片段

public class CallLogListFragment extends Fragment {
    private static final String TAG = "RecentCallsFragment";

    public static String getTAG() {
        return TAG;
    }

    public static Fragment newInstance() {
        return new CallLogListFragment();
    }

    public CallLogListFragment() {
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        FragmentCallLogListBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_call_log_list, container, false);
        CallLogListAdapter adapter = new CallLogListAdapter();
        binding.list.setAdapter(adapter);
        CallLogListViewModel model = ViewModelProviders.of(this).get(CallLogListViewModel.class);
        model.getCallLogList().observe(this, adapter::refreshData);
        return binding.getRoot();
    }

}

适配器

public class CallLogListAdapter extends PagedListAdapter<CallLogItem, CallLogListAdapter.ViewHolder> {
    CallLogListAdapter() {
        super(DIFF_CALLBACK);
    }

    void refreshData(List<CallLogItem> data) {
        DiffUtil.DiffResult calculatedDiff = DiffUtil.calculateDiff(new CallLogListDiffUtilCallBack(this.data, data));
        this.data.clear();
        this.data.addAll(data);
        calculatedDiff.dispatchUpdatesTo(this);
    }

    private List<CallLogItem> data = new ArrayList<>();

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(DataBindingUtil.inflate(
                LayoutInflater.from(parent.getContext()),
                R.layout.call_log_list_single_item,
                parent, false
        ));
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        CallLogItem item = data.get(position);
        holder.binding.setCallLog(item);
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        public CallLogListSingleItemBinding binding;
        public ViewHolder(@NonNull CallLogListSingleItemBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }
    }

     private static DiffUtil.ItemCallback<CallLogItem> DIFF_CALLBACK =
        new DiffUtil.ItemCallback<CallLogItem>() {
            @Override
            public boolean areItemsTheSame(CallLogItem oldItem, CallLogItem newItem) {
                return oldItem.getHeaderDateVisibility() == newItem.getHeaderDateVisibility()
                        && oldItem.getCallId().equals(newItem.getCallId());
            }

            @Override
            public boolean areContentsTheSame(@NonNull CallLogItem oldItem, @NonNull CallLogItem newItem) {
                return areItemsTheSame(oldItem, newItem);
            }
        };
}

@Dao
public interface CallLogDao extends BaseDao<CallLog>{
    @Query("SELECT * FROM log")
    List<CallLog> getAll();

    @Query("SELECT * FROM log WHERE number=:number")
    CallLog findByName(String number);

    @Query("SELECT * FROM log order by date desc")
    LiveData<List<CallLog>> getAllLive();

    @Query("SELECT * FROM log order by date desc")
    DataSource.Factory<Integer, CallLog> getAllLivePaged();
}

视图模型

public class CallLogListViewModel extends ViewModel {
    private LiveData<List<CallLogItem>> callLogList;

    public CallLogListViewModel() {
        callLogList = Transformations.map(new LivePagedListBuilder<>(AppDatabase.get().callLogDao().getAllLivePaged(), 3).build(), input -> {
            List<CallLogItem> list = new ArrayList<>();
            for (int i = 0; i < input.size(); i++) {
                boolean isHeader = true;
                CallLog callLog = input.get(i);
                if(callLog!=null) {
                    if (i > 0) {
                        CallLog previousCallLog = input.get(i - 1);
                        if(previousCallLog!=null) {
                            isHeader = TimeFormat.isDifferentDate(callLog.date, previousCallLog.date);
                        }
                    }
                    list.add(CallLogItem.Companion.from(callLog, isHeader));
                }
            }
            return list;
        });
    }

    LiveData<List<CallLogItem>> getCallLogList() {
        return callLogList;
    }
}

后来我试着做

private LiveData<List<CallLogItem>> callLogList; 

到分页列表,如

private LiveData<PagedList<CallLogItem>> callLogList; 

但是我没有找到合适的方法来转变为那个。

4

2 回答 2

3

为了能够返回映射PagedList,您应该知道DataSourceand DataSource.Factoryhas map()and mapByPage()mapByPage()您可以使用映射 DataSource Factory 项目Transformation,如下所示:

DataSource.Factory<Integer, CallLog> dataSourceFactoryCallLog = AppDatabase.get().callLogDao().getAllLivePaged();
                
DataSource.Factory<Integer, CallLogItem> dataSourceFactoryCallLogItem = dataSourceFactoryCallLog.mapByPage(input -> {
   List<CallLogItem> list = new ArrayList<>();
   for (int i = 0; i < input.size(); i++) {
       boolean isHeader = true;
       CallLog callLog = input.get(i);
       if(callLog!=null) {
           if (i > 0) {
               CallLog previousCallLog = input.get(i - 1);
               if(previousCallLog!=null) {
                   isHeader = TimeFormat.isDifferentDate(callLog.date, previousCallLog.date);
               }
           }
           list.add(CallLogItem.Companion.from(callLog, isHeader));
       }
   }
   return list;
 });
        
LiveData<PagedList<CallLogItem>> callLogItems = new LivePagedListBuilder<>(dataSourceFactoryCallLogItem, 3).build()

编辑

根据PagedList 文档

使用占位符,PagedList 始终是数据集的完整大小。get(N) 返回数据集中的第 N 个项目,如果尚未加载,则返回 null。

如果没有空占位符,PagedList 是已加载数据的子列表。PagedList 的大小是当前加载项的数量,get(N) 返回第 N 个加载项。这不一定是数据集中的第 N 项。

占位符默认启用,但可以通过两种方式禁用。如果 DataSource 未在其初始加载中计算其数据集,或者如果setEnablePlaceholders(boolean)在构建PagedList.Config.

您只需要创建一个PagedList.Config并将其添加到LivePagedListBuilder实例化中。

PagedList.Config pagedListConfig =
                (new PagedList.Config.Builder())
                        .setEnablePlaceholders(false)
                        .setPageSize(3).build();

LiveData<PagedList<CallLogItem>> callLogItems = new LivePagedListBuilder<>(dataSourceFactoryCallLogItem, pagedListConfig).build()
于 2018-11-14T11:47:58.400 回答
2

对于分页列表适配器,有两件事需要注意。1. 内部处理数据,无需手动声明任何数据结构来处理数据。2.有一个默认方法调用submitListPagedListAdapter有必要通过该方法向适配器提交分页列表。

改装适配器

public class CallLogListAdapter extends PagedListAdapter<CallLogItem, CallLogListAdapter.ViewHolder> {
    private Context context;
    CallLogListAdapter(Context context) {
        super(DIFF_CALLBACK);
        this.context = context;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(DataBindingUtil.inflate(
                LayoutInflater.from(parent.getContext()),
                R.layout.call_log_list_single_item,
                parent, false
        ));
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        CallLogItem item = getItem(position);
        if (item != null) {
            holder.binding.setCallLog(item);
            ImageUtil.setImage(holder.binding.ivProfileImage, item.getImageUrl(), item.getName());
        } else {
            holder.binding.invalidateAll();
        }
    }


    class ViewHolder extends RecyclerView.ViewHolder {
        public CallLogListSingleItemBinding binding;

        public ViewHolder(@NonNull CallLogListSingleItemBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }
    }

    private static DiffUtil.ItemCallback<CallLogItem> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<CallLogItem>() {
                @Override
                public boolean areItemsTheSame(CallLogItem oldItem, CallLogItem newItem) {
                    return oldItem.getHeaderDateVisibility() == newItem.getHeaderDateVisibility()
                            && oldItem.getCallId()!=null &&  oldItem.getCallId().equals(newItem.getCallId());
                }

                @Override
                public boolean areContentsTheSame(@NonNull CallLogItem oldItem, @NonNull CallLogItem newItem) {
                    return areItemsTheSame(oldItem, newItem);
                }
            };
}

修改后的数据传递给适配器

CallLogListViewModel model = ViewModelProviders.of(this).get(CallLogListViewModel.class);
model.getCallLogList().observe(this, adapter::submitList);
于 2018-12-02T07:38:17.730 回答