Skip to content

Commit ead7d0b

Browse files
committed
fix: tabview animations on Android
1 parent 41f8e08 commit ead7d0b

File tree

2 files changed

+25
-33
lines changed

2 files changed

+25
-33
lines changed

android/src/main/java/com/rcttabview/RCTTabView.kt

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,26 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder
1414
import com.facebook.react.bridge.Arguments
1515
import com.facebook.react.bridge.ReadableArray
1616
import com.facebook.react.bridge.WritableMap
17+
import com.facebook.react.modules.core.ReactChoreographer
1718
import com.facebook.react.views.imagehelper.ImageSource
1819
import com.facebook.react.views.imagehelper.ImageSource.Companion.getTransparentBitmapImageSource
1920
import com.google.android.material.bottomnavigation.BottomNavigationView
2021

2122

2223
class ReactBottomNavigationView(context: Context) : BottomNavigationView(context) {
23-
private val ANIMATION_DURATION: Long = 300
2424
private val icons: MutableMap<Int, ImageSource> = mutableMapOf()
25-
25+
private var isLayoutEnqueued = false
2626
var items: MutableList<TabInfo>? = null
2727
var onTabSelectedListener: ((WritableMap) -> Unit)? = null
2828
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)
3337
}
3438

3539
init {
@@ -41,37 +45,33 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
4145

4246
override fun requestLayout() {
4347
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") // mLayoutCallback 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+
)
4859
}
4960
}
5061

5162
private fun onTabSelected(item: MenuItem) {
63+
if (isLayoutEnqueued) {
64+
return;
65+
}
5266
val selectedItem = items?.first { it.title == item.title }
5367
selectedItem?.let {
5468
val event = Arguments.createMap().apply {
5569
putString("key", selectedItem.key)
5670
}
5771
onTabSelectedListener?.invoke(event)
58-
startAnimation()
5972
}
6073
}
6174

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-
}
68-
isAnimating = true
69-
Choreographer.getInstance().postFrameCallback(frameCallback)
70-
postDelayed({
71-
isAnimating = false
72-
}, ANIMATION_DURATION)
73-
}
74-
7575
fun updateItems(items: MutableList<TabInfo>) {
7676
this.items = items
7777
items.forEachIndexed {index, item ->
@@ -140,14 +140,6 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
140140
return BitmapDrawable(resources, bitmap)
141141
}
142142

143-
// Fixes issues with BottomNavigationView children layouting.
144-
private fun measureAndLayout() {
145-
measure(
146-
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
147-
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
148-
layout(left, top, right, bottom)
149-
}
150-
151143
override fun onDetachedFromWindow() {
152144
super.onDetachedFromWindow()
153145
isAnimating = false

android/src/main/java/com/rcttabview/RCTTabViewViewManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class RCTTabViewViewManager :
117117
this.mHeight = tabView.measuredHeight
118118
this.mMeasured = true
119119

120-
return YogaMeasureOutput.make(mWidth, mHeight)
120+
return YogaMeasureOutput.make(400, mHeight)
121121
}
122122
}
123123

0 commit comments

Comments
 (0)