adapter delegate logic finished

This commit is contained in:
Gavriil Sitnikov 2017-04-20 18:26:49 +03:00
parent 8925e5083e
commit 008b9f0b6b
7 changed files with 383 additions and 124 deletions

View File

@ -6,38 +6,248 @@ import android.view.ViewGroup;
import java.util.List;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.components.utils.UiUtils;
import rx.Completable;
import rx.Observable;
import rx.Single;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
public abstract class AdapterDelegate<TViewHolder extends BindableViewHolder, TItem> {
/**
* Objects of such class controls creation and binding of specific type of RecyclerView's ViewHolders.
* Default {@link #getItemViewType} is generating on construction of object.
*
* @param <TViewHolder> Type of {@link BindableViewHolder} of delegate;
* @param <TItem> Type of items to bind to {@link BindableViewHolder}s.
*/
@SuppressWarnings("PMD.TooManyMethods")
//TooManyMethods: it's ok as it is LifecycleBindable
public abstract class AdapterDelegate<TViewHolder extends BindableViewHolder, TItem> implements LifecycleBindable {
@NonNull
private final LifecycleBindable lifecycleBindable;
private final LifecycleBindable parentLifecycleBindable;
private final int defaultItemViewType;
public AdapterDelegate(@NonNull final LifecycleBindable lifecycleBindable) {
this.lifecycleBindable = lifecycleBindable;
public AdapterDelegate(@NonNull final LifecycleBindable parentLifecycleBindable) {
this.parentLifecycleBindable = parentLifecycleBindable;
this.defaultItemViewType = UiUtils.OfViews.generateViewId();
}
public abstract int getItemViewType();
/**
* Returns parent {@link LifecycleBindable} that this delegate created from (e.g. Activity or ViewController).
*
* @return Parent {@link LifecycleBindable}.
*/
@NonNull
public LifecycleBindable getParentLifecycleBindable() {
return parentLifecycleBindable;
}
/**
* Unique ID of AdapterDelegate.
*
* @return Unique ID.
*/
public int getItemViewType() {
return defaultItemViewType;
}
/**
* Returns if object is processable by this delegate.
* This item will be casted to {@link TItem} and passes to {@link #onBindViewHolder(TViewHolder, TItem, int, int)}.
*
* @param item Item to check;
* @param adapterPosition Position of item in adapter;
* @param itemCollectionPosition Position of item in collection that contains item;
* @return True if item is processable by this delegate.
*/
public abstract boolean isForViewType(@NonNull final Object item, final int adapterPosition, final int itemCollectionPosition);
/**
* Returns unique ID of item to support stable ID's logic of RecyclerView's adapter.
*
* @param item Item to check;
* @param adapterPosition Position of item in adapter;
* @param itemCollectionPosition Position of item in collection that contains item;
* @return Unique item ID.
*/
public long getItemId(@NonNull final TItem item, final int adapterPosition, final int itemCollectionPosition) {
return 0;
}
/**
* Creates ViewHolder to bind item to it later.
*
* @param parent Container of ViewHolder's view.
* @return New ViewHolder.
*/
@NonNull
public abstract TViewHolder onCreateViewHolder(@NonNull final ViewGroup parent);
/**
* Binds item to created by this object ViewHolder.
*
* @param holder ViewHolder to bind item to;
* @param item Item to check;
* @param adapterPosition Position of item in adapter;
* @param itemCollectionPosition Position of item in collection that contains item;
*/
public abstract void onBindViewHolder(@NonNull final TViewHolder holder, @NonNull final TItem item,
final int adapterPosition, final int itemCollectionPosition);
/**
* Binds item with payloads to created by this object ViewHolder.
*
* @param holder ViewHolder to bind item to;
* @param item Item to check;
* @param payloads Payloads;
* @param adapterPosition Position of item in adapter;
* @param itemCollectionPosition Position of item in collection that contains item;
*/
public void onBindViewHolder(@NonNull final TViewHolder holder, @NonNull final TItem item, @NonNull final List<Object> payloads,
final int adapterPosition, final int itemCollectionPosition) {
//do nothing by default
}
@SuppressWarnings("CPD-START")
//CPD: it is same as in other implementation based on BaseLifecycleBindable
@NonNull
protected LifecycleBindable getLifecycleBindable() {
return lifecycleBindable;
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {
return parentLifecycleBindable.untilStop(observable);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
return parentLifecycleBindable.untilStop(observable, onNextAction);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
@NonNull final Action1<T> onNextAction,
@NonNull final Action1<Throwable> onErrorAction) {
return parentLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
@NonNull final Action1<T> onNextAction,
@NonNull final Action1<Throwable> onErrorAction,
@NonNull final Action0 onCompletedAction) {
return parentLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Single<T> single) {
return parentLifecycleBindable.untilStop(single);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
return parentLifecycleBindable.untilStop(single, onSuccessAction);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Single<T> single,
@NonNull final Action1<T> onSuccessAction,
@NonNull final Action1<Throwable> onErrorAction) {
return parentLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Subscription untilStop(@NonNull final Completable completable) {
return parentLifecycleBindable.untilStop(completable);
}
@NonNull
@Override
public Subscription untilStop(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
return parentLifecycleBindable.untilStop(completable, onCompletedAction);
}
@NonNull
@Override
public Subscription untilStop(@NonNull final Completable completable,
@NonNull final Action0 onCompletedAction,
@NonNull final Action1<Throwable> onErrorAction) {
return parentLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
return parentLifecycleBindable.untilDestroy(observable);
}
@NonNull
@Override
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
return parentLifecycleBindable.untilDestroy(observable, onNextAction);
}
@NonNull
@Override
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Action1<T> onNextAction,
@NonNull final Action1<Throwable> onErrorAction) {
return parentLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Action1<T> onNextAction,
@NonNull final Action1<Throwable> onErrorAction,
@NonNull final Action0 onCompletedAction) {
return parentLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
return parentLifecycleBindable.untilDestroy(single);
}
@NonNull
@Override
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
return parentLifecycleBindable.untilDestroy(single, onSuccessAction);
}
@NonNull
@Override
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
@NonNull final Action1<T> onSuccessAction,
@NonNull final Action1<Throwable> onErrorAction) {
return parentLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Subscription untilDestroy(@NonNull final Completable completable) {
return parentLifecycleBindable.untilDestroy(completable);
}
@NonNull
@Override
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
return parentLifecycleBindable.untilDestroy(completable, onCompletedAction);
}
@NonNull
@Override
public Subscription untilDestroy(@NonNull final Completable completable,
@NonNull final Action0 onCompletedAction,
@NonNull final Action1<Throwable> onErrorAction) {
return parentLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
}
}

