Не удается загрузить следующие данные с библиотекой подкачки Android

Я пытаюсь показать список журналов вызовов, используя Room-Paging-LiveData-ViewModel. Без подкачки кода мой код работает отлично. И я хочу использовать пейджинг также.

В моей базе данных у меня всего 25 записей журнала звонков. Первые 9 звонков отображаются в списке.

Отладкой я обнаружил, что при чтении данных в представлении модели через Daoвозвращает список размером 25. Но только первые 9 из них не равны нулю. Все остальные записи в списке пустые.

И метод наблюдения модели представления вызывается только один раз, только в первый раз.

Я думаю, что я делаю что-то не так.

Вот код ниже

Фрагмент

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();
}

ViewModel

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; 

Но я не нашел подходящего способа превратиться в это.

1 ответ

Для того, чтобы иметь возможность вернуть на карту PagedList ты должен знать что DataSource а также DataSource.Factory имеет map() а также mapByPage(), Вы можете сопоставить элементы фабрики источников данных с mapByPage() вместо того, чтобы использовать 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-й элемент в наборе данных или ноль, если он еще не загружен.

Без пустых заполнителей PagedList является подсписком данных, которые уже были загружены. Размер PagedList - это количество загруженных в данный момент элементов, а get (N) возвращает N-й загруженный элемент. Это не обязательно N-й элемент в наборе данных.

Заполнители включены по умолчанию, но их можно отключить двумя способами. Они отключены, если DataSource не считает свой набор данных при начальной загрузке или если false передано 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()

Для адаптера с выгружаемым списком следует отметить 2 вещи 1. Данные будут обрабатываться внутри, и нет необходимости объявлять какую-либо структуру данных для обработки данных вручную. 2. Существует метод по умолчанию, который называется submitList в PagedListAdapter, По этому методу необходимо передать постраничный список адаптеру.

Модифицированный адаптер

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);
Другие вопросы по тегам