@@ -4,51 +4,65 @@ import android.content.Context
4
4
import android.view.Choreographer
5
5
import android.view.MenuItem
6
6
import android.view.View
7
+ import android.view.ViewGroup
7
8
import androidx.appcompat.content.res.AppCompatResources
8
9
import com.facebook.react.bridge.Arguments
9
10
import com.facebook.react.bridge.WritableMap
10
11
import com.google.android.material.bottomnavigation.BottomNavigationView
11
12
12
13
class ReactBottomNavigationView (context : Context ) : BottomNavigationView(context) {
13
- private var onTabSelectedListener: ((WritableMap ) -> Unit )? = null
14
+ private val ANIMATION_DURATION : Long = 300
15
+
14
16
var items: MutableList <TabInfo >? = null
17
+ var onTabSelectedListener: ((WritableMap ) -> Unit )? = null
18
+ private var isAnimating = false
19
+ private val frameCallback = Choreographer .FrameCallback {
20
+ if (isAnimating) {
21
+ measureAndLayout()
22
+ }
23
+ }
15
24
16
25
init {
17
- // TODO: Refactor this outside of TabView (attach listener in ViewManager).
18
26
setOnItemSelectedListener { item ->
19
27
onTabSelected(item)
20
28
true
21
29
}
22
30
}
23
31
24
- override fun onLayout (changed : Boolean , left : Int , top : Int , right : Int , bottom : Int ) {
25
- super .onLayout(changed, left, top, right, bottom)
26
- refreshViewChildrenLayout(this )
32
+ override fun requestLayout () {
33
+ super .requestLayout()
34
+ // Manually trigger measure & layout, as RN on Android skips those.
35
+ // See this issue: https://github.com/facebook/react-native/issues/17968#issuecomment-721958427
36
+ this .post {
37
+ measureAndLayout()
38
+ }
27
39
}
28
40
29
41
private fun onTabSelected (item : MenuItem ) {
30
42
val selectedItem = items?.first { it.title == item.title }
31
43
if (selectedItem == null ) {
32
44
return
33
45
}
46
+
47
+ startAnimation()
34
48
35
49
val event = Arguments .createMap().apply {
36
50
putString(" key" , selectedItem.key)
37
51
}
38
52
onTabSelectedListener?.invoke(event)
39
-
40
- // Refresh TabView children to fix issue with animations.
41
- // https://github.com/facebook/react-native/issues/17968#issuecomment-697136929
42
- Choreographer .getInstance().postFrameCallback(object : Choreographer .FrameCallback {
43
- override fun doFrame (frameTimeNanos : Long ) {
44
- refreshViewChildrenLayout(this @ReactBottomNavigationView)
45
- Choreographer .getInstance().postFrameCallback(this )
46
- }
47
- })
48
53
}
49
54
50
- fun setOnTabSelectedListener (listener : (WritableMap ) -> Unit ) {
51
- onTabSelectedListener = listener
55
+ // Refresh TabView children to fix issue with animations.
56
+ // https://github.com/facebook/react-native/issues/17968#issuecomment-697136929
57
+ private fun startAnimation () {
58
+ if (labelVisibilityMode != LABEL_VISIBILITY_AUTO ) {
59
+ return
60
+ }
61
+ isAnimating = true
62
+ Choreographer .getInstance().postFrameCallback(frameCallback)
63
+ postDelayed({
64
+ isAnimating = false
65
+ }, ANIMATION_DURATION )
52
66
}
53
67
54
68
fun updateItems (items : MutableList <TabInfo >) {
@@ -73,18 +87,19 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
73
87
removeBadge(index)
74
88
}
75
89
}
76
-
77
- refreshViewChildrenLayout(this )
78
90
}
79
91
80
-
81
92
// Fixes issues with BottomNavigationView children layouting.
82
- private fun refreshViewChildrenLayout (view : View ) {
83
- view.post {
84
- view.measure(
85
- View .MeasureSpec .makeMeasureSpec(view.width, View .MeasureSpec .EXACTLY ),
86
- View .MeasureSpec .makeMeasureSpec(view.height, View .MeasureSpec .EXACTLY ))
87
- view.layout(view.left, view.top, view.right, view.bottom)
88
- }
93
+ private fun measureAndLayout () {
94
+ viewTreeObserver.dispatchOnGlobalLayout();
95
+ measure(
96
+ MeasureSpec .makeMeasureSpec(width, MeasureSpec .EXACTLY ),
97
+ MeasureSpec .makeMeasureSpec(height, MeasureSpec .EXACTLY ))
98
+ layout(left, top, right, bottom)
99
+ }
100
+
101
+ override fun onDetachedFromWindow () {
102
+ super .onDetachedFromWindow()
103
+ isAnimating = false
89
104
}
90
105
}
0 commit comments