Closed dpyeates closed 2 years ago
Did you clear caches after installation? Try running ./gradlew clean
in android/ directory and running metro with --reset-cache
flag (i.e. yarn start --reset-cache
).
Just tried cleaning and resetting the metro cache but still the same. TapGestureHandler works fine on both iOS and Android.
I am facing the same problem. Tried the suggested steps but does not help.
PanGestureHandler is working fine for me on Android. Are you seeing any error or something, and also have you performed the required steps for linking on Android? If you want I can also share the snippe of how I have used it.
PanGestureHandler is working fine for me on Android. Are you seeing any error or something, and also have you performed the required steps for linking on Android? If you want I can also share the snippe of how I have used it.
Can you share the snippet. It will be useful
@rahmanharoon Here is the snippet for the gesture handler,
const translation = {
x: useSharedValue(0),
};
const SWIPE_VELOCITY = 500;
const { width: screenWidth } = useWindowDimensions();
const hiddenTranslateX = 2 * screenWidth;
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = translation.x.value;
},
onActive: (event, ctx) => {
if (flipped) {
translation.x.value = ctx.startX + event.translationX;
}
},
onEnd: (event) => {
if (flipped) {
if (Math.abs(event.velocityX) < SWIPE_VELOCITY) {
translation.x.value = withSpring(0);
return;
}
//perform actions with swipe
translation.x.value = withSpring(Math.sign(event.velocityX * 10000) * hiddenTranslateX);
}
},
});
return (
<PanGestureHandler onGestureEvent={gestureHandler}>
<Cards/>
</PanGestureHandler>
)
Also having this issue. Installed everything as per the docs, cleared cache and cleaned gradle. PanGestureHandler works fine on iOS but no triggers at all on Android.
What makes it even more strange is I have another PanGestureHandler in another component setup almost the exact same way that works fine on both iOS and Android. Any fixes for this? Here's my code in case it helps.
import React from 'react';
import {View, Text} from 'react-native';
import {PanGestureHandler} from 'react-native-gesture-handler';
import Animated, {
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
//redux
import {connect} from 'react-redux';
//styles
import {gbl} from '../styles/global_styles';
import {alert} from '../styles/components/alert';
let timer;
const Alert = props => {
const translateY = useSharedValue(-290);
const slideIn = () => {
translateY.value = withTiming(0);
};
const slideOut = () => {
translateY.value = withTiming(-290);
};
const clearTimer = () => {
clearTimeout(timer);
};
const setTimer = () => {
timer = setTimeout(() => {
props.alert.show ? (translateY.value = withTiming(-290)) : null;
}, 2000);
};
const animate = () => {
slideIn();
timer = setTimeout(() => {
props.alert.show ? slideOut() : null;
}, 3000);
};
animate();
const panGestureEvent = useAnimatedGestureHandler({
onStart: (e, context) => {
context.translateY = translateY.value;
},
onActive: (e, context) => {
runOnJS(clearTimer)();
if (!(e.translationY >= 20)) {
translateY.value = context.translateY + e.translationY;
}
},
onEnd: (e, context) => {
if (e.translationY <= -10) {
translateY.value = withTiming(-290);
} else {
translateY.value = withTiming(0);
runOnJS(setTimer)();
}
},
});
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: translateY.value,
},
],
};
});
return (
<PanGestureHandler onGestureEvent={panGestureEvent}>
<Animated.View
style={[
gbl.centerColumn,
gbl.w100,
{
position: 'absolute',
top: '12.5%',
zIndex: 10000000,
},
animatedStyle,
]}>
<View
style={[
gbl.w90,
gbl.py15,
gbl.pl15,
gbl.pr15,
alert.alert,
gbl.leftRow,
]}>
{/* circle shape on alert */}
<View
style={[
alert.alert_shape,
gbl.mr10,
{backgroundColor: props.alert.color},
]}></View>
<View style={[{flex: 1}, gbl.centerColumn]}>
<Text style={alert.alert_msg}>{props.alert.msg}</Text>
</View>
</View>
</Animated.View>
</PanGestureHandler>
);
};
const mapStateToProps = state => {
const {alert} = state;
return {alert};
};
export default connect(mapStateToProps)(Alert);
For me at least, this is now fixed.
The problem for me - which took wayyyy longer to uncover than it should - was that i had quite a complex view which I was using pointerEvents="none" on Image components. Now, on iOS this worked, but on Android the Image component doesn't seem to support pointerEvents="none", so the image was consuming the touch events and this is why my handler, wasn't seeing them. I fixed this by wrapping the Image component in a View and setting pointerEvents="none" on that instead. Ping! it all started working.
Hmm, interesting. I'm not using any images, and what seems to happen is my click event goes right through my view as if it's transparent. For example, if a TextInput is behind it, it will click the TextInput and not the view with the PanGestureHandler. I've tried removing all styles just to see if I could get any type of event to fire, thus to no avail. Again it makes no sense as I have another component working fine with PanGestureHandler lol. It's just Android as well, I genuinely have no clue.
Same problem happens on our App (and we also have React Navigation 5.x
if it's related). So I decided to try a simple example to see if it was some wrong code on our side. The following simple example does not work on Android
but works as expected on iOS
with our dependencies:
running into the same issues, so far I have no idea whats wrong. I just see that my PanGestureHandler works perfectly on iOS and on android is does not receive any touch events.
@jakub-gonet Hi, excuse me, I found an example that can be reproduced stably, if you use @react-navigation/native-stack
, PanGestureHandler
doesn't work on Android!
Example code:
import "react-native-gesture-handler";
import { StyleSheet, Text, View } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack"; // native-stack don't work
import { Example } from "./src/screens";
const Stack = createNativeStackNavigator();
export default function App() { return (
); }
const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#fff", alignItems: "center", justifyContent: "center", }, });
Example page:
import { PanGestureHandler } from "react-native-gesture-handler"; import { View, Alert } from "react-native"; import React from "react"; export const Example = () => { return ( <PanGestureHandler onActivated={() => { Alert.alert("onActivated"); }} onBegan={() => { Alert.alert("onBegan"); }} onEnded={() => { Alert.alert("onEnded"); }} onGestureEvent={() => { Alert.alert("onGestureEvent"); }} onHandlerStateChange={() => { Alert.alert("onHandlerStateChange"); }} onFailed={() => { Alert.alert("onFailed"); }}
<View style={{ height: 300, width: 500, backgroundColor: "red" }}> ); };
@dpyeates @ammarahm-ed @michaelknoch @ajesamann
I've solved the problem, I guess you use @react-navigation/native-stack
and react-native-modal
, because modals are not located under React Native Root view in native hierarchy.,you can see that.
So you can use :
1.gestureHandlerRootHOC
Example code:
import { gestureHandlerRootHOC } from "react-native-gesture-handler"
const ExampleWithHoc = gestureHandlerRootHOC(() => (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Example" component={Example} />
</Stack.Navigator>
</NavigationContainer>
));
OR
GestureHandlerRootView
Example code:
import { GestureHandlerRootView } from "react-native-gesture-handler"
<GestureHandlerRootView style={{flex:1}}>
<RootNavigator/>
</GestureHandlerRootView>
it dosen't work for me. if i use pangesturehandler in my components not screen.
for example i use pangestruehandler in childern component in mainscreen
where i should wrap
RNGH should always work in modals since 2.2.0 given that views under modal are wrapped in RNGH root view.
still does not work on android:
@jakub-gonet Not working PanGestureHandler on android inside default inside modal component.. How example you can see this problem in github.com/jeremybarbet/react-native-modalize/ lib. If I use parameter withReactModal click on overlay not working (only android).
Here is a simple example that does't work:
<Modal
supportedOrientations={['landscape', 'portrait', 'portrait-upside-down']}
visible
coverScreen
style={{ backgroundColor: '#303030', width: WIDTH, height: HEIGHT }}
>
<PanGestureHandler onGestureEvent={() => console.log(12381239812312312387638)}>
<AnimatedBox flex={1} backgroundColor="black" />
</PanGestureHandler>
</Modal>
"react-native": "0.70.6", "react-native-gesture-handler": "~2.7.1",
Same issue not working on Android, with sdk 47:
import React, { useState } from "react";
import { StyleSheet, View } from "react-native";
import { PanGestureHandler, TapGestureHandler, } from "react-native-gesture-handler";
import Animated, { cancelAnimation, runOnJS, runOnUI, useAnimatedGestureHandler, useAnimatedProps, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring, } from "react-native-reanimated";
import Svg, { Path } from "react-native-svg";
const AnimatedPath = Animated.createAnimatedComponent(Path);
export function PatternLock(props) {
const [isError, setIsError] = useState(false);
const canTouch = useSharedValue(true);
const patternPoints = useSharedValue();
const selectedIndexes = useSharedValue([]);
const endPoint = useSharedValue();
const containerLayout = useSharedValue({ width: 0, height: 0, min: 0 });
const R = useDerivedValue(
() =>
(containerLayout.value.min / props.rowCount - props.patternMargin * 2) / 2
);
const cvc = useAnimatedStyle(() => ({
flexDirection: "row",
flexWrap: "wrap",
marginBottom: `${Math.max(
0,
containerLayout.value.height / containerLayout.value.width - 1.25
) * 50
}%`,
width: containerLayout.value.min,
height: containerLayout.value.min,
}));
const msgX = useSharedValue(0);
const msgColor = { color: isError ? props.errorColor : props.activeColor };
const msgStyle = useAnimatedStyle(() => {
return { transform: [{ translateX: msgX.value }] };
});
const onContainerLayout = ({
nativeEvent: {
layout: { x, y, width, height },
},
}) =>
(containerLayout.value = {
width,
height,
min: Math.min(width, height),
});
const onPatternLayout = ({ nativeEvent: { layout } }) => {
const points = [];
for (let i = 0; i < props.rowCount; i++) {
for (let j = 0; j < props.columnCount; j++) {
points.push({
x: layout.x + (layout.width / props.columnCount) * (j + 0.5),
y: layout.y + (layout.height / props.rowCount) * (i + 0.5),
});
}
}
patternPoints.value = points;
};
const onEndJS = (res) => {
console.log('first')
if (props.onCheck) {
canTouch.value = false;
if (!props.onCheck(res)) {
setIsError(true);
const closeError = () => setIsError(false);
runOnUI(() => {
cancelAnimation(msgX);
//修复iOS上原地spring不动的问题。
msgX.value = withSpring(
msgX.value === 0 ? 0.1 : 0,
{
stiffness: 2000,
damping: 10,
mass: 1,
velocity: 2000,
},
(finished) => {
runOnJS(closeError)();
canTouch.value = true;
selectedIndexes.value = [];
}
);
})();
} else {
setIsError(false);
setTimeout(() => {
selectedIndexes.value = [];
canTouch.value = true;
}, 1000);
}
}
};
const panHandler = useAnimatedGestureHandler({
onStart: (evt) => {
console.log('x')
if (
canTouch.value &&
patternPoints.value &&
selectedIndexes.value.length === 0
) {
const selected = [];
patternPoints.value.every((p, idx) => {
if (
(p.x - evt.x) * (p.x - evt.x) + (p.y - evt.y) * (p.y - evt.y) <
R.value * R.value
) {
selected.push(idx);
return false;
}
return true;
});
selectedIndexes.value = selected;
}
},
onActive: (evt) => {
if (
canTouch.value &&
patternPoints.value &&
selectedIndexes.value.length > 0
) {
patternPoints.value.every((p, idx) => {
if (
(p.x - evt.x) * (p.x - evt.x) + (p.y - evt.y) * (p.y - evt.y) <
R.value * R.value
) {
if (selectedIndexes.value.indexOf(idx) < 0) {
selectedIndexes.value = [...selectedIndexes.value, idx];
}
return false;
}
return true;
});
endPoint.value = { x: evt.x, y: evt.y };
}
},
onEnd: (evt) => {
if (!canTouch.value) return;
endPoint.value = null;
if (selectedIndexes.value.length > 0)
runOnJS(onEndJS)(selectedIndexes.value.join(""));
},
});
const animatedProps = useAnimatedProps(() => {
let d = "";
selectedIndexes.value.forEach((idx) => {
d += !d ? " M" : " L";
d += ` ${patternPoints.value[idx].x},${patternPoints.value[idx].y}`;
});
if (d && endPoint.value) d += ` L${endPoint.value.x},${endPoint.value.y}`;
if (!d) d = "M-1,-1";
return { d };
});
return (
<PanGestureHandler onGestureEvent={panHandler}>
<Animated.View style={styles.container} onLayout={onContainerLayout}>
<TapGestureHandler onGestureEvent={panHandler}>
<Animated.View style={styles.container}>
<View style={styles.msgc}>
<Animated.Text style={[msgColor, msgStyle]}>
{props.message}
</Animated.Text>
</View>
<Animated.View style={cvc} onLayout={onPatternLayout}>
{Array(props.rowCount * props.columnCount)
.fill(0)
.map((_, idx) => {
const patternColor = useDerivedValue(() => {
if (selectedIndexes.value.findIndex((v) => v === idx) < 0) {
return props.inactiveColor;
} else if (isError) {
return props.errorColor;
} else {
return props.activeColor;
}
});
const outer = useAnimatedStyle(() => {
return {
borderWidth: 2,
width: 2 * R.value,
height: 2 * R.value,
alignItems: "center",
justifyContent: "center",
borderColor: patternColor.value,
borderRadius: 2 * R.value,
margin: props.patternMargin,
};
});
const inner = useAnimatedStyle(() => {
return {
width: R.value * 0.8,
height: R.value * 0.8,
borderRadius: R.value * 0.8,
backgroundColor: patternColor.value,
};
});
return (
<Animated.View key={idx} style={outer}>
<Animated.View style={inner} />
</Animated.View>
);
})}
</Animated.View>
<Svg style={styles.svg} width="100%" height="100%">
<AnimatedPath
fill="none"
strokeWidth={3}
animatedProps={animatedProps}
stroke={isError ? props.errorColor : props.activeColor}
/>
</Svg>
</Animated.View>
</TapGestureHandler>
</Animated.View>
</PanGestureHandler>
);
}
PatternLock.defaultProps = {
message: "",
rowCount: 3,
columnCount: 3,
patternMargin: 25,
inactiveColor: "#8E91A8",
activeColor: "#5FA8FC",
errorColor: "#D93609",
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: "stretch",
alignItems: "center",
},
msgc: {
flex: 1,
justifyContent: "center",
alignSelf: "center",
},
svg: {
position: "absolute",
left: 0,
top: 0,
},
});
@dpyeates @ammarahm-ed @michaelknoch @ajesamann I've solved the problem, I guess you use
@react-navigation/native-stack
andreact-native-modal
, because modals are not located under React Native Root view in native hierarchy.,you can see that. So you can use :1.
gestureHandlerRootHOC
Example code:import { gestureHandlerRootHOC } from "react-native-gesture-handler" const ExampleWithHoc = gestureHandlerRootHOC(() => ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Example" component={Example} /> </Stack.Navigator> </NavigationContainer> )); OR
GestureHandlerRootView
Example code:import { GestureHandlerRootView } from "react-native-gesture-handler" <GestureHandlerRootView style={{flex:1}}> <RootNavigator/> </GestureHandlerRootView>
It worked!!
@vanoob404 yeah thank you already solved it this way but forgot to post the answer. Appreciated still 🙌
@jakub-gonet Hi, excuse me, I found an example that can be reproduced stably, if you use
@react-navigation/native-stack
,PanGestureHandler
doesn't work on Android! Example code:
- navigation page
import "react-native-gesture-handler"; import { StyleSheet, Text, View } from "react-native"; import { NavigationContainer } from "@react-navigation/native"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; // native-stack don't work import { Example } from "./src/screens"; const Stack = createNativeStackNavigator(); export default function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Example" component={Example} /> </Stack.Navigator> </NavigationContainer> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#fff", alignItems: "center", justifyContent: "center", }, });
Example page:
import { PanGestureHandler } from "react-native-gesture-handler"; import { View, Alert } from "react-native"; import React from "react"; export const Example = () => { return ( <PanGestureHandler onActivated={() => { Alert.alert("onActivated"); }} onBegan={() => { Alert.alert("onBegan"); }} onEnded={() => { Alert.alert("onEnded"); }} onGestureEvent={() => { Alert.alert("onGestureEvent"); }} onHandlerStateChange={() => { Alert.alert("onHandlerStateChange"); }} onFailed={() => { Alert.alert("onFailed"); }} > <View style={{ height: 300, width: 500, backgroundColor: "red" }}></View> </PanGestureHandler> ); };
This works except that i only had to import and wrap the PanGestureHandler with the GestureHandlerRootView
Description
I am using a PanGestureHandler in my React Native app. This works perfectly on iOS. When I run on Android, I get nothing at all, no triggers. I have installed as per the guidelines and have made the change to MainActivity.java to add the ReactActivityDelegate. I am using React Navigation. I have also attempted to use the gestureHandlerRootHOC method without any luck. Is PanGestureHandler compatible with React Navigation?
Expected behavior
Pan gesture should trigger and work as per iOS
Actual behavior
No trigger at all on Android
Package versions