Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: improve BottomNavigation (experimental feature) #14126

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cee32ae
xml test
m1ga Sep 27, 2024
4d7916b
badge
m1ga Sep 29, 2024
a89b6cd
use old tabgroup layout
m1ga Oct 2, 2024
4978b22
theme
m1ga Oct 2, 2024
9f3b4c2
more features
m1ga Oct 2, 2024
aa21b57
Merge branch 'master' into bottomNavigationXML
m1ga Oct 5, 2024
c1294ec
more colors
m1ga Oct 5, 2024
0851417
touchfeedback
m1ga Oct 9, 2024
dd1615d
res icons
m1ga Oct 15, 2024
62a6168
add addTab()
m1ga Oct 17, 2024
ff6d9d5
Merge branch 'master' into bottomNavigationXML
m1ga Oct 17, 2024
a657695
Merge branch 'master' into bottomNavigationXML
m1ga Oct 17, 2024
20cda85
remove redundant code, floating bar
m1ga Oct 17, 2024
48c31b0
icon font support
m1ga Oct 19, 2024
a057fb5
tintColor support for icons
m1ga Oct 19, 2024
6536b01
Merge branch 'master' into bottomNavigationXML
m1ga Oct 19, 2024
6e0ddfc
optimize selected state
m1ga Oct 20, 2024
85fc7a6
color updates
m1ga Oct 20, 2024
706cf01
add flags
m1ga Oct 20, 2024
cb1da13
status bar
m1ga Oct 21, 2024
23b1f88
Merge branch 'master' into bottomNavigationXML
m1ga Dec 14, 2024
a1930c2
chore: apidoc
m1ga Dec 14, 2024
42ff775
update property
m1ga Dec 15, 2024
fa95329
Merge branch 'master' into bottomNavigationXML
m1ga Dec 21, 2024
92c117a
Merge branch 'master' into bottomNavigationXML
m1ga Jan 2, 2025
3a52011
events
m1ga Jan 2, 2025
dbceb8e
test, bugfix for empty title
m1ga Jan 2, 2025
dec1971
add enabled property
m1ga Jan 15, 2025
a6f57f9
docs
m1ga Jan 15, 2025
ac5a629
Merge branch 'master' into bottomNavigationXML
m1ga Jan 15, 2025
60c5047
fix WindowProxy
m1ga Jan 15, 2025
4df0d81
fix WindowProxy
m1ga Jan 15, 2025
ba637af
fix linting
m1ga Jan 15, 2025
ad2748e
add back windowFlags for light status bar
m1ga Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
TiC.PROPERTY_SWIPEABLE,
TiC.PROPERTY_AUTO_TAB_TITLE,
TiC.PROPERTY_EXIT_ON_CLOSE,
TiC.PROPERTY_SMOOTH_SCROLL_ON_TAB_CLICK
TiC.PROPERTY_SMOOTH_SCROLL_ON_TAB_CLICK,
TiC.PROPERTY_INDICATOR_COLOR
})
public class TabGroupProxy extends TiWindowProxy implements TiActivityWindow
{
Expand Down Expand Up @@ -326,7 +327,8 @@ protected void handleOpen(KrollDict options)
}

