下面列出了android.support.v7.widget.RecyclerView.AdapterDataObserver#android.support.v7.util.ListUpdateCallback 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Test
public void shouldNotApplyPresenter1ChangesWhenNotObservedUntilGetItemCount() {
when(mockPresenter1.getItemId(REPOSITORY_VALUE_B, 2)).thenReturn(10L);
repositoryAdapter.unregisterAdapterDataObserver(redirectingObserver);
repository1.accept(REPOSITORY_VALUE_B);
runUiThreadTasksIncludingDelayedTasks();
verify(mockPresenter1, never()).getItemCount(any());
final int itemCount = repositoryAdapter.getItemCount();
verify(mockPresenter1, never()).getItemCount(REPOSITORY_VALUE_A);
verify(mockPresenter1).getItemCount(REPOSITORY_VALUE_B);
assertThat(itemCount, is(
PRESENTER_1_B_ITEM_COUNT + STATIC_ITEM_COUNT + PRESENTER_2_A_ITEM_COUNT));
assertThat(repositoryAdapter.getItemId(2), is(10L + STATIC_ITEM_COUNT));
verify(mockPresenter1, never()).getUpdates(any(), any(), any(ListUpdateCallback.class));
}
@Test
public void shouldNotApplyPresenter2ChangesEvenWhenObservedUntilGetItemCount() {
when(mockPresenter2.getItemId(REPOSITORY_VALUE_B, 4)).thenReturn(100L);
repository2.accept(REPOSITORY_VALUE_B);
runUiThreadTasksIncludingDelayedTasks();
verify(mockPresenter1, never()).getItemCount(any());
final int itemCount = repositoryAdapter.getItemCount();
verify(mockPresenter2, never()).getItemCount(REPOSITORY_VALUE_A);
verify(mockPresenter2).getItemCount(REPOSITORY_VALUE_B);
assertThat(itemCount, is(
PRESENTER_1_A_ITEM_COUNT + STATIC_ITEM_COUNT + PRESENTER_2_B_ITEM_COUNT));
assertThat(repositoryAdapter.getItemId(PRESENTER_1_A_ITEM_COUNT + STATIC_ITEM_COUNT + 4),
is(100L + STATIC_ITEM_COUNT));
verify(mockPresenter2, never()).getUpdates(any(), any(), any(ListUpdateCallback.class));
}
@Test
public void shouldApplyPresenter1ChangesWithEventsWhenObserved() {
repositoryAdapter.getItemCount(); // usage
when(mockPresenter1.getItemId(REPOSITORY_VALUE_B, 2)).thenReturn(10L);
repository1.accept(REPOSITORY_VALUE_B);
runUiThreadTasksIncludingDelayedTasks();
assertThat(repositoryAdapter.getItemCount(), is(
PRESENTER_1_B_ITEM_COUNT + STATIC_ITEM_COUNT + PRESENTER_2_A_ITEM_COUNT));
assertThat(repositoryAdapter.getItemId(2), is(10L + STATIC_ITEM_COUNT));
verify(mockPresenter1).getUpdates(eq(REPOSITORY_VALUE_A), eq(REPOSITORY_VALUE_B),
any(ListUpdateCallback.class));
applyPresenter1FromAToBChanges(verifyingWrapper(fineGrainedEvents));
verifyNoMoreInteractions(fineGrainedEvents);
verify(onChangeEvent, never()).run();
}
@Test
public void shouldApplyPresenter2ChangesWithEventsWhenObserved() {
repositoryAdapter.getItemCount(); // usage
when(mockPresenter2.getItemId(REPOSITORY_VALUE_B, 4)).thenReturn(100L);
repository2.accept(REPOSITORY_VALUE_B);
runUiThreadTasksIncludingDelayedTasks();
assertThat(repositoryAdapter.getItemCount(), is(
PRESENTER_1_A_ITEM_COUNT + STATIC_ITEM_COUNT + PRESENTER_2_B_ITEM_COUNT));
assertThat(repositoryAdapter.getItemId(PRESENTER_1_A_ITEM_COUNT + STATIC_ITEM_COUNT + 4),
is(100L + STATIC_ITEM_COUNT));
verify(mockPresenter2).getUpdates(eq(REPOSITORY_VALUE_A), eq(REPOSITORY_VALUE_B),
any(ListUpdateCallback.class));
applyPresenter2FromAToBChanges(PRESENTER_1_A_ITEM_COUNT + STATIC_ITEM_COUNT,
verifyingWrapper(fineGrainedEvents));
verifyNoMoreInteractions(fineGrainedEvents);
verify(onChangeEvent, never()).run();
}
@Test
public void shouldAskBothPresentersOnAdditionalObservableUpdate() {
repository1.accept(REPOSITORY_VALUE_B);
repository2.accept(REPOSITORY_VALUE_B);
runUiThreadTasksIncludingDelayedTasks();
repositoryAdapter.getItemCount(); // usage
verify(mockPresenter1).getItemCount(REPOSITORY_VALUE_B); // consume method call
verify(mockPresenter2).getItemCount(REPOSITORY_VALUE_B); // consume method call
updateDispatcher.update();
runUiThreadTasksIncludingDelayedTasks();
repositoryAdapter.getItemCount();
verify(mockPresenter1).getUpdates(eq(REPOSITORY_VALUE_B), eq(REPOSITORY_VALUE_B),
any(ListUpdateCallback.class));
verifyNoMoreInteractions(mockPresenter1); // should not have called getItemCount() again
verify(mockPresenter2).getUpdates(eq(REPOSITORY_VALUE_B), eq(REPOSITORY_VALUE_B),
any(ListUpdateCallback.class));
verifyNoMoreInteractions(mockPresenter2); // should not have called getItemCount() again
applyPresenter1RefreshBChanges(verifyingWrapper(fineGrainedEvents));
applyPresenter2RefreshBChanges(PRESENTER_1_B_ITEM_COUNT + STATIC_ITEM_COUNT,
verifyingWrapper(fineGrainedEvents));
verifyNoMoreInteractions(fineGrainedEvents);
verify(onChangeEvent, never()).run();
}
@Override
boolean getUpdates(final boolean reloadData,
@NonNull final ListUpdateCallback listUpdateCallback) {
final Object oldData = data;
final Object newData = reloadData ? repository.get() : oldData;
if (presenter.getUpdates(oldData, newData, listUpdateCallback)) {
data = newData;
return true;
}
return false;
}
private static void applyPresenter1FromAToBChanges(@NonNull final ListUpdateCallback callback) {
// From count 1 to count 3:
// [X] -> [Y, X]
callback.onInserted(0, 1);
// [Y, X] -> [Y, X']
callback.onChanged(1, 1, PAYLOAD_PRESENTER_1_VALUE_A_TO_B);
// [Y, X] -> [Y, X', Z]
callback.onInserted(2, 1);
}
private static void applyPresenter1FromBToAChanges(@NonNull final ListUpdateCallback callback) {
// From count 3 to count 1:
// [Y, X', Z] -> [Y, Z]
callback.onRemoved(1, 1);
// [Y, Z] -> []
callback.onRemoved(0, 2);
// [] -> [X]
callback.onInserted(0, 1);
}
private static void applyPresenter2FromAToBChanges(
final int offset, @NonNull final ListUpdateCallback callback) {
// From count 4 to count 5:
// [M, N, O, P] -> [P, M, N, O]
callback.onMoved(3 + offset, offset);
// [P, M, N, O] -> [P, M]
callback.onRemoved(2 + offset, 2);
// [P, M] -> [P, X, Y, Z, M]
callback.onInserted(1 + offset, 3);
}
private static void applyPresenter2FromBToAChanges(
final int offset, @NonNull final ListUpdateCallback callback) {
// From count 5 to count 4:
// [P, X, Y, Z, M] -> [M, P, X, Y, Z]
callback.onMoved(4 + offset, offset);
// [M, P, X, Y, Z] -> [M, P]
callback.onRemoved(2 + offset, 3);
// [M, P] -> [M, N, O, P]
callback.onInserted(1 + offset, 2);
}
@After
public void tearDown() {
verify(mockStaticItemPresenter, never()).getUpdates(
any(), any(), any(ListUpdateCallback.class));
repositoryAdapter.stopObserving();
if (repositoryAdapter.hasObservers()) {
repositoryAdapter.unregisterAdapterDataObserver(redirectingObserver);
}
}
@Test
public void shouldSendDataChangeEventAndDisableUpdatesUntilGetItemCount() {
when(mockPresenter1.getUpdates(any(), any(), any(ListUpdateCallback.class))).thenReturn(false);
repositoryAdapter.getItemCount(); // usage
repository1.accept(REPOSITORY_VALUE_B);
runUiThreadTasksIncludingDelayedTasks();
verify(mockPresenter1).getUpdates(eq(REPOSITORY_VALUE_A), eq(REPOSITORY_VALUE_B),
any(ListUpdateCallback.class));
verifyZeroInteractions(fineGrainedEvents);
verify(onChangeEvent).run();
updateDispatcher.update();
runUiThreadTasksIncludingDelayedTasks();
verifyZeroInteractions(fineGrainedEvents);
verifyNoMoreInteractions(onChangeEvent);
repository2.accept(REPOSITORY_VALUE_B);
runUiThreadTasksIncludingDelayedTasks();
verifyZeroInteractions(fineGrainedEvents);
verifyNoMoreInteractions(onChangeEvent);
repositoryAdapter.getItemCount(); // usage
repository2.accept(REPOSITORY_VALUE_A);
runUiThreadTasksIncludingDelayedTasks();
verifyNoMoreInteractions(onChangeEvent);
applyPresenter2FromBToAChanges(PRESENTER_1_B_ITEM_COUNT + STATIC_ITEM_COUNT,
verifyingWrapper(fineGrainedEvents));
verifyNoMoreInteractions(fineGrainedEvents);
}
void setUpdatedCallback(@Nullable ListUpdateCallback listUpdatedCallback) {
mListUpdateCallback = listUpdatedCallback;
}
boolean getUpdates(final boolean reloadData,
@NonNull final ListUpdateCallback listUpdateCallback) {
return true;
}
@Test
public void shouldHaveDefaultGetUpdatesImplementation() throws Exception {
final boolean returnValue = new TestRepositoryPresenter().getUpdates(
new Object(), new Object(), mock(ListUpdateCallback.class));
assertThat(returnValue, is(false));
}
private static void applyPresenter1RefreshBChanges(@NonNull final ListUpdateCallback callback) {
// From count 3 to count 3:
// [Y, X', Z] -> [Y', X'', Z]
callback.onChanged(0, 2, PAYLOAD_PRESENTER_1_VALUE_B_REFRESH);
}
private static void applyPresenter2RefreshBChanges(
final int offset, @NonNull final ListUpdateCallback callback) {
// From count 5 to count 5: blanket change
callback.onChanged(offset, 5, PAYLOAD_PRESENTER_2_VALUE_B_REFRESH);
}
/**
* Produces a sequence of fine-grained events (addition, removal, changing and moving of
* individual items) to the given {@code listUpdateCallback} capturing the changes of data to be
* presented by this {@link RepositoryPresenter}, in response to an update. Implementation should
* do either of the following:
* <ul>
* <li>Produce and dispatch the fine-grained events that accurately describe the changes, and then
* return true;
* <li>Refuse to produce the fine-grained events by returning false. This is the base
* implementation.
* </ul>
*
* <p>While a {@link RepositoryAdapter} is {@link RepositoryAdapter#hasObservers() in use} (for
* example, attached to a {@code RecyclerView}), when it receives an update from a presented
* {@link Repository}, the associated {@code RepositoryPresenter} will be asked to produce a
* sequence of fine-grained events capturing the update; when the {@code RepositoryAdapter}
* receives an update from one of the {@linkplain Builder#addAdditionalObservable additional
* observables}, then all {@code RepositoryPresenter}s will be asked. If all affected
* {@code RepositoryPresenter}s produced fine-grained events, then {@code RepositoryAdapter} will
* apply the new data and notify the {@code RecyclerView} of the aggregated sequence of events;
* otherwise the adapter will call {@link Adapter#notifyDataSetChanged()}, and pause all update
* processing (both data and events) until fresh data is needed, hinted by the next call to
* {@link RepositoryAdapter#getItemCount()}.
*
* <p>Notes for implementation:
* <ul>
* <li>If the update comes from the paired repository, then {@code oldData} is the previously
* presented repository value, and {@code newData} is the value newly obtained from the
* repository. The new value has not been applied and is not exposed through the adapter.
* Note that repositories are allowed to dispatch updates while maintaining
* {@code oldData.equals(newData)} or even {@code oldData == newData}, depending on the
* repository implementation.
* <li>If the update comes from an additional observable, then it is guaranteed that
* {@code oldData == newData}, because the value is not reloaded from the repository.
* <li>A {@code RepositoryPresenter} may not have a chance to produce fine-grained events in
* response to an update. This can happen when an earlier presenter returns false from this
* method, or the adapter is not {@link RepositoryAdapter#hasObservers() in use} (for example,
* not attached to any {@code RecyclerView}).
* <li>{@code RepositoryAdapter} guarantees that, if this method is not called, then
* {@link #getItemCount} will be called before this {@code RepositoryPresenter} is asked to
* present any item from the new data. {@link #getItemCount} may be called again when there is
* a chance that the last data passed into the method may not be the latest.
* <li>A {@code RepositoryPresenter} presenting a static item (added with {@link Builder#addItem})
* will not be able to produce any events for any item views presented for the static item.
* </ul>
*
* @param oldData The data previously presented by this {@code RepositoryPresenter}.
* @param newData The data to be presented by this {@code RepositoryPresenter} following the
* update.
* @param listUpdateCallback A callback to record the sequence of fine-grained events to be
* dispatched via the {@code RepositoryAdapter} to the {@code RecyclerView}. To be used within
* this method call only. Positions and counts are relative to this presenter.
* @return Whether the sequence of calls to {@code listUpdateCallback} during the execution of
* this method has completely and accurately described all changes.
*/
public boolean getUpdates(@NonNull final T oldData, @NonNull final T newData,
@NonNull final ListUpdateCallback listUpdateCallback) {
return false;
}