Skip to content

Instantly share code, notes, and snippets.

@mandybess
Last active February 18, 2016 10:24
Show Gist options
  • Select an option

  • Save mandybess/5d8714095116c645a729 to your computer and use it in GitHub Desktop.

Select an option

Save mandybess/5d8714095116c645a729 to your computer and use it in GitHub Desktop.

Revisions

  1. Amanda Hill revised this gist Feb 17, 2016. 2 changed files with 103 additions and 0 deletions.
    65 changes: 65 additions & 0 deletions RxListViewTest.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,65 @@
    import android.app.Instrumentation;
    import android.support.test.InstrumentationRegistry;
    import android.support.test.rule.ActivityTestRule;
    import android.support.test.runner.AndroidJUnit4;
    import android.widget.ListView;

    import com.jakewharton.rxbinding.RecordingObserver;

    import org.junit.Before;
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.runner.RunWith;

    import rx.Subscription;
    import rx.android.schedulers.AndroidSchedulers;

    import static com.google.common.truth.Truth.assertThat;

    @RunWith(AndroidJUnit4.class)
    public final class RxListViewTest {
    @Rule
    public final ActivityTestRule<RxListViewTestActivity> activityRule =
    new ActivityTestRule<>(RxListViewTestActivity.class);

    private Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();

    private RxListViewTestActivity activity;
    private ListView listView;

    @Before
    public void setUp() {
    activity = activityRule.getActivity();
    listView = activity.listView;
    }

    @Test
    public void scrollEvents() {
    RecordingObserver<ListViewScrollEvent> o = new RecordingObserver<>();
    Subscription subscription = RxListView.scrollEvents(listView) //
    .subscribeOn(AndroidSchedulers.mainThread()) //
    .subscribe(o);
    o.assertNoMoreEvents();

    instrumentation.runOnMainSync(new Runnable() {
    @Override
    public void run() {
    listView.smoothScrollToPosition(50);
    }
    });
    ListViewScrollEvent event = o.takeNext();
    assertThat(event.view()).isEqualTo(listView);
    assertThat(event.firstVisibleItem()).isEqualTo(2);
    assertThat(event.totalItemCount()).isEqualTo(100);

    subscription.unsubscribe();

    instrumentation.runOnMainSync(new Runnable() {
    @Override
    public void run() {
    listView.smoothScrollToPosition(100);
    }
    });
    o.assertNoMoreEvents();
    }
    }
    38 changes: 38 additions & 0 deletions RxListViewTestActivity.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.ArrayAdapter;
    import android.widget.FrameLayout;
    import android.widget.ListView;

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;

    public final class RxListViewTestActivity extends Activity {
    ListView listView;

    List<String> values;
    ArrayAdapter<String> adapter;

    @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    values = createValues(100);
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, values);

    listView = new ListView(this);
    listView.setAdapter(adapter);

    FrameLayout layout = new FrameLayout(this);
    layout.addView(listView);
    setContentView(layout);
    }

    private static List<String> createValues(int count) {
    final List<String> values = new ArrayList<String>(count);
    for (int i = 0; i < count; i++) {
    values.add(String.valueOf(i));
    }
    return values;
    }
    }
  2. Amanda Hill created this gist Feb 17, 2016.
    79 changes: 79 additions & 0 deletions ListViewScrollEvent.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@
    import android.support.annotation.CheckResult;
    import android.support.annotation.NonNull;
    import android.widget.AbsListView;

    import com.jakewharton.rxbinding.view.ViewEvent;

    public final class ListViewScrollEvent extends ViewEvent<AbsListView> {

    @CheckResult
    @NonNull
    public static ListViewScrollEvent create(AbsListView listView, int scrollState,
    int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    return new ListViewScrollEvent(listView, scrollState, firstVisibleItem, visibleItemCount,
    totalItemCount);
    }

    public final int scrollState;
    public final int firstVisibleItem;
    public final int visibleItemCount;
    public final int totalItemCount;

    private ListViewScrollEvent(@NonNull AbsListView view, int scrollState,
    int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    super(view);
    this.scrollState = scrollState;
    this.firstVisibleItem = firstVisibleItem;
    this.visibleItemCount = visibleItemCount;
    this.totalItemCount = totalItemCount;
    }

    public int scrollState() {
    return scrollState;
    }

    public int firstVisibleItem() {
    return firstVisibleItem;
    }

    public int visibleItemCount() {
    return visibleItemCount;
    }

    public int totalItemCount() {
    return totalItemCount;
    }

    @Override
    public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    ListViewScrollEvent that = (ListViewScrollEvent) o;

    if (scrollState != that.scrollState) return false;
    if (firstVisibleItem != that.firstVisibleItem) return false;
    if (visibleItemCount != that.visibleItemCount) return false;
    return totalItemCount == that.totalItemCount;

    }

    @Override
    public int hashCode() {
    int result = scrollState;
    result = 31 * result + firstVisibleItem;
    result = 31 * result + visibleItemCount;
    result = 31 * result + totalItemCount;
    return result;
    }

    @Override
    public String toString() {
    return "ListViewScrollEvent{" +
    "scrollState=" + scrollState +
    ", firstVisibleItem=" + firstVisibleItem +
    ", visibleItemCount=" + visibleItemCount +
    ", totalItemCount=" + totalItemCount +
    '}';
    }
    }
    51 changes: 51 additions & 0 deletions ListViewScrollEventOnSubscribe.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,51 @@
    import android.widget.AbsListView;

    import rx.Observable;
    import rx.Subscriber;
    import rx.android.MainThreadSubscription;

    import static com.jakewharton.rxbinding.internal.Preconditions.checkUiThread;

    public final class ListViewScrollEventOnSubscribe
    implements Observable.OnSubscribe<ListViewScrollEvent> {

    final AbsListView view;

    public ListViewScrollEventOnSubscribe(AbsListView view) {
    this.view = view;
    }

    @Override
    public void call(final Subscriber<? super ListViewScrollEvent> subscriber) {
    checkUiThread();

    final AbsListView.OnScrollListener listener = new AbsListView.OnScrollListener() {
    int currentScrollState = SCROLL_STATE_IDLE;

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
    this.currentScrollState = scrollState;
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
    int totalItemCount) {
    if (!subscriber.isUnsubscribed()) {
    ListViewScrollEvent event = ListViewScrollEvent
    .create(view, this.currentScrollState, firstVisibleItem,
    visibleItemCount, totalItemCount);
    subscriber.onNext(event);
    }
    }
    };

    view.setOnScrollListener(listener);

    subscriber.add(new MainThreadSubscription() {
    @Override
    protected void onUnsubscribe() {
    view.setOnScrollListener(null);
    }
    });
    }
    }
    29 changes: 29 additions & 0 deletions RxListView.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    import android.support.annotation.CheckResult;
    import android.support.annotation.NonNull;
    import android.widget.AbsListView;
    import android.widget.AbsListView.OnScrollListener;

    import rx.Observable;

    import static com.google.common.base.Preconditions.checkNotNull;

    public final class RxListView {

    /**
    * Create an observable of scroll events on {@code listView}.
    * <p>
    * <em>Warning:</em> The created observable keeps a strong reference to {@code listView}.
    * Unsubscribe to free this reference.
    * * <p>
    * <em>Warning:</em> The created observable uses {@link AbsListView#setOnScrollListener(OnScrollListener)}
    * to observe scroll changes. Only one observable can be used for a view at a time.
    * <p>
    */
    @CheckResult
    @NonNull
    public static Observable<ListViewScrollEvent> scrollEvents(@NonNull AbsListView view) {
    checkNotNull(view, "view == null");

    return Observable.create(new ListScrollEventOnSubscribe(view));
    }
    }