Skip to content

Commit c0990a9

Browse files
authored
Merge pull request #6 from okwasniewski/fix/android-animations
fix: Android animations
2 parents a3c1f65 + c3dbd31 commit c0990a9

File tree

2 files changed

+43
-28
lines changed

2 files changed

+43
-28
lines changed

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

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,65 @@ import android.content.Context
44
import android.view.Choreographer
55
import android.view.MenuItem
66
import android.view.View
7+
import android.view.ViewGroup
78
import androidx.appcompat.content.res.AppCompatResources
89
import com.facebook.react.bridge.Arguments
910
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
14+
private val ANIMATION_DURATION: Long = 300
15+
1416
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+
}
1524

1625
init {
17-
// TODO: Refactor this outside of TabView (attach listener in ViewManager).
1826
setOnItemSelectedListener { item ->
1927
onTabSelected(item)
2028
true
2129
}
2230
}
2331

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+
}
2739
}
2840

2941
private fun onTabSelected(item: MenuItem) {
3042
val selectedItem = items?.first { it.title == item.title }
3143
if (selectedItem == null) {
3244
return
3345
}
46+
47+
startAnimation()
3448

3549
val event = Arguments.createMap().apply {
3650
putString("key", selectedItem.key)
3751
}
3852
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-
})
4853
}
4954

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)
5266
}
5367

5468
fun updateItems(items: MutableList<TabInfo>) {
@@ -73,18 +87,19 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
7387
removeBadge(index)
7488
}
7589
}
76-
77-
refreshViewChildrenLayout(this)
7890
}
7991

80-
8192
// 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
89104
}
90105
}

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)