Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for gesture observables. #128

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.jakewharton.rxbinding.view;

import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import rx.Observable;
import rx.subjects.PublishSubject;

final class ObservableGestureListener implements GestureDetector.OnGestureListener {

final View view;

private PublishSubject<ViewGestureEvent> downGestureObservable;
private PublishSubject<ViewGestureEvent> showPressGestureObservable;
private PublishSubject<ViewGestureEvent> singleTapUpGestureObservable;
private PublishSubject<ViewGestureScrollEvent> scrollGestureObservable;
private PublishSubject<ViewGestureEvent> longPressGestureObservable;
private PublishSubject<ViewGestureFlingEvent> flingGestureObservable;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these makes me super nervous. I'd rather use a single PublishSubject and then just use ofType to filter for the convenience methods.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we'd probably create an OnSubscribe class like the other listener bindings and just emit ViewGestureEvent. Then callers can filter or we can provide filters.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at the computer, but IIRC the issue with the custom OnSubscribe is
setting the OnTouchListener. Since we can only have one at a time, we
needed to be able to add these without requiring that this be the only
listener, so I chose this pattern so that you could get the onTouches
subscription and simply pass it to this, in addition to doing whatever
other things you want with it. Let me know if I'm wrong there, haven't
looked in a while.

Also, I like the single PublishSubject with ofType, I didn't know about
that.

On Sat, Sep 26, 2015, 11:05 PM Jake Wharton notifications@github.com
wrote:

In
rxbinding/src/main/java/com/jakewharton/rxbinding/view/ObservableGestureListener.java
#128 (comment):

+import android.view.MotionEvent;
+import android.view.View;
+
+import rx.Observable;
+import rx.subjects.PublishSubject;
+
+final class ObservableGestureListener implements GestureDetector.OnGestureListener {
+

  • final View view;
  • private PublishSubject downGestureObservable;
  • private PublishSubject showPressGestureObservable;
  • private PublishSubject singleTapUpGestureObservable;
  • private PublishSubject scrollGestureObservable;
  • private PublishSubject longPressGestureObservable;
  • private PublishSubject flingGestureObservable;

Actually we'd probably create an OnSubscribe class like the other
listener bindings and just emit ViewGestureEvent. Then callers can filter
or we can provide filters.


Reply to this email directly or view it on GitHub
https://github.com/JakeWharton/RxBinding/pull/128/files#r40501187.


ObservableGestureListener(View view) {
this.view = view;
}

public Observable<ViewGestureEvent> downObservable() {
if (downGestureObservable == null) {
downGestureObservable = PublishSubject.create();
}
return downGestureObservable;
}

public Observable<ViewGestureEvent> showPressObservable() {
if (showPressGestureObservable == null) {
showPressGestureObservable = PublishSubject.create();
}
return showPressGestureObservable;
}

public Observable<ViewGestureEvent> singleTapUpObservable() {
if (singleTapUpGestureObservable == null) {
singleTapUpGestureObservable = PublishSubject.create();
}
return singleTapUpGestureObservable;
}

public Observable<ViewGestureScrollEvent> scrollObservable() {
if (scrollGestureObservable == null) {
scrollGestureObservable = PublishSubject.create();
}
return scrollGestureObservable;
}

public Observable<ViewGestureEvent> longPressObservable() {
if (longPressGestureObservable == null) {
longPressGestureObservable = PublishSubject.create();
}
return longPressGestureObservable;
}

public Observable<ViewGestureFlingEvent> flingObservable() {
if (flingGestureObservable == null) {
flingGestureObservable = PublishSubject.create();
}
return flingGestureObservable;
}

@Override public boolean onDown(MotionEvent e) {
if (downGestureObservable != null) {
downGestureObservable.onNext(ViewGestureEvent.create(view, e));
}
return false;
}

@Override public void onShowPress(MotionEvent e) {
if (showPressGestureObservable != null) {
showPressGestureObservable.onNext(ViewGestureEvent.create(view, e));
}
}

@Override public boolean onSingleTapUp(MotionEvent e) {
if (singleTapUpGestureObservable != null) {
singleTapUpGestureObservable.onNext(ViewGestureEvent.create(view, e));
}
return false;
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (scrollGestureObservable != null) {
scrollGestureObservable.onNext(
ViewGestureScrollEvent.create(view, e1, e2, distanceX, distanceY));
}
return false;
}

@Override public void onLongPress(MotionEvent e) {
if (longPressGestureObservable != null) {
longPressGestureObservable.onNext(ViewGestureEvent.create(view, e));
}
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (flingGestureObservable != null) {
flingGestureObservable.onNext(
ViewGestureFlingEvent.create(view, e1, e2, velocityX, velocityY));
}
return false;
}
}
113 changes: 113 additions & 0 deletions rxbinding/src/main/java/com/jakewharton/rxbinding/view/RxGestures.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.jakewharton.rxbinding.view;

import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import rx.Observable;
import rx.functions.Action1;
import rx.functions.Func1;

/**
* Attaches a GestureDetector to the supplied touch event observable to be notified of
* gesture events. The RxGestures instance will subscribe to the supplied Observable, so
* if you'd like to have more than one subscriber you need to wrap it in a
* {@code ConnectableObservable} before passing it into this class.
* <p>
* {@code
* ConnectableObservable<ViewTouchEvent> touches = RxView.touches(view).publish();
* touches.connect();
* RxGestures gestures = RxGestures.withTouches(view, touches);
* Observable<ViewGestureScrollEvent> scrolls = gestures.scroll();
* }
* <p>
* <strong>Warning:</strong> Instances keep a strong reference to the view. Operators that
* cache instances have the potential to leak the associated {@code Context}.
*/
public class RxGestures {

private final Observable<MotionEvent> motionEventObservable;
private final GestureDetector gestureDetector;
private final ObservableGestureListener gestureListener;

/**
* Create an {@code RxGestures} object that listens to motion events.
* @param motionEvents
* @return
*/
public static RxGestures withMotionEvents(View view, Observable<MotionEvent> motionEvents) {
return new RxGestures(view, motionEvents);
}

/**
* Create an {@code RxGestures} object that listens to touch events.
*
* @param viewTouchEvents An observable of {@link ViewTouchEvent}s returned from
* {@code RxView#touchEvents(View view)}.
* @return A new RxGestures object listening for gestures.
*/
public static RxGestures withViewTouchEvents(View view,
Observable<ViewTouchEvent> viewTouchEvents) {
return new RxGestures(view, viewTouchEvents
.map(new Func1<ViewTouchEvent, MotionEvent>() {
@Override public MotionEvent call(ViewTouchEvent viewTouchEvent) {
return viewTouchEvent.motionEvent();
}
}));
}

private RxGestures(View view, Observable<MotionEvent> motionEventObservable) {
this.motionEventObservable = motionEventObservable;
this.gestureListener = new ObservableGestureListener(view);
this.gestureDetector = new GestureDetector(view.getContext(), gestureListener);

motionEventObservable.subscribe(new Action1<MotionEvent>() {
@Override public void call(MotionEvent motionEvent) {
gestureDetector.onTouchEvent(motionEvent);
}
});
}

/**
* Create an observable of down gestures.
*/
public Observable<ViewGestureEvent> down() {
return gestureListener.downObservable();
}

/**
* Create an observable of show press gestures.
*/
public Observable<ViewGestureEvent> showPress() {
return gestureListener.showPressObservable();
}

/**
* Create an observable of single tap up gestures.
*/
public Observable<ViewGestureEvent> singleTapUp() {
return gestureListener.singleTapUpObservable();
}

/**
* Create an observable of scroll gestures.
*/
public Observable<ViewGestureScrollEvent> scroll() {
return gestureListener.scrollObservable();
}

/**
* Create an observable of long press gestures.
*/
public Observable<ViewGestureEvent> longPress() {
return gestureListener.longPressObservable();
}

/**
* Create an observable of fling gestures.
*/
public Observable<ViewGestureFlingEvent> fling() {
return gestureListener.flingObservable();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.jakewharton.rxbinding.view;

import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import android.view.MotionEvent;
import android.view.View;

public final class ViewGestureEvent extends ViewEvent<View> {
@CheckResult @NonNull
public static ViewGestureEvent create(@NonNull View view, @NonNull MotionEvent motionEvent) {
return new ViewGestureEvent(view, motionEvent);
}

private final MotionEvent motionEvent;

private ViewGestureEvent(@NonNull View view, @NonNull MotionEvent motionEvent) {
super(view);
this.motionEvent = motionEvent;
}

@NonNull
public MotionEvent motionEvent() {
return motionEvent;
}

@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ViewGestureEvent)) return false;
ViewGestureEvent other = (ViewGestureEvent) o;
return other.view() == view() && other.motionEvent.equals(motionEvent);
}

@Override public int hashCode() {
int result = 17;
result = result * 37 + view().hashCode();
result = result * 37 + motionEvent.hashCode();
return result;
}

@Override public String toString() {
return "ViewGestureEvent{view=" + view() + ", motionEvent=" + motionEvent + '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.jakewharton.rxbinding.view;

import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import android.view.MotionEvent;
import android.view.View;

public final class ViewGestureFlingEvent extends ViewEvent<View> {
@CheckResult @NonNull
public static ViewGestureFlingEvent create(@NonNull View view, @NonNull MotionEvent e1,
@NonNull MotionEvent e2, float velocityX, float velocityY) {
return new ViewGestureFlingEvent(view, e1, e2, velocityX, velocityY);
}

private final MotionEvent e1, e2;
private final float velocityX, velocityY;

private ViewGestureFlingEvent(@NonNull View view, @NonNull MotionEvent e1,
@NonNull MotionEvent e2, float velocityX, float velocityY) {
super(view);
this.e1 = e1;
this.e2 = e2;
this.velocityX = velocityX;
this.velocityY = velocityY;
}

@NonNull public MotionEvent e1() {
return e1;
}

@NonNull public MotionEvent e2() {
return e2;
}

public float velocityX() {
return velocityX;
}

public float velocityY() {
return velocityY;
}

@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ViewGestureFlingEvent)) return false;
ViewGestureFlingEvent other = (ViewGestureFlingEvent) o;
return other.view() == view() && other.e1.equals(e1) && other.e2.equals(e2)
&& Float.compare(other.velocityX, velocityX) == 0
&& Float.compare(other.velocityY, velocityY) == 0;
}

@Override public int hashCode() {
int result = 17;
result = result * 37 + view().hashCode();
result = result * 37 + e1.hashCode();
result = result * 37 + e2.hashCode();
result = result * 37 + (velocityX != +0.0f ? Float.floatToIntBits(velocityX) : 0);
result = result * 37 + (velocityY != +0.0f ? Float.floatToIntBits(velocityY) : 0);
return result;
}

@Override public String toString() {
return "ViewScrollGestureEvent{view=" + view() + ", e1=" + e1 + ", e2=" + e2 +
", velocityX=" + velocityX + ", velocityY=" + velocityY + '}';
}
}