Skip to content

Commit ef58a6c

Browse files
authored
Support tabs without icons on Android (#5978)
This commit introduces parity with iOS which allows the user to show tabs without icons. While there's no real production use case for this feature, it's helpful when getting started with a new project.
1 parent 30b0b47 commit ef58a6c

File tree

4 files changed

+63
-27
lines changed

4 files changed

+63
-27
lines changed

lib/android/app/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ dependencies {
161161
implementation 'androidx.annotation:annotation:1.1.0'
162162
implementation 'com.google.android.material:material:1.2.0-alpha03'
163163

164-
implementation 'com.github.wix-playground:ahbottomnavigation:3.1.2'
164+
implementation 'com.github.wix-playground:ahbottomnavigation:3.2.0'
165165
implementation 'com.github.wix-playground:reflow-animator:1.0.6'
166166
implementation 'com.github.clans:fab:1.6.4'
167167

lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsPresenter.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector;
1414
import com.reactnativenavigation.views.BottomTabs;
1515

16+
import org.jetbrains.annotations.NotNull;
17+
1618
import java.util.List;
1719

1820
import androidx.annotation.IntRange;
@@ -118,7 +120,7 @@ private void applyBottomTabsOptions(Options options) {
118120

119121
bottomTabs.setLayoutDirection(options.layout.direction);
120122
bottomTabs.setPreferLargeIcons(options.bottomTabsOptions.preferLargeIcons.get(false));
121-
bottomTabs.setTitleState(bottomTabsOptions.titleDisplayMode.get(TitleState.SHOW_WHEN_ACTIVE));
123+
bottomTabs.setTitleState(bottomTabsOptions.titleDisplayMode.get(getDefaultTitleState()));
122124
bottomTabs.setBackgroundColor(bottomTabsOptions.backgroundColor.get(Color.WHITE));
123125
if (bottomTabsOptions.currentTabIndex.hasValue()) {
124126
int tabIndex = bottomTabsOptions.currentTabIndex.get();
@@ -148,6 +150,14 @@ private void applyBottomTabsOptions(Options options) {
148150
}
149151
}
150152

153+
@NotNull
154+
private TitleState getDefaultTitleState() {
155+
for (int i = 0; i < bottomTabs.getItemsCount(); i++) {
156+
if (bottomTabs.getItem(i).hasIcon()) return TitleState.SHOW_WHEN_ACTIVE;
157+
}
158+
return TitleState.ALWAYS_SHOW;
159+
}
160+
151161
public void applyBottomInset(int bottomInset) {
152162
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) bottomTabs.getLayoutParams();
153163
lp.bottomMargin = bottomInset;

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private List<AHBottomNavigationItem> createTabs() {
164164
BottomTabOptions options = tab.resolveCurrentOptions().bottomTabOptions;
165165
return new AHBottomNavigationItem(
166166
options.text.get(""),
167-
imageLoader.loadIcon(getActivity(), options.icon.get()),
167+
imageLoader.loadIcon(getActivity(), options.icon.get(null)),
168168
imageLoader.loadIcon(getActivity(), options.selectedIcon.get(null)),
169169
options.testId.get("")
170170
);

lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.java

+50-24
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import android.view.View;
77
import android.view.ViewGroup.MarginLayoutParams;
88

9+
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
910
import com.reactnativenavigation.BaseTest;
1011
import com.reactnativenavigation.TestUtils;
1112
import com.reactnativenavigation.mocks.ImageLoaderMock;
1213
import com.reactnativenavigation.mocks.SimpleViewController;
1314
import com.reactnativenavigation.parse.Options;
1415
import com.reactnativenavigation.parse.params.Bool;
1516
import com.reactnativenavigation.parse.params.Colour;
17+
import com.reactnativenavigation.parse.params.NullText;
1618
import com.reactnativenavigation.parse.params.Number;
1719
import com.reactnativenavigation.parse.params.Text;
1820
import com.reactnativenavigation.presentation.BottomTabPresenter;
@@ -43,6 +45,7 @@
4345
import edu.emory.mathcs.backport.java.util.Collections;
4446

4547
import static com.reactnativenavigation.TestUtils.hideBackButton;
48+
import static com.reactnativenavigation.utils.ObjectUtils.perform;
4649
import static org.assertj.core.api.Java6Assertions.assertThat;
4750
import static org.mockito.ArgumentMatchers.any;
4851
import static org.mockito.ArgumentMatchers.eq;
@@ -74,32 +77,9 @@ public class BottomTabsControllerTest extends BaseTest {
7477
@Override
7578
public void beforeEach() {
7679
activity = newActivity();
77-
bottomTabs = spy(new BottomTabs(activity) {
78-
@Override
79-
public void superCreateItems() {
80-
81-
}
82-
});
8380
childRegistry = new ChildControllersRegistry();
8481
eventEmitter = Mockito.mock(EventEmitter.class);
85-
86-
child1 = spy(new SimpleViewController(activity, childRegistry, "child1", tabOptions));
87-
child2 = spy(new SimpleViewController(activity, childRegistry, "child2", tabOptions));
88-
child3 = spy(new SimpleViewController(activity, childRegistry, "child3", tabOptions));
89-
child4 = spy(createStack());
90-
child5 = spy(new SimpleViewController(activity, childRegistry, "child5", tabOptions));
91-
when(child5.handleBack(any())).thenReturn(true);
92-
tabs = createTabs();
93-
presenter = spy(new BottomTabsPresenter(tabs, new Options()));
94-
bottomTabPresenter = spy(new BottomTabPresenter(activity, tabs, ImageLoaderMock.mock(), new Options()));
95-
tabsAttacher = spy(new BottomTabsAttacher(tabs, presenter));
96-
uut = createBottomTabs();
97-
98-
uut.setParentController(Mockito.mock(ParentController.class));
99-
CoordinatorLayout parent = new CoordinatorLayout(activity);
100-
parent.addView(uut.getView());
101-
activity.setContentView(parent);
102-
82+
prepareViewsForTests();
10383
StatusBarUtils.saveStatusBarHeight(63);
10484
}
10585

@@ -110,6 +90,22 @@ public void createView_checkProperStructure() {
11090
assertThat(((CoordinatorLayout.LayoutParams) uut.getBottomTabs().getLayoutParams()).gravity).isEqualTo(Gravity.BOTTOM);
11191
}
11292

93+
@Test
94+
public void createView_tabsWithoutIconsAreAccepted() {
95+
tabOptions.bottomTabOptions.icon = new NullText();
96+
prepareViewsForTests();
97+
assertThat(uut.getBottomTabs().getItemsCount()).isEqualTo(tabs.size());
98+
}
99+
100+
@Test
101+
public void createView_showTitlesWhenAllTabsDontHaveIcons() {
102+
tabOptions.bottomTabOptions.icon = new NullText();
103+
assertThat(tabOptions.bottomTabsOptions.titleDisplayMode.hasValue()).isFalse();
104+
prepareViewsForTests();
105+
presenter.applyOptions(Options.EMPTY);
106+
assertThat(bottomTabs.getTitleState()).isEqualTo(AHBottomNavigation.TitleState.ALWAYS_SHOW);
107+
}
108+
113109
@Test(expected = RuntimeException.class)
114110
public void setTabs_ThrowWhenMoreThan5() {
115111
tabs.add(new SimpleViewController(activity, childRegistry, "6", tabOptions));
@@ -393,6 +389,36 @@ public void destroy() {
393389
verify(tabsAttacher).destroy();
394390
}
395391

392+
private void prepareViewsForTests() {
393+
perform(uut, ViewController::destroy);
394+
bottomTabs = spy(new BottomTabs(activity) {
395+
@Override
396+
public void superCreateItems() {
397+
398+
}
399+
});
400+
createChildren();
401+
tabs = createTabs();
402+
presenter = spy(new BottomTabsPresenter(tabs, new Options()));
403+
bottomTabPresenter = spy(new BottomTabPresenter(activity, tabs, ImageLoaderMock.mock(), new Options()));
404+
tabsAttacher = spy(new BottomTabsAttacher(tabs, presenter));
405+
uut = createBottomTabs();
406+
407+
uut.setParentController(Mockito.mock(ParentController.class));
408+
CoordinatorLayout parent = new CoordinatorLayout(activity);
409+
parent.addView(uut.getView());
410+
activity.setContentView(parent);
411+
}
412+
413+
private void createChildren() {
414+
child1 = spy(new SimpleViewController(activity, childRegistry, "child1", tabOptions));
415+
child2 = spy(new SimpleViewController(activity, childRegistry, "child2", tabOptions));
416+
child3 = spy(new SimpleViewController(activity, childRegistry, "child3", tabOptions));
417+
child4 = spy(createStack());
418+
child5 = spy(new SimpleViewController(activity, childRegistry, "child5", tabOptions));
419+
when(child5.handleBack(any())).thenReturn(true);
420+
}
421+
396422
@NonNull
397423
private List<ViewController> createTabs() {
398424
return Arrays.asList(child1, child2, child3, child4, child5);

0 commit comments

Comments
 (0)