Skip to content

Commit f90ea5a

Browse files
committed
fix: Android animations
1 parent a3c1f65 commit f90ea5a

File tree

2 files changed

+41
-28
lines changed

2 files changed

+41
-28
lines changed
Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.rcttabview
22

33
import android.content.Context
4+
import android.util.Log
45
import android.view.Choreographer
56
import android.view.MenuItem
67
import android.view.View
@@ -10,45 +11,57 @@ import com.facebook.react.bridge.WritableMap
1011
import com.google.android.material.bottomnavigation.BottomNavigationView
1112

1213
class 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
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ class RCTTabViewViewManager :
6060
public override fun createViewInstance(context: ThemedReactContext): ReactBottomNavigationView {
6161
eventDispatcher = context.getNativeModule(UIManagerModule::class.java)!!.eventDispatcher
6262
val view = ReactBottomNavigationView(context)
63-
view.setOnTabSelectedListener { data ->
64-
data.getString("key")?.let {
63+
view.onTabSelectedListener = { data ->
64+
data.getString("key")?.let {
6565
eventDispatcher.dispatchEvent(PageSelectedEvent(viewTag = view.id, key = it ))
6666
}
6767
}

0 commit comments

Comments
 (0)