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

fix: rework open/back animation logic on Android #871

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import Test817 from './src/Test817';
import Test831 from './src/Test831';
import Test844 from './src/Test844';
import Test861 from './src/Test861';
import Test865 from './src/Test865';

enableScreens();

Expand Down
74 changes: 74 additions & 0 deletions TestsExample/src/Test865.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import {View, Text, Button} from 'react-native';

import {NavigationContainer, ParamListBase} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from 'react-native-screens/native-stack';

const Stack = createNativeStackNavigator();

const First = ({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) => (
<View style={{flex: 1, justifyContent: 'center'}}>
<Text style={{paddingBottom: 24, textAlign: 'center'}}>Screen 1</Text>
<Button
title="PUSH TO SCREEN 2"
onPress={() => navigation.push('Screen2')}
/>
</View>
);

const Second = ({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) => (
<View style={{flex: 1, justifyContent: 'center'}}>
<Text style={{paddingBottom: 24, textAlign: 'center'}}>Screen 2</Text>
<Button
title="PUSH TO SCREEN 3"
onPress={() => navigation.push('Screen3')}
/>
</View>
);

const Third = ({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) => (
<View style={{flex: 1, justifyContent: 'center'}}>
<Text style={{paddingBottom: 24, textAlign: 'center'}}>Screen 3</Text>
<Button
title="RESET TO SCREEN 1 WITH INDEX OF 0"
onPress={() =>
navigation.reset({
routes: [{name: 'Screen1'}],
index: 0,
})
}
/>
</View>
);

const App = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
stackAnimation: 'slide_from_right',
}}>
<Stack.Screen name="Screen1" component={First} />
<Stack.Screen name="Screen2" component={Second} />
<Stack.Screen name="Screen3" component={Third} />
</Stack.Navigator>
</NavigationContainer>
);
};

export default App;
80 changes: 41 additions & 39 deletions android/src/main/java/com/swmansion/rnscreens/ScreenStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,64 +169,62 @@ protected void performUpdate() {
}
}

boolean customAnimation = false;
boolean shouldUseOpenAnimation = true;
int transition;
kacperkapusciak marked this conversation as resolved.
Show resolved Hide resolved
Screen.StackAnimation stackAnimation = Screen.StackAnimation.DEFAULT;

if (!mStack.contains(newTop)) {
// if new top screen wasn't on stack we do "open animation" so long it is not the very first screen on stack
if (mTopScreen != null && newTop != null) {
// there was some other screen attached before
int transition = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
if (!mScreenFragments.contains(mTopScreen) && newTop.getScreen().getReplaceAnimation() == Screen.ReplaceAnimation.POP) {
// if the previous top screen does not exist anymore and the new top was not on the stack before,
// probably replace was called, so we check the animation
transition = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
}
switch (newTop.getScreen().getStackAnimation()) {
case NONE:
transition = FragmentTransaction.TRANSIT_NONE;
break;
case FADE:
transition = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
break;
case SLIDE_FROM_RIGHT:
customAnimation = true;
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left);
break;
case SLIDE_FROM_LEFT:
customAnimation = true;
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right);
break;
}

if (!customAnimation) {
getOrCreateTransaction().setTransition(transition);
}
// if the previous top screen does not exist anymore and the new top was not on the stack before,
// probably replace or reset was called, so we play the "close animation"
// otherwise it's open animation
shouldUseOpenAnimation = mScreenFragments.contains(mTopScreen) || newTop.getScreen().getReplaceAnimation() != Screen.ReplaceAnimation.POP;
stackAnimation = newTop.getScreen().getStackAnimation();
}
} else if (mTopScreen != null && !mTopScreen.equals(newTop)) {
// otherwise if we are performing top screen change we do "back animation"
int transition = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
// otherwise if we are performing top screen change we do "close animation"
shouldUseOpenAnimation = false;
stackAnimation = mTopScreen.getScreen().getStackAnimation();
}

switch (mTopScreen.getScreen().getStackAnimation()) {
case NONE:
transition = FragmentTransaction.TRANSIT_NONE;
// animation logic start
if (shouldUseOpenAnimation) {
transition = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;

switch (stackAnimation) {
case SLIDE_FROM_RIGHT:
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left);
break;
case FADE:
transition = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
case SLIDE_FROM_LEFT:
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right);
break;
}
} else {
transition = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;

switch (stackAnimation) {
case SLIDE_FROM_RIGHT:
customAnimation = true;
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right);
break;
case SLIDE_FROM_LEFT:
customAnimation = true;
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left);
break;
}
}

if (!customAnimation) {
getOrCreateTransaction().setTransition(transition);
}
if (stackAnimation == Screen.StackAnimation.NONE) {
transition = FragmentTransaction.TRANSIT_NONE;
}
if (stackAnimation == Screen.StackAnimation.FADE) {
transition = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
}

if (!isCustomAnimation(stackAnimation)) {
getOrCreateTransaction().setTransition(transition);
}
// animation logic end

// remove all screens previously on stack
for (ScreenStackFragment screen : mStack) {
Expand Down Expand Up @@ -340,6 +338,10 @@ private void setupBackHandlerIfNeeded(ScreenStackFragment topScreen) {
}
}

private static boolean isCustomAnimation(Screen.StackAnimation stackAnimation) {
return stackAnimation == Screen.StackAnimation.SLIDE_FROM_RIGHT || stackAnimation == Screen.StackAnimation.SLIDE_FROM_LEFT;
}

private static boolean isTransparent(ScreenStackFragment fragment){
return fragment.getScreen().getStackPresentation() == Screen.StackPresentation.TRANSPARENT_MODAL;
}
Expand Down