View File

@ -122,12 +122,6 @@ public class BindableViewHolder extends RecyclerView.ViewHolder implements Lifec
return ContextCompat.getDrawable(itemView.getContext(), resId);
}
@NonNull
@Override
public <T> Subscription bind(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
return baseLifecycleBindable.bind(observable, onNextAction);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {

View File

@ -30,7 +30,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.components.utils.UiUtils;
@ -42,7 +41,6 @@ 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.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.subjects.BehaviorSubject;
@ -58,30 +56,27 @@ import rx.subjects.BehaviorSubject;
* @param <TItem> Type of items to bind to ViewHolders;
* @param <TItemViewHolder> Type of ViewHolders to show items.
*/
@SuppressWarnings("unchecked")
public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends BindableViewHolder>
extends RecyclerView.Adapter<BindableViewHolder> {
//it is needed to avoid massive requests on initial view holders attaching (like if we will add 10 items they all will try to load history)
private static final long DELAY_BEFORE_LOADING_HISTORY = TimeUnit.SECONDS.toMillis(1);
private static final int PRE_LOADING_COUNT = 10;
private static final int PRE_LOADING_COUNT = 20;
@NonNull
private final BehaviorSubject<Optional<ObservableCollection<TItem>>> observableCollectionSubject
= BehaviorSubject.create(new Optional<>(null));
@NonNull
private final BehaviorSubject<Boolean> moreAutoLoadingRequested = BehaviorSubject.create();
@NonNull
private final LifecycleBindable lifecycleBindable;
@Nullable
private OnItemClickListener<TItem> onItemClickListener;
private Object onItemClickListener;
private int lastUpdatedChangeNumber = -1;
@NonNull
private final Observable<?> historyPreLoadingObservable;
@Nullable
private Subscription historyPreLoadingSubscription;
@NonNull
private final ObservableList<TItem> innerCollection = new ObservableList<>();
private boolean anyChangeApplied;
private long itemClickDelayMillis;
@NonNull
private final List<RecyclerView> attachedRecyclerViews = new LinkedList<>();
@NonNull
@ -101,14 +96,29 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
innerCollection.set(collection.getItems());
return collection.observeChanges().observeOn(AndroidSchedulers.mainThread());
}), this::onApplyChanges);
historyPreLoadingObservable = observableCollectionSubject
.switchMap(optional -> {
final ObservableCollection<TItem> collection = optional.get();
lifecycleBindable.untilDestroy(createMoreAutoLoadingObservable());
}
@NonNull
private Observable createMoreAutoLoadingObservable() {
return observableCollectionSubject
.switchMap(collectionOptional -> {
final ObservableCollection<TItem> collection = collectionOptional.get();
if (!(collection instanceof LoadingMoreList)) {
return Observable.empty();
}
final int size = collection.size();
return ((LoadingMoreList) collection).loadRange(size, size + PRE_LOADING_COUNT);
return moreAutoLoadingRequested
.distinctUntilChanged()
.switchMap(requested -> {
if (!requested) {
return Observable.empty();
}
final int size = collection.size();
return ((LoadingMoreList<?, ?, ?>) collection)
.loadRange(size, size + PRE_LOADING_COUNT)
.onErrorResumeNext(Observable.empty())
.doOnCompleted(() -> moreAutoLoadingRequested.onNext(false));
});
});
}
@ -134,7 +144,7 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
/**
* Returns if any change of source collection applied to adapter.
* It's important to not show some footers or header before first change have applied.
* It's important to not show some footers or headers before first change have applied.
*
* @return True id any change applied.
*/
@ -285,51 +295,72 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
return 0;
}
/**
* Returns list of added delegates.
*
* @return List of {@link AdapterDelegate}.
*/
@NonNull
public List<AdapterDelegate<TItemViewHolder, TItem>> getDelegates() {
return Collections.unmodifiableList(delegates);
}
@SuppressWarnings("unchecked")
/**
* Adds {@link AdapterDelegate} to adapter.
*
* @param delegate Delegate to add.
*/
public void addDelegate(@NonNull final AdapterDelegate<? extends TItemViewHolder, ? extends TItem> delegate) {
for (final AdapterDelegate addedDelegate : delegates) {
if (addedDelegate.getItemViewType() == delegate.getItemViewType()) {
Lc.assertion("AdapterDelegate with viewType=" + delegate.getItemViewType() + " already added");
return;
}
}
delegates.add((AdapterDelegate<TItemViewHolder, TItem>) delegate);
notifyDataSetChanged();
}
@SuppressWarnings("unchecked")
/**
* Removes {@link AdapterDelegate} from adapter.
*
* @param delegate Delegate to remove.
*/
public void removeDelegate(@NonNull final AdapterDelegate<? extends TItemViewHolder, ? extends TItem> delegate) {
delegates.remove((AdapterDelegate<TItemViewHolder, TItem>) delegate);
notifyDataSetChanged();
}
@Override
public int getItemViewType(final int adapterPosition) {
final int itemCollectionPosition = adapterPosition - getHeadersCount();
if (itemCollectionPosition < 0 || itemCollectionPosition >= innerCollection.size()) {
return super.getItemViewType(adapterPosition);
public int getItemViewType(final int positionInAdapter) {
final int positionInCollection = positionInAdapter - getHeadersCount();
if (positionInCollection < 0 || positionInCollection >= innerCollection.size()) {
return super.getItemViewType(positionInAdapter);
}
final TItem item = innerCollection.get(itemCollectionPosition);
final TItem item = innerCollection.get(positionInCollection);
for (final AdapterDelegate<?, TItem> delegate : delegates) {
if (delegate.isForViewType(item, adapterPosition, itemCollectionPosition)) {
if (delegate.isForViewType(item, positionInAdapter, positionInCollection)) {
return delegate.getItemViewType();
}
}
return super.getItemViewType(adapterPosition);
return super.getItemViewType(positionInAdapter);
}
@Override
public long getItemId(final int adapterPosition) {
final int itemCollectionPosition = adapterPosition - getHeadersCount();
if (itemCollectionPosition < 0 || itemCollectionPosition >= innerCollection.size()) {
return super.getItemId(adapterPosition);
public long getItemId(final int positionInAdapter) {
final int positionInCollection = positionInAdapter - getHeadersCount();
if (positionInCollection < 0 || positionInCollection >= innerCollection.size()) {
return super.getItemId(positionInAdapter);
}
final int itemViewType = getItemViewType(adapterPosition);
final TItem item = innerCollection.get(itemCollectionPosition);
final int itemViewType = getItemViewType(positionInAdapter);
final TItem item = innerCollection.get(positionInCollection);
for (final AdapterDelegate<?, TItem> delegate : delegates) {
if (delegate.getItemViewType() == itemViewType) {
return delegate.getItemId(item, adapterPosition, itemCollectionPosition);
return delegate.getItemId(item, positionInAdapter, positionInCollection);
}
}
return super.getItemId(adapterPosition);
return super.getItemId(positionInAdapter);
}
@Override
@ -349,31 +380,30 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
}
@Override
public void onBindViewHolder(@NonNull final BindableViewHolder holder, final int adapterPosition) {
public void onBindViewHolder(@NonNull final BindableViewHolder holder, final int positionInAdapter) {
lastUpdatedChangeNumber = innerCollection.getChangesCount();
final int itemCollectionPosition = adapterPosition - getHeadersCount();
if (itemCollectionPosition < 0 || itemCollectionPosition >= innerCollection.size()) {
final int positionInCollection = positionInAdapter - getHeadersCount();
if (positionInCollection < 0 || positionInCollection >= innerCollection.size()) {
return;
}
updateHistoryLoadingSubscription(adapterPosition);
bindItemViewHolder(holder, null, adapterPosition, itemCollectionPosition);
updateMoreAutoLoadingRequest(positionInCollection);
bindItemViewHolder(holder, null, positionInAdapter, positionInCollection);
}
@Override
public void onBindViewHolder(@NonNull final BindableViewHolder holder, final int adapterPosition, @NonNull final List<Object> payloads) {
super.onBindViewHolder(holder, adapterPosition, payloads);
final int itemCollectionPosition = adapterPosition - getHeadersCount();
if (itemCollectionPosition < 0 || itemCollectionPosition >= innerCollection.size()) {
public void onBindViewHolder(@NonNull final BindableViewHolder holder, final int positionInAdapter, @NonNull final List<Object> payloads) {
super.onBindViewHolder(holder, positionInAdapter, payloads);
final int positionInCollection = positionInAdapter - getHeadersCount();
if (positionInCollection < 0 || positionInCollection >= innerCollection.size()) {
return;
}
bindItemViewHolder(holder, payloads, adapterPosition, itemCollectionPosition);
bindItemViewHolder(holder, payloads, positionInAdapter, positionInCollection);
}
@SuppressWarnings("unchecked")
private void bindItemViewHolder(@NonNull final BindableViewHolder holder, @Nullable final List<Object> payloads,
final int adapterPosition, final int itemCollectionPosition) {
final int positionInAdapter, final int positionInCollection) {
final TItemViewHolder itemViewHolder;
try {
itemViewHolder = (TItemViewHolder) holder;
@ -381,69 +411,79 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
Lc.assertion(exception);
return;
}
final TItem item = innerCollection.get(itemCollectionPosition);
final int itemViewType = getItemViewType(adapterPosition);
if (onItemClickListener != null && !isOnClickListenerDisabled(item)) {
UiUtils.setOnRippleClickListener(holder.itemView, () -> onItemClickListener.onItemClicked(item, adapterPosition), getItemClickDelay());
}
final TItem item = innerCollection.get(positionInCollection);
final int itemViewType = getItemViewType(positionInAdapter);
updateClickListener(holder, item, positionInAdapter, positionInCollection);
for (final AdapterDelegate<TItemViewHolder, TItem> delegate : delegates) {
if (itemViewType == delegate.getItemViewType()) {
if (payloads == null) {
delegate.onBindViewHolder(itemViewHolder, item, adapterPosition, itemCollectionPosition);
delegate.onBindViewHolder(itemViewHolder, item, positionInAdapter, positionInCollection);
} else {
delegate.onBindViewHolder(itemViewHolder, item, payloads, adapterPosition, itemCollectionPosition);
delegate.onBindViewHolder(itemViewHolder, item, payloads, positionInAdapter, positionInCollection);
}
return;
}
}
if (payloads == null) {
onBindItemToViewHolder(itemViewHolder, adapterPosition, item);
onBindItemToViewHolder(itemViewHolder, positionInAdapter, item);
} else {
onBindItemToViewHolder(itemViewHolder, adapterPosition, item, payloads);
onBindItemToViewHolder(itemViewHolder, positionInAdapter, item, payloads);
}
}
private void updateHistoryLoadingSubscription(final int position) {
if ((historyPreLoadingSubscription != null && !historyPreLoadingSubscription.isUnsubscribed())
|| position - getHeadersCount() > innerCollection.size() - PRE_LOADING_COUNT) {
private void updateClickListener(@NonNull final BindableViewHolder holder, @NonNull final TItem item,
final int positionInAdapter, final int positionInCollection) {
if (onItemClickListener != null && !isOnClickListenerDisabled(item, positionInAdapter, positionInCollection)) {
UiUtils.setOnRippleClickListener(holder.itemView,
() -> {
if (onItemClickListener instanceof OnItemClickListener) {
((OnItemClickListener) onItemClickListener).onItemClicked(item);
} else if (onItemClickListener instanceof OnItemWithPositionClickListener) {
((OnItemWithPositionClickListener) onItemClickListener).onItemClicked(item, positionInAdapter, positionInCollection);
} else {
Lc.assertion("Unexpected onItemClickListener type " + onItemClickListener);
}
},
itemClickDelayMillis);
}
}
private void updateMoreAutoLoadingRequest(final int positionInCollection) {
if (positionInCollection > innerCollection.size() - PRE_LOADING_COUNT) {
return;
}
historyPreLoadingSubscription = lifecycleBindable
.untilDestroy(historyPreLoadingObservable
.delaySubscription(DELAY_BEFORE_LOADING_HISTORY, TimeUnit.MILLISECONDS)
.onErrorResumeNext(Observable.empty()));
moreAutoLoadingRequested.onNext(true);
}
/**
* Method to bind item (from {@link #getObservableCollection()}) to item-specific ViewHolder.
* It is not calling for headers and footer which counts are returned by {@link #getHeadersCount()} and @link #getFootersCount()}.
*
* @param holder ViewHolder to bind item to;
* @param position Position of ViewHolder (NOT item!);
* @param item Item returned by position (WITH HEADER OFFSET!).
* @param holder ViewHolder to bind item to;
* @param positionInAdapter Position of ViewHolder (NOT item!);
* @param item Item returned by position (WITH HEADER OFFSET!).
*/
protected abstract void onBindItemToViewHolder(@NonNull TItemViewHolder holder, int position, @NonNull TItem item);
protected abstract void onBindItemToViewHolder(@NonNull TItemViewHolder holder, int positionInAdapter, @NonNull TItem item);
/**
* Method to bind item (from {@link #getObservableCollection()}) to item-specific ViewHolder with payloads.
* It is not calling for headers and footer which counts are returned by {@link #getHeadersCount()} and @link #getFootersCount()}.
*
* @param holder ViewHolder to bind item to;
* @param position Position of ViewHolder (NOT item!);
* @param item Item returned by position (WITH HEADER OFFSET!);
* @param payloads Payloads;
* @param holder ViewHolder to bind item to;
* @param positionInAdapter Position of ViewHolder in adapter (NOT item!);
* @param item Item returned by position (WITH HEADER OFFSET!);
* @param payloads Payloads.
*/
protected void onBindItemToViewHolder(@NonNull final TItemViewHolder holder, final int position, @NonNull final TItem item,
protected void onBindItemToViewHolder(@NonNull final TItemViewHolder holder, final int positionInAdapter, @NonNull final TItem item,
@NonNull final List<Object> payloads) {
// do nothing by default
}
@Nullable
public TItem getItem(final int position) {
final int positionInList = position - getHeadersCount();
return positionInList < 0 || positionInList >= innerCollection.size() ? null : innerCollection.get(positionInList);
public TItem getItem(final int positionInAdapter) {
final int positionInCollection = positionInAdapter - getHeadersCount();
return positionInCollection < 0 || positionInCollection >= innerCollection.size() ? null : innerCollection.get(positionInCollection);
}
/**
@ -452,26 +492,51 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
* @param onItemClickListener Item click listener.
*/
public void setOnItemClickListener(@Nullable final OnItemClickListener<TItem> onItemClickListener) {
this.setOnItemClickListener(onItemClickListener, UiUtils.RIPPLE_EFFECT_DELAY);
}
/**
* Sets item click listener.
*
* @param onItemClickListener Item click listener;
* @param itemClickDelayMillis Delay of calling click listener.
*/
public void setOnItemClickListener(@Nullable final OnItemClickListener<TItem> onItemClickListener, final long itemClickDelayMillis) {
this.onItemClickListener = onItemClickListener;
this.itemClickDelayMillis = itemClickDelayMillis;
refreshUpdate();
}
/**
* Returns delay of item click. By default returns delay of ripple effect duration.
* Sets item click listener.
*
* @return Milliseconds delay of click.
* @param onItemClickListener Item click listener.
*/
protected long getItemClickDelay() {
return UiUtils.RIPPLE_EFFECT_DELAY;
public void setOnItemClickListener(@Nullable final OnItemWithPositionClickListener<TItem> onItemClickListener) {
this.setOnItemClickListener(onItemClickListener, UiUtils.RIPPLE_EFFECT_DELAY);
}
/**
* Sets item click listener.
*
* @param onItemClickListener Item click listener;
* @param itemClickDelayMillis Delay of calling click listener.
*/
public void setOnItemClickListener(@Nullable final OnItemWithPositionClickListener<TItem> onItemClickListener, final long itemClickDelayMillis) {
this.onItemClickListener = onItemClickListener;
this.itemClickDelayMillis = itemClickDelayMillis;
refreshUpdate();
}
/**
* Returns if click listening disabled or not for specific item.
*
* @param item Item to check click availability;
* @param item Item to check click availability;
* @param positionInAdapter Position of clicked item in adapter (with headers);
* @param positionInCollection Position of clicked item in inner collection;
* @return True if click listener enabled for such item.
*/
public boolean isOnClickListenerDisabled(@NonNull final TItem item) {
public boolean isOnClickListenerDisabled(@NonNull final TItem item, final int positionInAdapter, final int positionInCollection) {
return false;
}
@ -485,10 +550,27 @@ public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends
/**
* Calls when item have clicked.
*
* @param item Clicked item;
* @param position Position of clicked item.
* @param item Clicked item.
*/
void onItemClicked(@NonNull TItem item, int position);
void onItemClicked(@NonNull TItem item);
}
/**
* Interface to simply add item click listener based on item position in adapter and collection.
*
* @param <TItem> Type of item
*/
public interface OnItemWithPositionClickListener<TItem> {
/**
* Calls when item have clicked.
*
* @param item Clicked item;
* @param positionInAdapter Position of clicked item in adapter (with headers);
* @param positionInCollection Position of clicked item in inner collection.
*/
void onItemClicked(@NonNull TItem item, final int positionInAdapter, final int positionInCollection);
}

View File

@ -339,12 +339,6 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
@SuppressWarnings("CPD-START")
//CPD: it is same as in other implementation based on BaseLifecycleBindable
@NonNull
@Override
public <T> Subscription bind(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
return baseLifecycleBindable.bind(observable, onNextAction);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {

View File

@ -256,12 +256,6 @@ public abstract class BaseActivity extends AppCompatActivity
@SuppressWarnings("CPD-START")
//CPD: it is same as in other implementation based on BaseLifecycleBindable
@NonNull
@Override
public <T> Subscription bind(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
return baseLifecycleBindable.bind(observable, onNextAction);
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable,

View File

@ -101,15 +101,6 @@ public class BaseLifecycleBindable implements LifecycleBindable {
isCreatedSubject.onNext(false);
}
@NonNull
@Override
public <T> Subscription bind(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return isStartedSubject.switchMap(started -> started ? observable.observeOn(AndroidSchedulers.mainThread()) : Observable.never())
.takeUntil(isCreatedSubject.filter(created -> !created))
.subscribe(onNextAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
}
@NonNull
@Override
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {

View File

@ -35,18 +35,12 @@ import rx.functions.Action1;
* Created by Gavriil Sitnikov on 15/04/16.
* Interface that should be implemented by lifecycle-based elements ({@link android.app.Activity}, {@link android.support.v4.app.Fragment} etc.)
* to not manually manage subscriptions.
* Use {@link #bind(Observable, Action1)} method to subscribe to observable onStart and unsubscribe onStop automatically.
* Use {@link #untilStop(Observable)} method to subscribe to observable where you want and unsubscribe onStop.
* Use {@link #untilDestroy(Observable)} method to subscribe to observable where you want and unsubscribe onDestroy.
*/
@SuppressWarnings("PMD.TooManyMethods")
public interface LifecycleBindable {
@NonNull
//do not use this method - it's not obvious method
@Deprecated
<T> Subscription bind(@NonNull Observable<T> observable, @NonNull Action1<T> onNextAction);
/**
* Method should be used to guarantee that observable won't be subscribed after onStop.
* It is automatically subscribing to the observable.