Skip to content

Commit 402c690

Browse files
committed
wip2
1 parent 491c72a commit 402c690

2 files changed

Lines changed: 66 additions & 89 deletions

File tree

android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt

Lines changed: 17 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -124,78 +124,25 @@ class PagerViewViewManager :
124124
}
125125

126126
override fun onDropViewInstance(view: NestedScrollableHost) {
127-
try {
128-
val viewPager = PagerViewViewManagerImpl.getViewPager(view)
129-
// Access private mRecyclerView field using reflection
130-
val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
131-
recyclerViewField.isAccessible = true
132-
val recyclerView = recyclerViewField.get(viewPager) as RecyclerView
133-
134-
// Stop any scroll/drag in progress first
135-
recyclerView.stopScroll()
136-
137-
// Suppress layout to prevent any pending layout operations from executing
138-
recyclerView.suppressLayout(true)
139-
140-
// Access and stop the ViewFlinger directly via reflection - do this early
141-
try {
142-
val viewFlingerField = RecyclerView::class.java.getDeclaredField("mViewFlinger")
143-
viewFlingerField.isAccessible = true
144-
val viewFlinger = viewFlingerField.get(recyclerView) as? Runnable
145-
viewFlinger?.let {
146-
recyclerView.removeCallbacks(it)
147-
// Also try to stop it via reflection if it has a stop method
148-
try {
149-
val stopMethod = viewFlinger.javaClass.getDeclaredMethod("stop")
150-
stopMethod.isAccessible = true
151-
stopMethod.invoke(viewFlinger)
152-
} catch (ignored: Exception) {}
153-
}
154-
} catch (ignored: Exception) {
155-
// ViewFlinger reflection may fail on some versions
156-
}
127+
val vp = view.getChildAt(0) as? ViewPager2
128+
val recyclerView = vp?.getChildAt(0) as? RecyclerView
157129

158-
// Clear the recycler's scrap views before clearing adapter
159-
try {
160-
val recyclerField = RecyclerView::class.java.getDeclaredField("mRecycler")
161-
recyclerField.isAccessible = true
162-
val recycler = recyclerField.get(recyclerView)
163-
164-
// Clear scrap heaps
165-
val clearScrapMethod = recycler.javaClass.getDeclaredMethod("clear")
166-
clearScrapMethod.isAccessible = true
167-
clearScrapMethod.invoke(recycler)
168-
} catch (ignored: Exception) {
169-
// Recycler reflection may fail on some versions
170-
}
130+
// Stop any in-progress scroll/fling
131+
recyclerView?.stopScroll()
171132

172-
// Clear any pending animations and layout transitions
173-
recyclerView.clearAnimation()
174-
recyclerView.itemAnimator = null
175-
recyclerView.layoutTransition = null
176-
recyclerView.recycledViewPool.clear()
177-
178-
// Remove all pending operations and choreographer callbacks
179-
recyclerView.removeCallbacks(null)
180-
181-
// Try to clear any pending adapter updates
182-
try {
183-
val adapterHelperField = RecyclerView::class.java.getDeclaredField("mAdapterHelper")
184-
adapterHelperField.isAccessible = true
185-
val adapterHelper = adapterHelperField.get(recyclerView)
186-
val resetMethod = adapterHelper.javaClass.getDeclaredMethod("reset")
187-
resetMethod.isAccessible = true
188-
resetMethod.invoke(adapterHelper)
189-
} catch (ignored: Exception) {}
190-
191-
// Clear the adapter to prevent recycling during teardown
192-
viewPager.adapter = null
193-
194-
// Unsuppress layout after clearing adapter (won't do anything as view is being
195-
// destroyed)
196-
recyclerView.suppressLayout(false)
197-
} catch (e: Exception) {
198-
// View might already be in an invalid state
133+
// Disable item animations to prevent animator callbacks during teardown
134+
recyclerView?.itemAnimator = null
135+
136+
// Clear cached views in the recycled pool
137+
recyclerView?.recycledViewPool?.clear()
138+
139+
try {
140+
// Clear adapter to prevent post-teardown fling callbacks.
141+
// setAdapter(null) internally calls removeAndRecycleAllViews which
142+
// can throw if views are still attached during mid-scroll teardown.
143+
recyclerView?.adapter = null
144+
} catch (_: IllegalArgumentException) {
145+
// Safe to ignore during teardown — view is being destroyed
199146
}
200147
super.onDropViewInstance(view)
201148
}

example/src/MaterialTopTabExample.tsx

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,43 @@ import React, { useState } from 'react';
22
import { createNativeStackNavigator } from '@react-navigation/native-stack';
33
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
44

5-
import { View, Text, Button } from 'react-native';
5+
import { View, Text, Button, FlatList, StyleSheet } from 'react-native';
66

7-
function Tab1() {
7+
const DATA = Array.from({ length: 50 }, (_, i) => ({
8+
id: String(i),
9+
title: `Item ${i + 1}`,
10+
}));
11+
12+
function ScrollableTab({ name, color }: { name: string; color: string }) {
813
return (
9-
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
10-
<Text>Tab 1</Text>
11-
</View>
14+
<FlatList
15+
data={DATA}
16+
keyExtractor={(item) => item.id}
17+
renderItem={({ item }) => (
18+
<View style={[styles.item, { backgroundColor: color }]}>
19+
<Text style={styles.itemText}>
20+
{name} - {item.title}
21+
</Text>
22+
</View>
23+
)}
24+
/>
1225
);
1326
}
1427

15-
function Tab2(props: any) {
16-
return (
17-
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
18-
<Button title="Logout" onPress={props.onLogout} />
19-
</View>
20-
);
28+
function Tab1() {
29+
return <ScrollableTab name="Tab 1" color="#ff6b6b" />;
30+
}
31+
32+
function Tab2() {
33+
return <ScrollableTab name="Tab 2" color="#4ecdc4" />;
34+
}
35+
36+
function Tab3() {
37+
return <ScrollableTab name="Tab 3" color="#45b7d1" />;
38+
}
39+
40+
function Tab4() {
41+
return <ScrollableTab name="Tab 4" color="#96ceb4" />;
2142
}
2243

2344
const PreAuthScreen = (props: any) => {
@@ -30,19 +51,14 @@ const PreAuthScreen = (props: any) => {
3051

3152
const PostAuthScreen = (props: any) => {
3253
const { Navigator, Screen } = createMaterialTopTabNavigator();
33-
const onLogout = () => {
34-
setTimeout(() => {
35-
props.setIsSignedIn(false);
36-
}, 0);
37-
};
3854

3955
return (
4056
<View style={{ flex: 1 }}>
4157
<Navigator>
4258
<Screen name="Tab1" component={Tab1} />
43-
<Screen name="Tab2">
44-
{(props: any) => <Tab2 {...props} onLogout={onLogout} />}
45-
</Screen>
59+
<Screen name="Tab2" component={Tab2} />
60+
<Screen name="Tab3" component={Tab3} />
61+
<Screen name="Tab4" component={Tab4} />
4662
</Navigator>
4763
</View>
4864
);
@@ -70,3 +86,17 @@ export function MaterialTopBarExample() {
7086
</Navigator>
7187
);
7288
}
89+
90+
const styles = StyleSheet.create({
91+
item: {
92+
padding: 20,
93+
marginVertical: 2,
94+
marginHorizontal: 8,
95+
borderRadius: 8,
96+
},
97+
itemText: {
98+
fontSize: 16,
99+
color: '#fff',
100+
fontWeight: '600',
101+
},
102+
});

0 commit comments

Comments
 (0)