-
Notifications
You must be signed in to change notification settings - Fork 970
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
rharter
wants to merge
3
commits into
JakeWharton:master
Choose a base branch
from
rharter:gestures
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
110 changes: 110 additions & 0 deletions
110
rxbinding/src/main/java/com/jakewharton/rxbinding/view/ObservableGestureListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
||
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
113
rxbinding/src/main/java/com/jakewharton/rxbinding/view/RxGestures.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
|
||
} |
43 changes: 43 additions & 0 deletions
43
rxbinding/src/main/java/com/jakewharton/rxbinding/view/ViewGestureEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 + '}'; | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
rxbinding/src/main/java/com/jakewharton/rxbinding/view/ViewGestureFlingEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 + '}'; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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 useofType
to filter for the convenience methods.There was a problem hiding this comment.
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 emitViewGestureEvent
. Then callers can filter or we can provide filters.There was a problem hiding this comment.
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 [email protected]
wrote: