11package com.rcttabview
22
33import android.content.Context
4+ import android.util.Log
45import android.view.Choreographer
56import android.view.MenuItem
67import android.view.View
@@ -10,45 +11,57 @@ import com.facebook.react.bridge.WritableMap
1011import com.google.android.material.bottomnavigation.BottomNavigationView
1112
1213class ReactBottomNavigationView (context : Context ) : BottomNavigationView(context) {
13- private var onTabSelectedListener: ((WritableMap ) -> Unit )? = null
1414 var items: MutableList <TabInfo >? = null
15+ var onTabSelectedListener: ((WritableMap ) -> Unit )? = null
16+ private var isAnimating = false
17+ private val frameCallback = object : Choreographer .FrameCallback {
18+ override fun doFrame (frameTimeNanos : Long ) {
19+ if (isAnimating) {
20+ measureAndLayout(this @ReactBottomNavigationView)
21+ this @ReactBottomNavigationView.viewTreeObserver.dispatchOnGlobalLayout();
22+
23+ Choreographer .getInstance().postFrameCallback(this )
24+ }
25+ }
26+ }
1527
1628 init {
17- // TODO: Refactor this outside of TabView (attach listener in ViewManager).
1829 setOnItemSelectedListener { item ->
1930 onTabSelected(item)
2031 true
2132 }
2233 }
2334
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 )
35+ override fun requestLayout () {
36+ super .requestLayout()
37+ // Manually trigger measure & layout, as RN on Android skips those.
38+ // See this issue: https://github.com/facebook/react-native/issues/17968#issuecomment-721958427
39+ this .post {
40+ measureAndLayout(this )
41+ }
2742 }
2843
2944 private fun onTabSelected (item : MenuItem ) {
3045 val selectedItem = items?.first { it.title == item.title }
3146 if (selectedItem == null ) {
3247 return
3348 }
49+ startAnimation()
3450
3551 val event = Arguments .createMap().apply {
3652 putString(" key" , selectedItem.key)
3753 }
3854 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- })
4855 }
4956
50- fun setOnTabSelectedListener (listener : (WritableMap ) -> Unit ) {
51- onTabSelectedListener = listener
57+ // Refresh TabView children to fix issue with animations.
58+ // https://github.com/facebook/react-native/issues/17968#issuecomment-697136929
59+ private fun startAnimation () {
60+ isAnimating = true
61+ Choreographer .getInstance().postFrameCallback(frameCallback)
62+ postDelayed({
63+ isAnimating = false
64+ }, 300 )
5265 }
5366
5467 fun updateItems (items : MutableList <TabInfo >) {
@@ -73,18 +86,18 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
7386 removeBadge(index)
7487 }
7588 }
76-
77- refreshViewChildrenLayout(this )
7889 }
7990
80-
8191 // 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- }
92+ private fun measureAndLayout (view : View ) {
93+ measure(
94+ MeasureSpec .makeMeasureSpec(width, MeasureSpec .EXACTLY ),
95+ MeasureSpec .makeMeasureSpec(height, MeasureSpec .EXACTLY ))
96+ layout(left, top, right, bottom)
97+ }
98+
99+ override fun onDetachedFromWindow () {
100+ super .onDetachedFromWindow()
101+ isAnimating = false
89102 }
90103}
0 commit comments