Skip to content

Commit 4ac6a4a

Browse files
authored
fix: preserve Android EaseView visuals on drop and add example (#47)
* fix: preserve Android EaseView visuals on drop and add example * test: remove extra animation from Issue 45 repro
1 parent 940e4d3 commit 4ac6a4a

6 files changed

Lines changed: 218 additions & 4 deletions

File tree

android/src/main/java/com/ease/EaseView.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ class EaseView(context: Context) : ReactViewGroup(context) {
691691

692692
private fun applyBackgroundColor(color: Int) {
693693
currentBackgroundColor = color
694-
setBackgroundColor(color)
694+
BackgroundStyleApplicator.setBackgroundColor(this, color)
695695
}
696696

697697
private fun animateBackgroundColor(fromColor: Int, toColor: Int, config: TransitionConfig, loop: Boolean = false) {
@@ -714,8 +714,7 @@ class EaseView(context: Context) : ReactViewGroup(context) {
714714
}
715715
addUpdateListener { animation ->
716716
val color = animation.animatedValue as Int
717-
this@EaseView.currentBackgroundColor = color
718-
this@EaseView.setBackgroundColor(color)
717+
this@EaseView.applyBackgroundColor(color)
719718
}
720719
addListener(object : AnimatorListenerAdapter() {
721720
private var cancelled = false
@@ -983,7 +982,7 @@ class EaseView(context: Context) : ReactViewGroup(context) {
983982
runningSpringAnimations.remove(viewProperty)
984983
}
985984

986-
fun cleanup() {
985+
fun stopAnimations() {
987986
for (runnable in pendingDelayedRunnables) {
988987
removeCallbacks(runnable)
989988
}
@@ -1004,6 +1003,12 @@ class EaseView(context: Context) : ReactViewGroup(context) {
10041003
setLayerType(savedLayerType, null)
10051004
}
10061005
activeAnimationCount = 0
1006+
pendingBatchAnimationCount = 0
1007+
anyInterrupted = false
1008+
}
1009+
1010+
fun cleanup() {
1011+
stopAnimations()
10071012

10081013
prevOpacity = null
10091014
prevTranslateX = null

android/src/main/java/com/ease/EaseViewManager.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,17 @@ class EaseViewManager : ReactViewManager() {
225225

226226
override fun onDropViewInstance(view: ReactViewGroup) {
227227
super.onDropViewInstance(view)
228+
// Android screen transitions may keep drawing dropped views briefly.
229+
// Stop animation work here, but keep visual state until recycle.
230+
(view as? EaseView)?.stopAnimations()
231+
}
232+
233+
override fun prepareToRecycleView(
234+
reactContext: ThemedReactContext,
235+
view: ReactViewGroup
236+
): ReactViewGroup? {
228237
(view as? EaseView)?.cleanup()
238+
return super.prepareToRecycleView(reactContext, view)
229239
}
230240

231241
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {

example/app/issues/45/_layout.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Stack } from 'expo-router';
2+
3+
export default function Issue45Layout() {
4+
return (
5+
<>
6+
<Stack.Screen
7+
options={{ title: 'Issue #45 — Android modal background' }}
8+
/>
9+
<Stack
10+
screenOptions={{
11+
headerStyle: { backgroundColor: '#1a1a2e' },
12+
headerTintColor: '#fff',
13+
headerTitleStyle: { fontWeight: '700' },
14+
contentStyle: { backgroundColor: '#1a1a2e' },
15+
}}
16+
>
17+
<Stack.Screen name="index" options={{ title: 'Tab One' }} />
18+
<Stack.Screen
19+
name="modal"
20+
options={{
21+
animation: 'slide_from_right',
22+
presentation: 'card',
23+
title: 'Modal',
24+
}}
25+
/>
26+
</Stack>
27+
</>
28+
);
29+
}

example/app/issues/45/index.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { useRouter } from 'expo-router';
2+
import { Pressable, StyleSheet, Text, View } from 'react-native';
3+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
4+
5+
export default function Issue45Home() {
6+
const insets = useSafeAreaInsets();
7+
const router = useRouter();
8+
9+
return (
10+
<View style={[styles.root, { paddingTop: insets.top + 24 }]}>
11+
<Text style={styles.heading}>Issue #45 Android modal background</Text>
12+
<Text style={styles.body}>
13+
Open the modal, then dismiss it with the back gesture or header back
14+
button. The red animated card and green style card should keep their
15+
backgrounds throughout the screen transition.
16+
</Text>
17+
<Text style={styles.link}>
18+
https://github.com/AppAndFlow/react-native-ease/issues
19+
</Text>
20+
21+
<Pressable
22+
style={({ pressed }) => [styles.button, pressed && styles.buttonDown]}
23+
onPress={() => router.push('/issues/45/modal')}
24+
>
25+
<Text style={styles.buttonText}>Open Modal</Text>
26+
</Pressable>
27+
</View>
28+
);
29+
}
30+
31+
const styles = StyleSheet.create({
32+
root: {
33+
flex: 1,
34+
backgroundColor: '#1a1a2e',
35+
paddingHorizontal: 20,
36+
},
37+
heading: {
38+
color: '#fff',
39+
fontSize: 22,
40+
fontWeight: '700',
41+
marginBottom: 8,
42+
},
43+
body: {
44+
color: '#aaaacc',
45+
fontSize: 14,
46+
lineHeight: 20,
47+
marginBottom: 8,
48+
},
49+
link: {
50+
color: '#6666aa',
51+
fontSize: 12,
52+
marginBottom: 28,
53+
},
54+
button: {
55+
alignItems: 'center',
56+
alignSelf: 'flex-start',
57+
backgroundColor: '#4a90d9',
58+
borderRadius: 8,
59+
paddingHorizontal: 18,
60+
paddingVertical: 12,
61+
},
62+
buttonDown: {
63+
backgroundColor: '#3978b8',
64+
},
65+
buttonText: {
66+
color: '#fff',
67+
fontSize: 15,
68+
fontWeight: '700',
69+
},
70+
});

example/app/issues/45/modal.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { StyleSheet, Text, View } from 'react-native';
2+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
3+
import { EaseView } from 'react-native-ease';
4+
5+
export default function Issue45Modal() {
6+
const insets = useSafeAreaInsets();
7+
8+
return (
9+
<View style={[styles.container, { paddingTop: insets.top + 24 }]}>
10+
<Text style={styles.heading}>Modal repro</Text>
11+
<Text style={styles.body}>
12+
Dismiss this screen. If the bug is present on Android, one or both card
13+
backgrounds disappear during the closing transition while the blue child
14+
remains visible.
15+
</Text>
16+
17+
<View style={styles.cards}>
18+
<View style={styles.demo}>
19+
<Text style={styles.label}>Animated background</Text>
20+
<EaseView
21+
animate={{ backgroundColor: 'red' }}
22+
transition={{ type: 'timing', duration: 300 }}
23+
style={styles.card}
24+
>
25+
<View style={styles.child} />
26+
</EaseView>
27+
</View>
28+
29+
<View style={styles.demo}>
30+
<Text style={styles.label}>Style background</Text>
31+
<EaseView
32+
transition={{ type: 'timing', duration: 300 }}
33+
style={styles.card2}
34+
>
35+
<View style={styles.child} />
36+
</EaseView>
37+
</View>
38+
</View>
39+
</View>
40+
);
41+
}
42+
43+
const styles = StyleSheet.create({
44+
container: {
45+
flex: 1,
46+
alignItems: 'center',
47+
backgroundColor: '#1a1a2e',
48+
gap: 16,
49+
justifyContent: 'center',
50+
paddingHorizontal: 20,
51+
},
52+
heading: {
53+
color: '#fff',
54+
fontSize: 22,
55+
fontWeight: '700',
56+
},
57+
body: {
58+
color: '#aaaacc',
59+
fontSize: 14,
60+
lineHeight: 20,
61+
maxWidth: 340,
62+
textAlign: 'center',
63+
},
64+
cards: {
65+
alignItems: 'center',
66+
gap: 16,
67+
marginTop: 12,
68+
},
69+
demo: {
70+
alignItems: 'center',
71+
gap: 8,
72+
},
73+
label: {
74+
color: '#8888aa',
75+
fontSize: 13,
76+
},
77+
card: {
78+
alignItems: 'center',
79+
height: 100,
80+
justifyContent: 'center',
81+
width: 100,
82+
},
83+
card2: {
84+
alignItems: 'center',
85+
backgroundColor: 'green',
86+
height: 100,
87+
justifyContent: 'center',
88+
width: 100,
89+
},
90+
child: {
91+
backgroundColor: 'blue',
92+
height: 24,
93+
width: 24,
94+
},
95+
});

example/src/demos/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ export const demos: Record<string, DemoEntry> = {
142142
title: 'Issue #42 — Tabs loop',
143143
section: 'Issues',
144144
},
145+
'issue-45': {
146+
route: '/issues/45',
147+
title: 'Issue #45 — Android modal background',
148+
section: 'Issues',
149+
},
145150
};
146151

147152
interface SectionData {

0 commit comments

Comments
 (0)