Merge pull request #72 from TouchInstinct/feature/diff_utils_to_master

Optional diff utils use
This commit is contained in:
Gavriil 2017-06-30 18:03:08 +03:00 committed by GitHub
commit aee975833a
2 changed files with 67 additions and 58 deletions

View File

@ -18,8 +18,8 @@ android {
dependencies {
compile project(':libraries:core')
provided 'com.android.support:appcompat-v7:25.3.1'
provided 'com.android.support:recyclerview-v7:25.3.1'
provided 'com.android.support:appcompat-v7:25.4.0'
provided 'com.android.support:recyclerview-v7:25.4.0'
provided 'io.reactivex:rxandroid:1.2.1'
provided 'io.reactivex:rxjava:1.3.0'

View File

@ -34,14 +34,16 @@ import java.util.List;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.components.utils.UiUtils;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.observables.collections.Change;
import ru.touchin.roboswag.core.observables.collections.ObservableCollection;
import ru.touchin.roboswag.core.observables.collections.ObservableList;
import ru.touchin.roboswag.core.observables.collections.changes.Change;
import ru.touchin.roboswag.core.observables.collections.changes.ChangePayloadProducer;
import ru.touchin.roboswag.core.observables.collections.changes.CollectionChanges;
import ru.touchin.roboswag.core.observables.collections.changes.SameItemsPredicate;
import ru.touchin.roboswag.core.observables.collections.loadable.LoadingMoreList;
import ru.touchin.roboswag.core.utils.Optional;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Action2;
import rx.functions.Action3;
@ -98,17 +100,12 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
public ObservableCollectionAdapter(@NonNull final LifecycleBindable lifecycleBindable) {
super();
this.lifecycleBindable = lifecycleBindable;
innerCollection.observeChanges().subscribe(this::onItemsChanged);
lifecycleBindable.untilDestroy(innerCollection.observeChanges(), this::onItemsChanged);
lifecycleBindable.untilDestroy(observableCollectionSubject
.switchMap(optional -> {
final ObservableCollection<TItem> collection = optional.get();
if (collection == null) {
innerCollection.clear();
return Observable.empty();
}
innerCollection.set(collection.getItems());
return collection.observeChanges().observeOn(AndroidSchedulers.mainThread());
}), this::onApplyChanges);
return collection != null ? collection.observeItems() : Observable.just(Collections.emptyList());
}), innerCollection::set);
lifecycleBindable.untilDestroy(createMoreAutoLoadingObservable());
}
@ -135,26 +132,6 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
});
}
private void onApplyChanges(@NonNull final ObservableCollection.CollectionChange<TItem> changes) {
anyChangeApplied = true;
for (final Change<TItem> change : changes.getChanges()) {
switch (change.getType()) {
case INSERTED:
innerCollection.addAll(change.getStart(), change.getChangedItems());
break;
case CHANGED:
innerCollection.update(change.getStart(), change.getChangedItems());
break;
case REMOVED:
innerCollection.remove(change.getStart(), change.getCount());
break;
default:
Lc.assertion("Not supported " + change.getType());
break;
}
}
}
/**
* Returns if any change of source collection applied to adapter.
* It's important to not show some footers or headers before first change have applied.
@ -223,7 +200,6 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
*/
public void setObservableCollection(@Nullable final ObservableCollection<TItem> observableCollection) {
this.observableCollectionSubject.onNext(new Optional<>(observableCollection));
refreshUpdate();
}
/**
@ -238,9 +214,9 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
/**
* Calls when collection changes.
*
* @param collectionChange Changes of collection.
* @param collectionChanges Changes of collection.
*/
protected void onItemsChanged(@NonNull final ObservableCollection.CollectionChange<TItem> collectionChange) {
protected void onItemsChanged(@NonNull final CollectionChanges<TItem> collectionChanges) {
if (Looper.myLooper() != Looper.getMainLooper()) {
Lc.assertion("Items changes called on not main thread");
return;
@ -250,14 +226,14 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
refreshUpdate();
return;
}
if (collectionChange.getNumber() != innerCollection.getChangesCount()
|| collectionChange.getNumber() != lastUpdatedChangeNumber + 1) {
if (lastUpdatedChangeNumber < collectionChange.getNumber()) {
if (collectionChanges.getNumber() != innerCollection.getChangesCount()
|| collectionChanges.getNumber() != lastUpdatedChangeNumber + 1) {
if (lastUpdatedChangeNumber < collectionChanges.getNumber()) {
refreshUpdate();
}
return;
}
notifyAboutChanges(collectionChange.getChanges());
notifyAboutChanges(collectionChanges.getChanges());
lastUpdatedChangeNumber = innerCollection.getChangesCount();
}
@ -266,26 +242,30 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
lastUpdatedChangeNumber = innerCollection.getChangesCount();
}
private void notifyAboutChanges(@NonNull final Collection<Change<TItem>> changes) {
private void notifyAboutChanges(@NonNull final Collection<Change> changes) {
for (final Change change : changes) {
switch (change.getType()) {
case INSERTED:
notifyItemRangeInserted(change.getStart() + getHeadersCount(), change.getCount());
break;
case CHANGED:
notifyItemRangeChanged(change.getStart() + getHeadersCount(), change.getCount());
break;
case REMOVED:
if (getItemCount() - getHeadersCount() == 0) {
//TODO: bug of recyclerview?
notifyDataSetChanged();
} else {
notifyItemRangeRemoved(change.getStart() + getHeadersCount(), change.getCount());
}
break;
default:
Lc.assertion("Not supported " + change.getType());
break;
if (change instanceof Change.Inserted) {
final Change.Inserted castedChange = (Change.Inserted) change;
notifyItemRangeInserted(castedChange.getPosition() + getHeadersCount(), castedChange.getCount());
} else if (change instanceof Change.Removed) {
if (getItemCount() - getHeadersCount() == 0) {
//TODO: bug of recyclerview?
notifyDataSetChanged();
} else {
final Change.Removed castedChange = (Change.Removed) change;
notifyItemRangeRemoved(castedChange.getPosition() + getHeadersCount(), castedChange.getCount());
}
} else if (change instanceof Change.Moved) {
final Change.Moved castedChange = (Change.Moved) change;
notifyItemMoved(castedChange.getFromPosition() + getHeadersCount(), castedChange.getToPosition() + getHeadersCount());
} else if (change instanceof Change.Changed) {
final Change.Changed castedChange = (Change.Changed) change;
notifyItemRangeChanged(
castedChange.getPosition() + getHeadersCount(),
castedChange.getCount(),
castedChange.getPayload());
} else {
Lc.assertion("Not supported " + change);
}
}
}
@ -624,6 +604,35 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
return false;
}
/**
* Enable diff utils algorithm in collection changes.
*
* @param detectMoves The flag that determines whether the {@link Change.Moved} changes will be generated or not;
* @param sameItemsPredicate Predicate for the determination of the same elements;
* @param changePayloadProducer Function that calculate change payload when items the same but contents are different.
*/
public void enableDiffUtils(final boolean detectMoves,
@NonNull final SameItemsPredicate<TItem> sameItemsPredicate,
@Nullable final ChangePayloadProducer<TItem> changePayloadProducer) {
innerCollection.enableDiffUtils(detectMoves, sameItemsPredicate, changePayloadProducer);
}
/**
* Disable diff utils algorithm.
*/
public void disableDiffUtils() {
innerCollection.disableDiffUtils();
}
/**
* Returns enabled flag of diff utils.
*
* @return true if diff utils is enabled.
*/
public boolean diffUtilsIsEnabled() {
return innerCollection.diffUtilsIsEnabled();
}
/**
* Interface to simply add item click listener.
*