I would like to get some input on the best way to structure my app's architecture when using Android's Bottom Navigation View.
Currently I define my BottomNavigationView in my MainActivity. It looks something like this.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()){
case R.id.action_home:
selectedFragment = HomeFragment.newInstance();
break;
case R.id.action_search:
selectedFragment = SearchFragment.newInstance();
break;
case R.id.action_message:
selectedFragment = MessageFragment.newInstance();
break;
case R.id.action_profile:
selectedFragment = ProfileFragment.newInstance();
break;
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout, selectedFragment);
transaction.commit();
return true;
}
});
//Manually displaying the first fragment - one time only
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout, HomeFragment.newInstance());
transaction.commit();
}
The problem is that once I click on one tab, it opens up a fragment, and I would like to have those Fragments open up other Fragments/Activities (i.e:
I open the profile tab (`ProfileFragment` loads)
I click on a button from `ProfileFragment`, and from this the `SignUpFragment` loads
After running into many bugs, I've researched on how to architect my app, but i've found mixed results. Would anyone know the correct way of using a BottomNavigationView with Fragments, and in those fragments I can load more Activities/fragments. A huge thanks in advance.
Related
I am trying to build app with BottomNavigationView and I set setOnItemSelectedListener() method to bottom navigation so I can do what I want when user select one of the menu in bottom navigation.
everything is good when I don't set setOnItemSelectedListener(), but when I set setOnItemSelectedListener() method then the fragment is not updated automatically when user select the bottom navigation menu.
I consider if that do i have to handle fragment transaction manually when I set this method?
thanks ^^
Yes. You need to manually replace the fragment item on onNavigationItemSelected
Example:
private BottomNavigationView.OnNavigationItemSelectedListener navListener = new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
// By using switch we can easily get
// the selected fragment
// by using there id.
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.algorithm:
selectedFragment = new AlgorithmFragment();
break;
case R.id.course:
selectedFragment = new CourseFragment();
break;
case R.id.profile:
selectedFragment = new ProfileFragment();
break;
}
// It will help to replace the
// one fragment to other.
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, selectedFragment)
.commit();
return true;
}
};
You can find a good tutorial here: https://www.geeksforgeeks.org/bottomnavigationview-inandroid/
I have a setup of 3 Fragments inside my main activity and want to navigate them using the BottomNavigationView. I want to add them once and then just switch between them without actually destroying the Fragments. Everything works fine except that the first fragment added to the SupportFragmentManager always disappears (tried changing the order so the problem is not with the Fragments themselves). Actually, it doesn't disappear but the last Fragment that occupied the container shows up.
Eg. I go to position 3, Fragment 3 shows up in the container and then click on position 1, Fragment 3 will still occupy the container. But if I tap position 2 Fragment 2 will appear. How I'm adding the Fragments:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.container, fragment1); //whatever gets added here ends up being invisible
ft.commitAllowingStateLoss();
ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.container, fragment2);
ft.commitAllowingStateLoss();
ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.container, fragment3);
ft.commitAllowingStateLoss();
How I'm switching between them using the OnNavigationItemSelectedListener:
private BottomNavigationView.OnNavigationItemSelectedListener navigation_listener =
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
Fragment previousFragment = getSupportFragmentManager().findFragmentById(R.id.container);
switch(item.getItemId()){
case R.id.bottom_navigation_bar_position1:
selectedFragment = fragment1;
break;
case R.id.bottom_navigation_bar_position2:
selectedFragment = fragment2;
break;
case R.id.bottom_navigation_bar_position3:
selectedFragment = fragment3;
break;
}
getSupportFragmentManager().beginTransaction().hide(previousFragment).commit();
getSupportFragmentManager().beginTransaction().show(selectedFragment).commit();
return true;
}
};
The XML menu navigation:
<item
android:id="#+id/bottom_navigation_bar_position1"
android:title="Title1"
android:icon="#drawable/ic1"
/>
<item
android:id="#+id/bottom_navigation_bar_position2"
android:title="Title2"
android:icon="#drawable/ic2"
/>
<item
android:id="#+id/bottom_navigation_bar_position3"
android:title="Title3"
android:icon="#drawable/ic3"
/>
Btw. just using replace() instead of show and hide works but that's not the goal...
I've also tested whether the first added Fragment exits (and doesn't get destroyed) and it indeed exists...
Thanks!
Instead of trying to show and hide fragment I solved the problem by managing the Fragments using a ViewPager and a SectionsPageAdapter. Once you link these two, use ViewPager.setOffscreenPageLimit(3) to make sure no Fragments get destroyed. My BottomNavigationView.OnNavigationItemSelectedListener looks like this now:
private BottomNavigationView.OnNavigationItemSelectedListener navigation_listener =
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch(item.getItemId()){
case R.id.bottom_navigation_bar_position1:
tab_view_pager.setCurrentItem(0);
return true;
case R.id.bottom_navigation_bar_position2:
tab_view_pager.setCurrentItem(1);
return true;
case R.id.bottom_navigation_bar_position3:
tab_view_pager.setCurrentItem(2);
return true;
}
return false;
}
};
You have to set 0th position item selected for BottomNavigationView.OnNavigationItemSelectedListener. Find Menu bottom_navigation_bar_position1
I used this in my code, please refactor according to your needs
BottomNavigationView navigation = findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(this);
Menu menu = navigation.getMenu();
this.onNavigationItemSelected(menu.findItem(R.id.navigation_doc));
I have a BottomNavigationView where I can navigate between fragments. The third fragment is a PreferenceFragment while the other two extend Fragment. I have a simple switch case for replacing fragments but I get an error that SettingsFragment cannot be converted to Fragment.
How can I navigate between these fragments?
BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
toolbar.setTitle("Home");
selectedFragment = HomeFragment.newInstance();
break;
case R.id.navigation_dashboard:
toolbar.setTitle("Dashboard");
selectedFragment = DashboardFragment.newInstance();
break;
case R.id.navigation_settings:
toolbar.setTitle("Settings");
selectedFragment = SettingsFragment.newInstance();
break;
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentContainer, selectedFragment);
transaction.commit();
return true;
}
};
Unless you're using the support preference library, PreferenceFragment extends android.app.Fragment, so you need to also extend android.app.Fragment for your other Fragments.
However, since Fragment and PreferenceFragment have been deprecated in API 28, you might want to just convert to the support Fragment and PreferenceFragmentCompat. Be aware that this will mean any Preference libraries you might be using will probably not work until they've been updated.
I'm currently developing a Android studio App using fragment and a bottom navigation bar.
When I click on a navigation bar's item, it's replacing the current fragment by another one which correspond the fragment I wanted for this item.
The problem is, the objects in my fragment are all reset after replacing fragment.
I'm not removing the fragment from the container so I don't really understand why all the objects are reset after doing this.
Here is my code to add and replace fragment to my FrameLayout :
private void setFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.fade_out);
if (getSupportFragmentManager().findFragmentById(R.id.main_frame) == null) {
fragmentTransaction.add(R.id.main_frame, fragment);
}
else
{
fragmentTransaction.replace(R.id.main_frame, fragment);
}
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
and here is the bottom navigation bar code to execute the previous function and change the displayed fragment:
homeFragment = new HomeFragment();
programFragment = new ProgramFragment();
bluetoothFragment = new BluetoothFragment();
mMainNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
test = mMainNav.getMenu().getItem(2);
switch (item.getItemId()) {
case R.id.nav_home:
//mMainNav.setItemBackgroundResource(R.color.colorPrimary);
HQ_logo_IV.setVisibility(View.VISIBLE);
setFragment(homeFragment);
return true;
case R.id.nav_program:
//mMainNav.setItemBackgroundResource(R.color.colorAccent);
HQ_logo_IV.setVisibility(View.INVISIBLE);
setFragment(programFragment);
return true;
case R.id.nav_bluetooth:
//mMainNav.setItemBackgroundResource(R.color.colorPrimaryDark);
HQ_logo_IV.setVisibility(View.INVISIBLE);
setFragment(bluetoothFragment);
return true;
default:
return false;
}
}
});
I found the way to stop this thing.
I used an AsyncTask to refresh my fragment state.
I just put new "yourAsyncTaskName"().execute() at the beginning of my onCreate() method to refresh the fragment when it's created or replaced.
Hope this will help.
I created an activity with a bottom navigation bar.
I googled a lot things about it but now I don't know how to handle this exactly.
Before, I just started another activity when the user clicks the bottom navigation but I think it's not good.
How can I switch between the tabs?
Do I have to work with fragments? And what about 'setContentView(int layoutResID)'? How can I do that? I'm confused...
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
return true;
case R.id.navigation_dashboard:
startActivity(dashboardActivity);
return true;
case R.id.navigation_notifications:
startActivity(dashboardActivity);
return true;
}
return false;
}
};
Thank you very much for your help - I hope, you understood what I mean.
Activity transition is always expensive and we should switch from one activity to another only when we are switching the context. A fragment is a portion of UI in an activity. Same fragment can be used with multiple activities. Just like activity a fragment has its own lifecycle and setContentView(int layoutResID) can be set to different layout in OnCreate of fragment.
This link explains more on when to use activity or fragment.
Android developer guide on Fragments
Code path tutorial on bottom navigation views.
Please refer to :-
https://github.com/waleedsarwar86/BottomNavigationDemo
and complete explanation in
http://waleedsarwar.com/posts/2016-05-21-three-tabs-bottom-navigation/
You will get a running code with the explanation here.
Bottom Navigation View is a navigation bar introduced in android library to make it easy to switch between views with a single tap. It can although be used for almost any purpose, but is most commonly used to switch between fragments with a single tap. Its use for opening activities is somewhat absurd, since it ignores its most important functionality of switching the views with a single tap. There are many good articles and blogs out there in this regard, one of which is:
https://medium.com/#hitherejoe/exploring-the-android-design-support-library-bottom-navigation-drawer-548de699e8e0
Hope this solves your doubt..
bottomNavigationView.setOnNavigationItemSelectedListener
(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.action_item1:
selectedFragment = ItemOneFragment.newInstance();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout, selectedFragment);
transaction.commit();
// selectedFragment.getChildFragmentManager().beginTransaction();
break;
case R.id.action_item2:
selectedFragment = ItemTwoFragment.newInstance();
FragmentTransaction transactiona = getSupportFragmentManager().beginTransaction();
transactiona.replace(R.id.frame_layout, selectedFragment);
transactiona.commit();
// selectedFragment = ItemThreeFragment.newInstance();
break;
case R.id.action_item3:
// selectedFragment = ItemOneFragment.newInstance();
Intent intent=new Intent(MainView.this, YoutActivityLive.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
// selectedFragment = ItemTwoFragment.newInstance();
break;
case R.id.action_item5:
selectedFragment = ItemOneFragment.newInstance();
FragmentTransaction transactionb = getSupportFragmentManager().beginTransaction();
transactionb.replace(R.id.frame_layout, selectedFragment);
transactionb.commit();
// selectedFragment = ItemFiveFragment.newInstance();
break;
}
return true;
}
});