Skip to content

Commit 2c4ea72

Browse files
committed
fix(android): resolve Scrapped or attached views may not be recycled crash
1 parent 5a4638a commit 2c4ea72

3 files changed

Lines changed: 38 additions & 23 deletions

File tree

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.reactnativepagerview
22

33
import android.view.View
44
import android.view.ViewGroup
5+
import androidx.recyclerview.widget.RecyclerView
56
import androidx.viewpager2.widget.ViewPager2
67
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
78
import com.facebook.infer.annotation.Assertions
@@ -88,6 +89,18 @@ class PagerViewViewManager : ViewGroupManager<NestedScrollableHost>(), RNCViewPa
8889
return host
8990
}
9091

92+
private fun stopScrollIfNeeded(host: NestedScrollableHost) {
93+
val recyclerView = (host.getChildAt(0) as? ViewPager2)?.getChildAt(0) as? RecyclerView
94+
recyclerView?.stopScroll()
95+
}
96+
97+
override fun onDropViewInstance(view: NestedScrollableHost) {
98+
stopScrollIfNeeded(view)
99+
val recyclerView = (view.getChildAt(0) as? ViewPager2)?.getChildAt(0) as? RecyclerView
100+
recyclerView?.swapAdapter(null, false)
101+
super.onDropViewInstance(view)
102+
}
103+
91104
override fun addView(host: NestedScrollableHost, child: View, index: Int) {
92105
PagerViewViewManagerImpl.addView(host, child, index)
93106
}
@@ -99,14 +112,17 @@ class PagerViewViewManager : ViewGroupManager<NestedScrollableHost>(), RNCViewPa
99112
}
100113

101114
override fun removeView(parent: NestedScrollableHost, view: View) {
115+
stopScrollIfNeeded(parent)
102116
PagerViewViewManagerImpl.removeView(parent, view)
103117
}
104118

105119
override fun removeAllViews(parent: NestedScrollableHost) {
120+
stopScrollIfNeeded(parent)
106121
PagerViewViewManagerImpl.removeAllViews(parent)
107122
}
108123

109124
override fun removeViewAt(parent: NestedScrollableHost, index: Int) {
125+
stopScrollIfNeeded(parent)
110126
PagerViewViewManagerImpl.removeViewAt(parent, index)
111127
}
112128

@@ -206,4 +222,4 @@ class PagerViewViewManager : ViewGroupManager<NestedScrollableHost>(), RNCViewPa
206222
PageScrollStateChangedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageScrollStateChanged"),
207223
PageSelectedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageSelected"))
208224
}
209-
}
225+
}

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,7 @@ object PagerViewViewManagerImpl {
7474

7575
fun removeViewAt(parent: NestedScrollableHost, index: Int) {
7676
val pager = getViewPager(parent)
77-
val adapter = pager.adapter as ViewPagerAdapter?
78-
79-
val child = adapter?.getChildAt(index)
80-
81-
if (child != null && child.parent != null) {
82-
(child.parent as? ViewGroup)?.removeView(child)
83-
}
84-
85-
adapter?.removeChildAt(index)
86-
77+
(pager.adapter as? ViewPagerAdapter)?.removeChildAt(index)
8778
debouncedRefreshViewChildrenLayout(pager)
8879
}
8980

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import android.widget.FrameLayout
66
import androidx.recyclerview.widget.RecyclerView.Adapter
77
import java.util.*
88

9+
private fun View.detachFromParent() {
10+
(parent as? ViewGroup)?.removeView(this)
11+
}
912

1013
class ViewPagerAdapter() : Adapter<ViewPagerViewHolder>() {
1114
private val childrenViews: ArrayList<View> = ArrayList()
@@ -17,19 +20,26 @@ class ViewPagerAdapter() : Adapter<ViewPagerViewHolder>() {
1720
override fun onBindViewHolder(holder: ViewPagerViewHolder, index: Int) {
1821
val container: FrameLayout = holder.container
1922
val child = getChildAt(index)
20-
holder.setIsRecyclable(false)
2123

2224
if (container.childCount > 0) {
2325
container.removeAllViews()
2426
}
2527

26-
if (child.parent != null) {
27-
(child.parent as FrameLayout).removeView(child)
28-
}
28+
child.detachFromParent()
2929

3030
container.addView(child)
3131
}
3232

33+
override fun onViewRecycled(holder: ViewPagerViewHolder) {
34+
super.onViewRecycled(holder)
35+
holder.container.removeAllViews()
36+
}
37+
38+
override fun onFailedToRecycleView(holder: ViewPagerViewHolder): Boolean {
39+
holder.container.removeAllViews()
40+
return true
41+
}
42+
3343
override fun getItemCount(): Int {
3444
return childrenViews.size
3545
}
@@ -45,26 +55,24 @@ class ViewPagerAdapter() : Adapter<ViewPagerViewHolder>() {
4555

4656
fun removeChild(child: View) {
4757
val index = childrenViews.indexOf(child)
48-
49-
if(index > -1) {
58+
59+
if (index > -1) {
5060
removeChildAt(index)
5161
}
5262
}
5363

5464
fun removeAll() {
55-
for (index in 1..childrenViews.size) {
56-
val child = childrenViews[index-1]
57-
if (child.parent?.parent != null) {
58-
(child.parent.parent as ViewGroup).removeView(child.parent as View)
59-
}
65+
for (child in childrenViews) {
66+
child.detachFromParent()
6067
}
6168
val removedChildrenCount = childrenViews.size
6269
childrenViews.clear()
6370
notifyItemRangeRemoved(0, removedChildrenCount)
6471
}
6572

6673
fun removeChildAt(index: Int) {
67-
if (index >= 0 && index < childrenViews.size) {
74+
if (index >= 0 && index < childrenViews.size) {
75+
childrenViews[index].detachFromParent()
6876
childrenViews.removeAt(index)
6977
notifyItemRemoved(index)
7078
}

0 commit comments

Comments
 (0)