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 2 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
59 changes: 59 additions & 0 deletions TestsExample/src/Test865.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { View, Text, Button } from 'react-native';

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

const Stack = createNativeStackNavigator();

const First = ({ navigation }: any) => (
kacperkapusciak marked this conversation as resolved.
Show resolved Hide resolved
<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 }: any) => (
<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 }: any) => (
<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;
94 changes: 48 additions & 46 deletions android/src/main/java/com/swmansion/rnscreens/ScreenStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,65 +169,63 @@ protected void performUpdate() {
}
}

boolean customAnimation = false;
// variables used for "open/back animation" logic
kacperkapusciak marked this conversation as resolved.
Show resolved Hide resolved
boolean shouldPlayOpenAnimation = true;
kacperkapusciak marked this conversation as resolved.
Show resolved Hide resolved
boolean isCustomAnimation = false;
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 "back animation"
// otherwise it's open animation
shouldPlayOpenAnimation = mScreenFragments.contains(mTopScreen) || newTop.getScreen().getReplaceAnimation() != Screen.ReplaceAnimation.POP;
// and check if it is a custom animation
isCustomAnimation = isCustomAnimation(newTop);
kacperkapusciak marked this conversation as resolved.
Show resolved Hide resolved
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;

switch (mTopScreen.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_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;
shouldPlayOpenAnimation = false;
// and check if it is a custom animation
isCustomAnimation = isCustomAnimation(mTopScreen);
stackAnimation = mTopScreen.getScreen().getStackAnimation();
}

// animation logic start
if (shouldPlayOpenAnimation) {
transition = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;

if (stackAnimation == Screen.StackAnimation.SLIDE_FROM_RIGHT) {
kacperkapusciak marked this conversation as resolved.
Show resolved Hide resolved
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left);
} else if (stackAnimation == Screen.StackAnimation.SLIDE_FROM_LEFT) {
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right);
}
} else {
transition = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;

if (!customAnimation) {
getOrCreateTransaction().setTransition(transition);
if (stackAnimation == Screen.StackAnimation.SLIDE_FROM_RIGHT) {
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right);
} else if (stackAnimation == Screen.StackAnimation.SLIDE_FROM_LEFT) {
getOrCreateTransaction().setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left);
}
}

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

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

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

private static boolean isCustomAnimation(ScreenStackFragment fragment) {
return fragment.getScreen().getStackAnimation() == Screen.StackAnimation.SLIDE_FROM_RIGHT || fragment.getScreen().getStackAnimation() == Screen.StackAnimation.SLIDE_FROM_LEFT;
}

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