@@ -14,22 +14,26 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder
14
14
import com.facebook.react.bridge.Arguments
15
15
import com.facebook.react.bridge.ReadableArray
16
16
import com.facebook.react.bridge.WritableMap
17
+ import com.facebook.react.modules.core.ReactChoreographer
17
18
import com.facebook.react.views.imagehelper.ImageSource
18
19
import com.facebook.react.views.imagehelper.ImageSource.Companion.getTransparentBitmapImageSource
19
20
import com.google.android.material.bottomnavigation.BottomNavigationView
20
21
21
22
22
23
class ReactBottomNavigationView (context : Context ) : BottomNavigationView(context) {
23
- private val ANIMATION_DURATION : Long = 300
24
24
private val icons: MutableMap <Int , ImageSource > = mutableMapOf ()
25
-
25
+ private var isLayoutEnqueued = false
26
26
var items: MutableList <TabInfo >? = null
27
27
var onTabSelectedListener: ((WritableMap ) -> Unit )? = null
28
28
private var isAnimating = false
29
- private val frameCallback = Choreographer .FrameCallback {
30
- if (isAnimating) {
31
- measureAndLayout()
32
- }
29
+
30
+ private val layoutCallback = Choreographer .FrameCallback {
31
+ isLayoutEnqueued = false
32
+ measure(
33
+ MeasureSpec .makeMeasureSpec(width, MeasureSpec .EXACTLY ),
34
+ MeasureSpec .makeMeasureSpec(height, MeasureSpec .EXACTLY ),
35
+ )
36
+ layout(left, top, right, bottom)
33
37
}
34
38
35
39
init {
@@ -41,35 +45,31 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
41
45
42
46
override fun requestLayout () {
43
47
super .requestLayout()
44
- // Manually trigger measure & layout, as RN on Android skips those.
45
- // See this issue: https://github.com/facebook/react-native/issues/17968#issuecomment-721958427
46
- this .post {
47
- measureAndLayout()
48
+ @Suppress(" SENSELESS_COMPARISON" ) // layoutCallback can be null here since this method can be called in init
49
+ if (! isLayoutEnqueued && layoutCallback != null ) {
50
+ isLayoutEnqueued = true
51
+ // we use NATIVE_ANIMATED_MODULE choreographer queue because it allows us to catch the current
52
+ // looper loop instead of enqueueing the update in the next loop causing a one frame delay.
53
+ ReactChoreographer
54
+ .getInstance()
55
+ .postFrameCallback(
56
+ ReactChoreographer .CallbackType .NATIVE_ANIMATED_MODULE ,
57
+ layoutCallback,
58
+ )
48
59
}
49
60
}
50
61
51
62
private fun onTabSelected (item : MenuItem ) {
63
+ if (isLayoutEnqueued) {
64
+ return ;
65
+ }
52
66
val selectedItem = items?.first { it.title == item.title }
53
67
selectedItem?.let {
54
68
val event = Arguments .createMap().apply {
55
69
putString(" key" , selectedItem.key)
56
70
}
57
71
onTabSelectedListener?.invoke(event)
58
- startAnimation()
59
- }
60
- }
61
-
62
- // Refresh TabView children to fix issue with animations.
63
- // https://github.com/facebook/react-native/issues/17968#issuecomment-697136929
64
- private fun startAnimation () {
65
- if (labelVisibilityMode != LABEL_VISIBILITY_AUTO ) {
66
- return
67
72
}
68
- isAnimating = true
69
- Choreographer .getInstance().postFrameCallback(frameCallback)
70
- postDelayed({
71
- isAnimating = false
72
- }, ANIMATION_DURATION )
73
73
}
74
74
75
75
fun updateItems (items : MutableList <TabInfo >) {
@@ -117,6 +117,16 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
117
117
}
118
118
}
119
119
120
+ fun setConfig (config : TabViewConfig ) {
121
+ labelVisibilityMode = if (config.labeled == false ) {
122
+ LABEL_VISIBILITY_UNLABELED
123
+ } else if (config.labeled == true ) {
124
+ LABEL_VISIBILITY_LABELED
125
+ } else {
126
+ LABEL_VISIBILITY_AUTO
127
+ }
128
+ }
129
+
120
130
private fun getDrawable (imageSource : ImageSource ): Drawable {
121
131
// TODO: Check if this can be done using some built-in React Native class
122
132
val imageRequest = ImageRequestBuilder .newBuilderWithSource(imageSource.uri).build()
@@ -130,14 +140,6 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
130
140
return BitmapDrawable (resources, bitmap)
131
141
}
132
142
133
- // Fixes issues with BottomNavigationView children layouting.
134
- private fun measureAndLayout () {
135
- measure(
136
- MeasureSpec .makeMeasureSpec(width, MeasureSpec .EXACTLY ),
137
- MeasureSpec .makeMeasureSpec(height, MeasureSpec .EXACTLY ))
138
- layout(left, top, right, bottom)
139
- }
140
-
141
143
override fun onDetachedFromWindow () {
142
144
super .onDetachedFromWindow()
143
145
isAnimating = false
0 commit comments