@@ -3,17 +3,20 @@ package com.rcttabview
33import android.annotation.SuppressLint
44import android.content.Context
55import android.content.res.ColorStateList
6+ import android.graphics.Color
67import android.graphics.Typeface
78import android.graphics.drawable.ColorDrawable
89import android.graphics.drawable.Drawable
910import android.os.Build
1011import android.util.Log
1112import android.util.TypedValue
1213import android.view.Choreographer
14+ import android.view.Gravity
1315import android.view.HapticFeedbackConstants
1416import android.view.MenuItem
1517import android.view.View
1618import android.view.ViewGroup
19+ import android.widget.FrameLayout
1720import android.widget.TextView
1821import androidx.appcompat.content.res.AppCompatResources
1922import coil3.ImageLoader
@@ -25,14 +28,20 @@ import com.facebook.react.views.text.ReactTypefaceUtils
2528import com.google.android.material.bottomnavigation.BottomNavigationView
2629import coil3.request.ImageRequest
2730import coil3.svg.SvgDecoder
31+ import com.google.android.material.navigation.NavigationBarView.LABEL_VISIBILITY_AUTO
32+ import com.google.android.material.navigation.NavigationBarView.LABEL_VISIBILITY_LABELED
33+ import com.google.android.material.navigation.NavigationBarView.LABEL_VISIBILITY_UNLABELED
2834
2935
30- class ReactBottomNavigationView (context : Context ) : BottomNavigationView(context) {
36+ class ReactBottomNavigationView (context : Context ) : FrameLayout(context) {
37+ private val bottomNavigation = BottomNavigationView (context)
38+ private val layoutHolder = FrameLayout (context)
3139 private val iconSources: MutableMap <Int , ImageSource > = mutableMapOf ()
3240 private var isLayoutEnqueued = false
3341 var items: MutableList <TabInfo >? = null
3442 var onTabSelectedListener: ((key: String ) -> Unit )? = null
3543 var onTabLongPressedListener: ((key: String ) -> Unit )? = null
44+ var onNativeLayoutListener: ((width: Double , height: Double ) -> Unit )? = null
3645 private var activeTintColor: Int? = null
3746 private var inactiveTintColor: Int? = null
3847 private val checkedStateSet = intArrayOf(android.R .attr.state_checked)
@@ -42,6 +51,50 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
4251 private var fontFamily: String? = null
4352 private var fontWeight: Int? = null
4453
54+ init {
55+ val layoutHolderFrameLayout = LayoutParams (
56+ LayoutParams .MATCH_PARENT ,
57+ LayoutParams .MATCH_PARENT
58+ )
59+ addView(layoutHolder, layoutHolderFrameLayout)
60+
61+ val bottomNavParams = LayoutParams (
62+ LayoutParams .MATCH_PARENT ,
63+ LayoutParams .WRAP_CONTENT
64+ ).apply {
65+ gravity = Gravity .BOTTOM
66+ }
67+
68+ addView(bottomNavigation, bottomNavParams)
69+
70+ post {
71+ this .addOnLayoutChangeListener { _, left, top, right, bottom,
72+ oldLeft, oldTop, oldRight, oldBottom ->
73+ val newWidth = right - left
74+ val newHeight = bottom - top
75+ val oldWidth = oldRight - oldLeft
76+ val oldHeight = oldBottom - oldTop
77+ if (newWidth != oldWidth || newHeight != oldHeight) {
78+ val availableHeight = height - bottomNavigation.height
79+ val displayDensity = context.resources.displayMetrics.density
80+
81+ val dpWidth = (width / displayDensity).toDouble()
82+ val dpHeight = (availableHeight / displayDensity).toDouble()
83+
84+ onNativeLayoutListener?.invoke(dpWidth, dpHeight)
85+ }
86+ }
87+ }
88+ }
89+
90+ override fun addView (child : View , index : Int , params : ViewGroup .LayoutParams ? ) {
91+ if (child == = layoutHolder || child == = bottomNavigation) {
92+ super .addView(child, index, params)
93+ } else {
94+ layoutHolder.addView(child, params)
95+ }
96+ }
97+
4598 private val imageLoader = ImageLoader .Builder (context)
4699 .components {
47100 add(SvgDecoder .Factory ())
@@ -105,11 +158,11 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
105158 }
106159
107160 if (item.badge.isNotEmpty()) {
108- val badge = this .getOrCreateBadge(index)
161+ val badge = bottomNavigation .getOrCreateBadge(index)
109162 badge.isVisible = true
110163 badge.text = item.badge
111164 } else {
112- removeBadge(index)
165+ bottomNavigation. removeBadge(index)
113166 }
114167 post {
115168 val itemView = findViewById<View >(menuItem.itemId)
@@ -136,7 +189,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
136189 }
137190
138191 private fun getOrCreateItem (index : Int , title : String ): MenuItem {
139- return menu.findItem(index) ? : menu.add(0 , index, 0 , title)
192+ return bottomNavigation. menu.findItem(index) ? : bottomNavigation. menu.add(0 , index, 0 , title)
140193 }
141194
142195 fun setIcons (icons : ReadableArray ? ) {
@@ -155,7 +208,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
155208 this .iconSources[idx] = imageSource
156209
157210 // Update existing item if exists.
158- menu.findItem(idx)?.let { menuItem ->
211+ bottomNavigation. menu.findItem(idx)?.let { menuItem ->
159212 getDrawable(imageSource) {
160213 menuItem.icon = it
161214 }
@@ -164,7 +217,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
164217 }
165218
166219 fun setLabeled (labeled : Boolean? ) {
167- labelVisibilityMode = when (labeled) {
220+ bottomNavigation. labelVisibilityMode = when (labeled) {
168221 false -> {
169222 LABEL_VISIBILITY_UNLABELED
170223 }
@@ -178,7 +231,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
178231 }
179232
180233 fun setRippleColor (color : ColorStateList ) {
181- itemRippleColor = color
234+ bottomNavigation. itemRippleColor = color
182235 }
183236
184237 @SuppressLint(" CheckResult" )
@@ -205,7 +258,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
205258 // Apply the same color to both active and inactive states
206259 val colorDrawable = ColorDrawable (backgroundColor)
207260
208- itemBackground = colorDrawable
261+ bottomNavigation. itemBackground = colorDrawable
209262 backgroundTintList = ColorStateList .valueOf(backgroundColor)
210263 }
211264
@@ -220,7 +273,7 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
220273 }
221274
222275 fun setActiveIndicatorColor (color : ColorStateList ) {
223- itemActiveIndicatorColor = color
276+ bottomNavigation. itemActiveIndicatorColor = color
224277 }
225278
226279 fun setHapticFeedback (enabled : Boolean ) {
@@ -248,6 +301,10 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
248301 else -> Typeface .NORMAL
249302 }
250303
304+ fun setSelectedItemId (itemId : Int ) {
305+ bottomNavigation.selectedItemId = itemId
306+ }
307+
251308 private fun updateTextAppearance () {
252309 if (fontSize != null || fontFamily != null || fontWeight != null ) {
253310 val menuView = getChildAt(0 ) as ? ViewGroup ? : return
@@ -293,8 +350,8 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context
293350 val colors = intArrayOf(colorSecondary, colorPrimary)
294351
295352 ColorStateList (states, colors).apply {
296- this @ReactBottomNavigationView.itemTextColor = this
297- this @ReactBottomNavigationView.itemIconTintList = this
353+ this @ReactBottomNavigationView.bottomNavigation. itemTextColor = this
354+ this @ReactBottomNavigationView.bottomNavigation. itemIconTintList = this
298355 }
299356 }
300357
0 commit comments