// set theme for XML layout
if (((Integer) getProperty(TiC.PROPERTY_STYLE)) == AndroidModule.TABS_STYLE_BOTTOM_NAVIGATION
if (hasProperty(TiC.PROPERTY_STYLE)
&& ((Integer) getProperty(TiC.PROPERTY_STYLE)) == AndroidModule.TABS_STYLE_BOTTOM_NAVIGATION
&& getProperty(TiC.PROPERTY_THEME) != null) {
try {
String themeName = getProperty(TiC.PROPERTY_THEME).toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ public abstract class TiUIAbstractTabGroup extends TiUIView
*/
public abstract void updateTabBackgroundDrawable(int index);

/**
* Material 3 active indicator color
hansemannn marked this conversation as resolved.
Show resolved Hide resolved
*
* @param color color
*/
public abstract void updateActiveIndicatorColor(int color);

/**
* Update the tab's title to the proper text.
*
Expand Down Expand Up @@ -495,6 +502,9 @@ public void processProperties(KrollDict d)
} else {
setBackgroundColor(getDefaultBackgroundColor());
}
if (d.containsKeyAndNotNull(TiC.PROPERTY_INDICATOR_COLOR)) {
updateActiveIndicatorColor(TiConvert.toColor(d, TiC.PROPERTY_INDICATOR_COLOR, proxy.getActivity()));
}
super.processProperties(d);
}

Expand All @@ -516,6 +526,8 @@ public void propertyChanged(String key, Object oldValue, Object newValue, KrollP
for (TiUITab tabView : tabs) {
updateTabBackgroundDrawable(tabs.indexOf(tabView));
}
} else if (key.equals(TiC.PROPERTY_INDICATOR_COLOR)) {
updateActiveIndicatorColor(TiColorHelper.parseColor(newValue.toString(), proxy.getActivity()));
} else {
super.propertyChanged(key, oldValue, newValue, proxy);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@
package ti.modules.titanium.ui.widget.tabgroup;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;

import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.bottomnavigation.BottomNavigationItemView;
import com.google.android.material.bottomnavigation.BottomNavigationMenuView;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.shape.MaterialShapeDrawable;

import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.TiBaseActivity;
Expand All @@ -42,6 +52,7 @@ public class TiUIBottomNavigation extends TiUIAbstractTabGroup implements Bottom
static int id_layout = 0;
static int id_content = 0;
static int id_bottomNavigation = 0;
private final int currentlySelectedIndex = -1;
private ArrayList<MenuItem> mMenuItemsArray = new ArrayList<>();
private RelativeLayout layout = null;
private FrameLayout centerView;
Expand All @@ -67,7 +78,7 @@ public void setTabs(Object tabs)
tabsArray = objArray;
for (Object tabView : tabsArray) {
if (tabView instanceof TabProxy tp) {
MenuItem menuItem = bottomNavigation.getMenu().add(0, mMenuItemsArray.size(), 0, "");
MenuItem menuItem = bottomNavigation.getMenu().add(0, mMenuItemsArray.size(), 0, "");
menuItem.setTitle(tp.getProperty(TiC.PROPERTY_TITLE).toString());
Drawable drawable = TiUIHelper.getResourceDrawable(tp.getProperty(TiC.PROPERTY_ICON));
menuItem.setIcon(drawable);
Expand Down Expand Up @@ -98,6 +109,7 @@ public void addViews(TiBaseActivity activity)
setTabs(proxy.getProperty(TiC.PROPERTY_TABS));
selectTab(0);
}

} catch (Exception ex) {
Log.e(TAG, "XML resources could not be found!!!" + ex.getMessage());
}
Expand Down Expand Up @@ -145,19 +157,72 @@ public void selectTabItemInController(int position)
@Override
public void setBackgroundColor(int colorInt)
{
// Update tab bar's background color.
Drawable drawable = bottomNavigation.getBackground();
if (drawable instanceof MaterialShapeDrawable shapeDrawable) {
shapeDrawable.setFillColor(ColorStateList.valueOf(colorInt));
shapeDrawable.setElevation(0); // Drawable will tint the fill color if elevation is non-zero.
} else {
bottomNavigation.setBackgroundColor(colorInt);
}

// Apply given color to bottom navigation bar if using a "solid" theme.
if (isUsingSolidTitaniumTheme() && (Build.VERSION.SDK_INT >= 27)) {
Activity activity = (this.proxy != null) ? this.proxy.getActivity() : null;
Window window = (activity != null) ? activity.getWindow() : null;
View decorView = (window != null) ? window.getDecorView() : null;
if ((window != null) && (decorView != null)) {
int uiFlags = decorView.getSystemUiVisibility();
if (ColorUtils.calculateLuminance(colorInt) > 0.5) {
uiFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
} else {
uiFlags &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
decorView.setSystemUiVisibility(uiFlags);
window.setNavigationBarColor(colorInt);
}
}
}

@Override
@SuppressLint("RestrictedApi")
public void updateTabBackgroundDrawable(int index)
{

try {
// BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time.
TiViewProxy tabProxy = tabs.get(index).getProxy();
boolean hasTouchFeedbackColor = tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_TOUCH_FEEDBACK_COLOR);
if (hasCustomBackground(tabProxy) || hasCustomIconTint(tabProxy) || hasTouchFeedbackColor) {
BottomNavigationMenuView bottomMenuView =
((BottomNavigationMenuView) this.bottomNavigation.getChildAt(0));
Drawable drawable = createBackgroundDrawableForState(tabProxy, android.R.attr.state_checked);
int color = getActiveColor(tabProxy);
if (hasTouchFeedbackColor) {
color = TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_TOUCH_FEEDBACK_COLOR),
tabProxy.getActivity());
}
drawable = new RippleDrawable(createRippleColorStateListFrom(color), drawable, null);
bottomMenuView.getChildAt(index).setBackground(drawable);
}
} catch (Exception e) {
org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE);
}
}

@Override
public void updateTabTitle(int index)
{
if ((index < 0) || (index >= this.tabs.size())) {
return;
}

TiViewProxy tabProxy = this.tabs.get(index).getProxy();
if (tabProxy == null) {
return;
}

String title = TiConvert.toString(tabProxy.getProperty(TiC.PROPERTY_TITLE));
this.bottomNavigation.getMenu().getItem(index).setTitle(title);
}

@SuppressLint("RestrictedApi")
Expand Down Expand Up @@ -191,25 +256,122 @@ public void updateBadge(int index)
@Override
public void updateBadgeColor(int index)
{
if ((index < 0) || (index >= this.tabs.size())) {
return;
}

TiViewProxy tabProxy = this.tabs.get(index).getProxy();
if (tabProxy == null) {
return;
}

// TODO: reset to default value when property is null
if (tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_BADGE_COLOR)) {
org.appcelerator.kroll.common.Log.w(TAG, "badgeColor is deprecated. Use badgeBackgroundColor instead.");
int menuItemId = this.bottomNavigation.getMenu().getItem(index).getItemId();
BadgeDrawable badgeDrawable = this.bottomNavigation.getOrCreateBadge(menuItemId);
badgeDrawable.setBackgroundColor(
TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_BADGE_COLOR), tabProxy.getActivity()));
}
if (tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_BADGE_BACKGROUND_COLOR)) {
int menuItemId = this.bottomNavigation.getMenu().getItem(index).getItemId();
BadgeDrawable badgeDrawable = this.bottomNavigation.getOrCreateBadge(menuItemId);
badgeDrawable.setBackgroundColor(
TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_BADGE_BACKGROUND_COLOR), tabProxy.getActivity()));
}
if (tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_BADGE_TEXT_COLOR)) {
int menuItemId = this.bottomNavigation.getMenu().getItem(index).getItemId();
BadgeDrawable badgeDrawable = this.bottomNavigation.getOrCreateBadge(menuItemId);
badgeDrawable.setBadgeTextColor(
TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_BADGE_TEXT_COLOR), tabProxy.getActivity()));
}
}

@Override
@SuppressLint("RestrictedApi")
public void updateTabTitleColor(int index)
{
try {
// BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time.
TiViewProxy tabProxy = tabs.get(index).getProxy();
if (hasCustomTextColor(tabProxy)) {
// Set the TextView textColor.
BottomNavigationMenuView bottomMenuView =
((BottomNavigationMenuView) this.bottomNavigation.getChildAt(0));
((BottomNavigationItemView) bottomMenuView.getChildAt(index))
.setTextColor(textColorStateList(tabProxy, android.R.attr.state_checked));
}
} catch (Exception e) {
org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE);
}
}

@SuppressLint("RestrictedApi")
public void updateActiveIndicatorColor(int color)
{
try {
// BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time.

int[][] states = new int[][] {
new int[] { android.R.attr.state_enabled }, // enabled
new int[] { -android.R.attr.state_enabled }, // disabled
new int[] { -android.R.attr.state_checked }, // unchecked
new int[] { android.R.attr.state_pressed } // pressed
};

int[] colors = new int[] {
color,
color,
color,
color
};

ColorStateList myList = new ColorStateList(states, colors);

bottomNavigation.setItemActiveIndicatorColor(myList);
} catch (Exception e) {
org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE);
}
}

@Override
public void updateTabIcon(int index)
{
if ((index < 0) || (index >= this.tabs.size())) {
return;
}

TiViewProxy tabProxy = this.tabs.get(index).getProxy();
if (tabProxy == null) {
return;
}

final Drawable drawable = TiUIHelper.getResourceDrawable(tabProxy.getProperty(TiC.PROPERTY_ICON));
this.bottomNavigation.getMenu().getItem(index).setIcon(drawable);
updateIconTint();
}

private void updateIconTint()
{
for (int i = 0; i < this.tabs.size(); i++) {
final TiViewProxy tabProxy = this.tabs.get(i).getProxy();
if (hasCustomIconTint(tabProxy)) {
final boolean selected = i == currentlySelectedIndex;
Drawable drawable = this.bottomNavigation.getMenu().getItem(i).getIcon();
drawable = updateIconTint(tabProxy, drawable, selected);
this.bottomNavigation.getMenu().getItem(i).setIcon(drawable);
}
}
}

@Override
public String getTabTitle(int index)
{
return "";
// Validate index.
if (index < 0 || index > tabs.size() - 1) {
return null;
}
return this.bottomNavigation.getMenu().getItem(index).getTitle().toString();
}

@Override
Expand All @@ -234,6 +396,7 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item)
{
item.setChecked(true);
selectTab(item.getItemId());
((TabGroupProxy) getProxy()).onTabSelected(item.getItemId());
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,12 @@ public void updateTabBackgroundDrawable(int index)
}
}

@Override
public void updateActiveIndicatorColor(int color)
{

}

@Override
public void updateTabTitle(int index)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ public void updateTabBackgroundDrawable(int index)
this.mTabLayout.setBackground(backgroundDrawable);
}

@Override
public void updateActiveIndicatorColor(int color)
{

}

@Override
public void updateTabTitle(int index)
{
Expand Down
Loading