Skip to content

Commit fb3764c

Browse files
committed
Buttons for android.
1 parent 640ded0 commit fb3764c

File tree

9 files changed

+310
-28
lines changed

9 files changed

+310
-28
lines changed

GestureHandler.js

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react';
22
import {
33
findNodeHandle,
44
requireNativeComponent,
5+
Animated,
56
NativeModules,
67
ScrollView,
78
Slider,
@@ -11,6 +12,7 @@ import {
1112
ViewPagerAndroid,
1213
DrawerLayoutAndroid,
1314
WebView,
15+
StyleSheet,
1416
} from 'react-native';
1517

1618
const RNGestureHandlerModule = NativeModules.RNGestureHandlerModule;
@@ -256,7 +258,7 @@ function createNativeWrapper(Component, config = {}) {
256258
return ComponentWrapper;
257259
}
258260

259-
const WrappedScrollView = createNativeWrapper(ScrollView);
261+
const WrappedScrollView = createNativeWrapper(ScrollView, { disallowInterruption: true });
260262
const WrappedSlider = createNativeWrapper(Slider, {
261263
shouldCancelWhenOutside: false,
262264
shouldActivateOnStart: true,
@@ -284,11 +286,107 @@ State.print = (state) => {
284286
}
285287
}
286288

287-
const Button = createNativeWrapper(requireNativeComponent('RNGestureHandlerButton', null), {
289+
const RawButton = createNativeWrapper(requireNativeComponent('RNGestureHandlerButton', null), {
288290
shouldCancelWhenOutside: false,
289-
shouldActivateOnStart: true,
291+
shouldActivateOnStart: false,
290292
});
291293

294+
/* Buttons */
295+
296+
class BaseButton extends React.Component {
297+
static propTypes = RawButton.propTypes;
298+
constructor(props) {
299+
super(props);
300+
this._lastActive = false;
301+
}
302+
_onHandlerEvent = (e) => {
303+
console.log("EV", e.nativeEvent);
304+
const { state, oldState, pointerInside } = e.nativeEvent;
305+
const active = pointerInside && state === State.ACTIVE;
306+
if (active != this._lastActive) {
307+
this.props.onActiveStateChange && this.props.onActiveStateChange(active);
308+
}
309+
if (oldState === State.ACTIVE && this._lastActive) {
310+
this.props.onPress && this.props.onPress(active);
311+
}
312+
this._lastActive = active;
313+
}
314+
render() {
315+
return (
316+
<RawButton
317+
{...this.props}
318+
onHandlerStateChange={this._onHandlerEvent}
319+
onGestureEvent={this._onHandlerEvent}
320+
/>
321+
)
322+
}
323+
}
324+
325+
const btnStyles = StyleSheet.create({
326+
underlay: {
327+
position: 'absolute',
328+
left: 0,
329+
right: 0,
330+
bottom: 0,
331+
top: 0,
332+
},
333+
borderlessContainer: {
334+
position: 'absolute',
335+
left: 0,
336+
right: 0,
337+
bottom: 0,
338+
top: 0,
339+
}
340+
});
341+
342+
class RectButton extends React.Component {
343+
static propTypes = BaseButton.propTypes;
344+
static defaultProps = {
345+
activeOpacity: 0.105,
346+
underlayColor: 'black',
347+
}
348+
constructor(props) {
349+
super(props);
350+
this._opacity = new Animated.Value(0);
351+
}
352+
_handleActiveStateChange = (active) => {
353+
// this._opacity.setValue(active ? this.props.activeOpacity : 0);
354+
}
355+
render() {
356+
const { children, ...rest } = this.props;
357+
return (
358+
<BaseButton {...rest} onActiveStateChange={this._handleActiveStateChange}>
359+
<Animated.View style={[btnStyles.underlay, { backgroundColor: this.props.underlayColor }, { opacity: this._opacity }]} />
360+
{children}
361+
</BaseButton>
362+
);
363+
}
364+
}
365+
366+
class BorderlessButton extends React.Component {
367+
static propTypes = BaseButton.propTypes;
368+
static defaultProps = {
369+
activeOpacity: 0.3,
370+
}
371+
constructor(props) {
372+
super(props);
373+
this._opacity = new Animated.Value(1);
374+
}
375+
_handleActiveStateChange = (active) => {
376+
this._opacity.setValue(active ? this.props.activeOpacity : 1);
377+
}
378+
render() {
379+
const { children, ...rest } = this.props;
380+
return (
381+
<BaseButton borderless={true} {...rest} onActiveStateChange={this._handleActiveStateChange}>
382+
<Animated.View style={[btnStyles.borderlessContainer, { opacity: this._opacity }]}>
383+
{children}
384+
</Animated.View>
385+
</BaseButton>
386+
)
387+
}
388+
}
389+
292390
export {
293391
WrappedScrollView as ScrollView,
294392
WrappedSlider as Slider,
@@ -305,5 +403,10 @@ export {
305403
PinchGestureHandler,
306404
RotationGestureHandler,
307405
State,
308-
Button,
406+
407+
/* Buttons */
408+
RawButton,
409+
BaseButton,
410+
RectButton,
411+
BorderlessButton,
309412
}

android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandler.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class GestureHandler<T extends GestureHandler> {
2121
private View mView;
2222
private int mState = STATE_UNDETERMINED;
2323
private float mX, mY;
24+
private boolean mWithinBounds;
2425
private float mHitSlop[];
2526

2627
private boolean mShouldCancelWhenOutside;
@@ -88,6 +89,10 @@ public float getY() {
8889
return mY;
8990
}
9091

92+
public boolean isWithinBounds() {
93+
return mWithinBounds;
94+
}
95+
9196
public final void prepare(View view, GestureHandlerOrchestrator orchestrator) {
9297
if (mView != null || mOrchestrator != null) {
9398
throw new IllegalStateException("Already prepared or hasn't been reset");
@@ -104,8 +109,9 @@ public final void handle(MotionEvent event) {
104109
}
105110
mX = event.getX();
106111
mY = event.getY();
112+
mWithinBounds = isWithinBounds(mView, mX, mY);
107113
if (mState == STATE_ACTIVE) {
108-
if (mShouldCancelWhenOutside && !isWithinBounds(mView, event.getX(), event.getY())) {
114+
if (mShouldCancelWhenOutside && !mWithinBounds) {
109115
cancel();
110116
return;
111117
}
@@ -157,6 +163,16 @@ public boolean shouldRecognizeSimultaneously(GestureHandler handler) {
157163
return false;
158164
}
159165

166+
public boolean shouldBeCancelledBy(GestureHandler handler) {
167+
if (handler == this) {
168+
return false;
169+
}
170+
if (mInteractionController != null) {
171+
return mInteractionController.shouldHandlerBeCancelledBy(this, handler);
172+
}
173+
return false;
174+
}
175+
160176
public boolean isWithinBounds(View view, float posX, float posY) {
161177
float left = 0;
162178
float top = 0;
@@ -240,6 +256,7 @@ public GestureHandler setOnTouchEventListener(OnTouchEventListener<T> listener)
240256

241257
@Override
242258
public String toString() {
243-
return this.getClass().getSimpleName() + "@[" + mTag + "]:" + mView;
259+
String viewString = mView == null ? null : mView.getClass().getSimpleName();
260+
return this.getClass().getSimpleName() + "@[" + mTag + "]:" + viewString;
244261
}
245262
}

android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerInteractionController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ public interface GestureHandlerInteractionController {
44
boolean shouldWaitForHandlerFailure(GestureHandler handler, GestureHandler otherHandler);
55
boolean shouldRequireHandlerToWaitForFailure(GestureHandler handler, GestureHandler otherHandler);
66
boolean shouldRecognizeSimultaneously(GestureHandler handler, GestureHandler otherHandler);
7+
boolean shouldHandlerBeCancelledBy(GestureHandler handler, GestureHandler otherHandler);
78
}

android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,12 @@ private void scheduleFinishedHandlersCleanup() {
7575
}
7676

7777
private void cleanupFinishedHandlers() {
78-
for (int i = 0; i < mGestureHandlersCount; i++) {
78+
for (int i = mGestureHandlersCount - 1; i >= 0; i--) {
7979
GestureHandler handler = mGestureHandlers[i];
8080
if (isFinished(handler.getState()) && !isAwaiting(handler)) {
8181
mGestureHandlers[i] = mGestureHandlers[mGestureHandlersCount - 1];
8282
mGestureHandlers[mGestureHandlersCount - 1] = null;
8383
mGestureHandlersCount--;
84-
i--;
8584
handler.reset();
8685
handler.mIsActive = false;
8786
handler.mIsAwaiting = false;
@@ -114,12 +113,12 @@ private void tryActivate(GestureHandler handler) {
114113

115114
/*package*/ void onHandlerStateChange(GestureHandler handler, int newState, int prevState) {
116115
mHandlingChangeSemaphore += 1;
117-
if (isFinished(newState) && !handler.mIsAwaiting) {
116+
if (isFinished(newState)) {
118117
removeFromAwaitingHandlers(handler);
119118
}
120119
if (newState == GestureHandler.STATE_CANCELLED || newState == GestureHandler.STATE_FAILED) {
121120
// if there were handlers awaiting completion of this handler, we can trigger active state
122-
for (int i = 0; i < mAwaitingHandlersCount; i++) {
121+
for (int i = mAwaitingHandlersCount - 1; i >= 0; i--) {
123122
GestureHandler otherHandler = mAwaitingHandlers[i];
124123
if (shouldHandlerWaitForOther(otherHandler, handler)) {
125124
tryActivate(otherHandler);
@@ -149,23 +148,22 @@ private void makeActive(GestureHandler handler) {
149148
// Cancel all handlers that are required to be cancel upon current handler's activation
150149
for (int i = 0; i < mGestureHandlersCount; i++) {
151150
GestureHandler otherHandler = mGestureHandlers[i];
152-
if (!canRunSimultaneously(handler, otherHandler)) {
151+
if (shouldHandlerBeCancelledBy(otherHandler, handler)) {
153152
mHandlersToCancel[toCancelCount++] = otherHandler;
154153
}
155154
}
156155

157-
for (int i = 0; i < toCancelCount; i++) {
156+
for (int i = toCancelCount - 1; i >= 0; i--) {
158157
mHandlersToCancel[i].cancel();
159158
}
160159

161160
// Clear all awaiting handlers waiting for the current handler to fail
162-
for (int i = 0; i < mAwaitingHandlersCount; i++) {
161+
for (int i = mAwaitingHandlersCount - 1; i >= 0; i--) {
163162
GestureHandler otherHandler = mAwaitingHandlers[i];
164-
if (!canRunSimultaneously(handler, otherHandler)) {
163+
if (shouldHandlerBeCancelledBy(otherHandler, handler)) {
165164
mAwaitingHandlers[i] = mAwaitingHandlers[mAwaitingHandlersCount - 1];
166165
mAwaitingHandlers[mAwaitingHandlersCount - 1] = null;
167166
mAwaitingHandlersCount--;
168-
i--;
169167
otherHandler.cancel();
170168
}
171169
}
@@ -188,13 +186,13 @@ public void deliverEventToGestureHandlers(MotionEvent event) {
188186
for (int i = 0; i < handlersCount; i++) {
189187
mPreparedHandlers[i] = mGestureHandlers[i];
190188
}
191-
for (int i = 0; i < handlersCount; i++) {
189+
for (int i = handlersCount - 1; i >= 0; i--) {
192190
deliverEventToGestureHandler(mPreparedHandlers[i], event);
193191
}
194192
}
195193

196194
public void cancelAll() {
197-
for (int i = 0; i < mAwaitingHandlersCount; i++) {
195+
for (int i = mAwaitingHandlersCount - 1; i >= 0; i--) {
198196
mAwaitingHandlers[i].cancel();
199197
}
200198
// Copy handlers to "prepared handlers" array, because the list of active handlers can change
@@ -203,13 +201,16 @@ public void cancelAll() {
203201
for (int i = 0; i < handlersCount; i++) {
204202
mPreparedHandlers[i] = mGestureHandlers[i];
205203
}
206-
for (int i = 0; i < handlersCount; i++) {
204+
for (int i = handlersCount - 1; i >= 0; i--) {
207205
mPreparedHandlers[i].cancel();
208206
}
209207
}
210208

211209
private void deliverEventToGestureHandler(GestureHandler handler, MotionEvent event) {
212-
if (!handler.wantEvents() || handler.mIsAwaiting) {
210+
if (!handler.wantEvents()) {
211+
return;
212+
}
213+
if (handler.mIsAwaiting && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
213214
return;
214215
}
215216
float[] coords = sTempCoords;
@@ -398,6 +399,11 @@ private static boolean canRunSimultaneously(GestureHandler a, GestureHandler b)
398399
return a == b || a.shouldRecognizeSimultaneously(b) || b.shouldRecognizeSimultaneously(a);
399400
}
400401

402+
private static boolean shouldHandlerBeCancelledBy(GestureHandler handler, GestureHandler other) {
403+
return !canRunSimultaneously(handler, other) ||
404+
(handler != other && handler.shouldBeCancelledBy(other));
405+
}
406+
401407
private static boolean isFinished(int state) {
402408
return state == GestureHandler.STATE_CANCELLED || state == GestureHandler.STATE_FAILED
403409
|| state == GestureHandler.STATE_END;

android/lib/src/main/java/com/swmansion/gesturehandler/NativeViewGestureHandler.java

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,22 @@ public NativeViewGestureHandler setDisallowInterruption(boolean disallowInterrup
3131

3232
@Override
3333
public boolean shouldRequireToWaitForFailure(GestureHandler handler) {
34-
if (mDisallowInterruption) {
35-
return handler != this;
36-
}
3734
return super.shouldRequireToWaitForFailure(handler);
3835
}
3936

37+
@Override
38+
public boolean shouldRecognizeSimultaneously(GestureHandler handler) {
39+
if (!mDisallowInterruption) {
40+
return true;
41+
}
42+
return super.shouldRecognizeSimultaneously(handler);
43+
}
44+
45+
@Override
46+
public boolean shouldBeCancelledBy(GestureHandler handler) {
47+
return !mDisallowInterruption;
48+
}
49+
4050
@Override
4151
protected void onHandle(MotionEvent event) {
4252
View view = getView();
@@ -63,17 +73,28 @@ private void onHandleForView(View view, MotionEvent event) {
6373

6474
private void onHandleForViewGroup(ViewGroup view, MotionEvent event) {
6575
int state = getState();
76+
boolean delivered = false;
6677
if (state == STATE_UNDETERMINED || state == STATE_BEGAN) {
67-
if (view.onInterceptTouchEvent(event) || mShouldActivateOnStart) {
78+
if (view.isPressed()) {
6879
activate();
69-
} else if (state != STATE_BEGAN) {
70-
begin();
80+
} else {
81+
// view could become pressed due to
82+
view.onTouchEvent(event);
83+
delivered = true;
84+
if (view.onInterceptTouchEvent(event) || mShouldActivateOnStart || view.isPressed()) {
85+
activate();
86+
} else if (state != STATE_BEGAN) {
87+
begin();
88+
}
7189
}
72-
}
73-
if (getState() == STATE_ACTIVE) {
90+
} else if (state == STATE_ACTIVE) {
7491
view.onTouchEvent(event);
92+
delivered = true;
7593
}
7694
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
95+
if (!delivered) {
96+
view.onTouchEvent(event);
97+
}
7798
end();
7899
}
79100
}

0 commit comments

Comments
 (0)