morristech
11/15/2016 - 9:08 AM

Create an observable of long click events on RecyclerView items (refer to RxBinding tests for the whole picture).

Create an observable of long click events on RecyclerView items (refer to RxBinding tests for the whole picture).

import android.support.v7.widget.RecyclerView;
import android.view.View;

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

import static rx.android.MainThreadSubscription.verifyMainThread;

final class RecyclerViewItemLongClickOnSubscribe implements Observable.OnSubscribe<Integer> {
  final RecyclerView recyclerView;

  public RecyclerViewItemLongClickOnSubscribe(RecyclerView recyclerView) {
    this.recyclerView = recyclerView;
  }

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

    final View.OnLongClickListener onLongClickListener = new View.OnLongClickListener() {
      @Override public boolean onLongClick(View view) {
        if (!subscriber.isUnsubscribed()) {
          RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(view);
          subscriber.onNext(holder.getAdapterPosition());
          return true;
        }
        return false;
      }
    };

    final RecyclerView.OnChildAttachStateChangeListener onAttachListener = new RecyclerView.OnChildAttachStateChangeListener() {
      @Override public void onChildViewAttachedToWindow(View view) {
        view.setOnLongClickListener(onLongClickListener);
      }

      @Override public void onChildViewDetachedFromWindow(View view) {
        view.setOnLongClickListener(null);
      }
    };

    subscriber.add(new MainThreadSubscription() {
      @Override protected void onUnsubscribe() {
        recyclerView.removeOnChildAttachStateChangeListener(onAttachListener);
      }
    });

    recyclerView.addOnChildAttachStateChangeListener(onAttachListener);
  }
}
@RunWith(AndroidJUnit4.class)
public final class RxRecyclerViewTest {
  @Rule public final ActivityTestRule<RxRecyclerViewTestActivity> activityRule =
      new ActivityTestRule<>(RxRecyclerViewTestActivity.class);

  private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();

  RecyclerView view;
  private ViewInteraction interaction;
  private ViewDirtyIdlingResource viewDirtyIdler;
  private View child;

  @Before public void setUp() {
    RxRecyclerViewTestActivity activity = activityRule.getActivity();
    view = activity.recyclerView;
    child = new View(activityRule.getActivity());
    interaction = Espresso.onView(ViewMatchers.withId(android.R.id.primary));
    viewDirtyIdler = new ViewDirtyIdlingResource(activity);
    Espresso.registerIdlingResources(viewDirtyIdler);
  }

  @After public void tearDown() {
    Espresso.unregisterIdlingResources(viewDirtyIdler);
  }

  @Test public void itemsClicked() {
    RecordingObserver<Integer> o = new RecordingObserver<>();
    Subscription subscription = RxRecyclerView.itemClick(view)
        .subscribeOn(AndroidSchedulers.mainThread())
        .subscribe(o);
    o.assertNoMoreEvents();

    final Adapter adapter = new Adapter();

    instrumentation.runOnMainSync(new Runnable() {
      @Override public void run() {
        view.setAdapter(adapter);
      }
    });
    for (int i = 0, childCount = view.getChildCount(); i < childCount; i++) {
      final View childAt = view.getChildAt(i);
      instrumentation.runOnMainSync(new Runnable() {
        @Override public void run() {
          childAt.performClick();
        }
      });
      assertThat(o.takeNext()).isEqualTo(i);
    }

    subscription.unsubscribe();

    instrumentation.runOnMainSync(new Runnable() {
      @Override public void run() {
        view.getChildAt(0).performClick();
      }
    });
    o.assertNoMoreEvents();
  }

  @Test public void itemsLongClicked() {
    RecordingObserver<Integer> o = new RecordingObserver<>();
    Subscription subscription = RxRecyclerView.itemLongClick(view)
        .subscribeOn(AndroidSchedulers.mainThread())
        .subscribe(o);
    o.assertNoMoreEvents();

    final Adapter adapter = new Adapter();

    instrumentation.runOnMainSync(new Runnable() {
      @Override public void run() {
        view.setAdapter(adapter);
      }
    });
    for (int i = 0, childCount = view.getChildCount(); i < childCount; i++) {
      final View childAt = view.getChildAt(i);
      instrumentation.runOnMainSync(new Runnable() {
        @Override public void run() {
          childAt.performLongClick();
        }
      });
      assertThat(o.takeNext()).isEqualTo(i);
    }

    subscription.unsubscribe();

    instrumentation.runOnMainSync(new Runnable() {
      @Override public void run() {
        view.getChildAt(0).performLongClick();
      }
    });
    o.assertNoMoreEvents();
  }

  private static class Adapter extends RecyclerView.Adapter<ViewHolder> {
    public Adapter() {
      setHasStableIds(true);
    }

    @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
      TextView v = (TextView) LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
      return new ViewHolder(v);
    }

    @Override public void onBindViewHolder(ViewHolder holder, int position) {
      holder.textView.setText(String.valueOf(position));
    }

    @Override public int getItemCount() {
      return 100;
    }

    @Override public long getItemId(int position) {
      return position;
    }
  }

  private static class ViewHolder extends RecyclerView.ViewHolder {

    TextView textView;

    public ViewHolder(TextView itemView) {
      super(itemView);
      this.textView = itemView;
    }
  }
}