Im creating a app with three sections home,color,profile I'm using bottom navigation view with fragments to achieve this.
App UI
Here is my code:
Main Activity on create method:
final HomeFragment homeFragment = new HomeFragment();
final ColorFragment colorFragment = new ColorFragment();
final ProfileFragment profileFragment = new ProfileFragment();
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment active = homeFragment;
switch (item.getItemId()){
case R.id.action_home:
active = homeFragment;
break;
//return true;
case R.id.action_color:
active = colorFragment;
break;
case R.id.action_profile:
active = profileFragment;
break;
default:
active = homeFragment;
break;
}
//Loading the fragment on click
fragmentManager.beginTransaction().replace(R.id.fragment_container,active).commit();
return true;
}
});
Im using fragment manager to load the fragment in my Main Activity which comes with below problem.
1.It reloads the fragment each time I click on the bottom navigation icon.(Im getting data from firebase this will be costly )
I have read many articles regarding this,i found solutions like below:
Using hiding the fragments on creation of activity
fragmentManager.beginTransaction().add(R.id.fragment_container, profileFragment, "3").hide(profileFragment).commit();
fragmentManager.beginTransaction().add(R.id.fragment_container, colorFragment, "2").hide(colorFragment).commit();
fragmentManager.beginTransaction().add(R.id.fragment_container,homeFragment, "1").commit();
Above solution loads all the fragments on the creation of activity which isn't a efficient scnario
Using NavHost :
It refresh the fragment every time.
This also not working for me.
Please Help:
What I want to acheive:
1.The fragment should only load when I click on the icon for the first time.
2.All the fragments should not load at the time of activity creation.
3.When I revist the fragment It should not load again, I should stay at previous state ex. scrolled till 25th card
//Its should be like playstore,youtube where when we click on icon it loads the data, when we revisit the state will be there.
You either need to use extension function provided by Google for BottomNavigationView or use a ViewPager/ViewPager2 to have each fragment have it's own back stack and going back to exact same fragment instead of creating root fragment over and over again. Both solutions require you to have NavHostFragment as root of each tab. You can check out the samples in this repo.
You can either use a FragmentPagerAdapter or ViewPager2. I refer you to two answers for a similar question:
1- FragmentPagerAdapter
2- ViewPager2
Related
Problem in short:
I have an MainActivity that holds BottomNavigationView and FrameLayout on top of it. BottomNavigationView has 5 tabs and when tab is clicked, I add some fragment on that FrameLayout. But, from some fragment, I need to open another fragment. From that another fragment, I need to open the other one. Every time when I need to show fragment, I notify MainActivity from fragment, that it needs to add the another one. Every fragment checks does its activity implement interface. And it is annoying. So, if I have 100 fragments, MainActivity implements too many interfaces. It leads to boilerplate code. So, how to properly navigate between fragments if you have a lot?
Problem in detail:
Please, read problem in short section first.
As I've said I have BottomNavigationView that has 5 tabs. Let's call the fragments that responsible for each tab as FragmentA, FragmentB, FragmentC, FragmentD, FragmentE. I really know, how to show these fragments when tab is clicked. I just replace/add these fragments in activity. But, wait, what if you wanna go from FragmentA to FragmentF? After that from FragmentF to FragmentG? This is how I handle this problem: from FragmentF or FragmentG I notify MainActivity that I wanna change the fragment. But how they communicate with MainActivity? For this, I have interfaces inside of each fragment. MainActivity implements those interfaces. And here is problem. MainActivity implements too many interfaces that leads to boilerplate code. So, what is the best way to navigate through Fragments? I don't even touch that I also need to handle back button presses :)
Here is how my code looks like:
MainActivity implementing interfaces to change fragments if necessary:
class MainActivity : AppCompatActivity(), DashboardFragment.OnFragmentInteractionListener,
PaymentFragment.BigCategoryChosenListener, PaymentSubcategoryFragment.ItemClickedListener, PayServiceFragment.OnPayServiceListener, ContactListFragment.ContactTapListener, P2PFragment.P2PNotifier
Here is my PaymentFragment's onAttach method for example:
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof BigCategoryChosenListener) {
listener = (BigCategoryChosenListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement BigCategoryChosenListener");
}
}
And using this listener I notify activity to change fragment. And in EACH fragment I should do so. I don't think that it is best practice. So, is it ok or there is a better way?
Ok What you need is something like this in activity where you would initialized on your BottomNavigationView.
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_1://Handle menu click -
//Call Navigator helper to replace Fragment to Fragment A
break;
case R.id.menu_2:
//Call Navigator helper to replace Fragment to Fragment B
break;
case R.id.menu_3:
//Call Navigator helper to replace Fragment to Fragment C
break;
}
return true;
}
});
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` or `SignUpActivity` 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.
Every approach depends on the project and what you pretend to achieve. I had to code a Bottom Navigation app that works with over 20 Bottom Navigation layouts, meaning one single Activity. The process you wish to achieve is pretty much the same of setting the desired fragment in the desired tab on tab selected, the difference is that, instead of taping on a tab, you will tap on a button inside a fragment, which you will replace with the new desired fragment.
tap tab -> replace fragment -> button click inside fragment -> replace fragment -> and so on.
Since you are using replace, you will have to carefully handle your onBackPress event, since I'm assuming that on every back press you wish to go back to the previous fragment. Myself, I've implemented an interface in the Main Activity that listens to the visible fragment onBackPress.
I am trying to wrap my head around the proper use of fragments with the ViewPager. I currently have 5 fragments (with one main activity) each of which pulls some data from a web service and displays it to the user. The user can swipe between the views to see the different info. The problem I am running into is the fragments get destroyed and recreated once the user swipes two pages away and then back to the fragment resulting in multiple calls to the web services when they are not needed. Configuration changes also force the fragments to be recreated (thus recalling the webservice). I am currently recreating a new instance of the fragment every time. Whats the best way to cache the data? Or am I using FragmentPagerAdapter in the wrong way? I've attached some relevant code.
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
WebFragmentOne a = WebFragmentOne.newInstance();
return a;
case 1:
WebFragmentTwo b = WebFragmentTwo.newInstance();
return b;
case 2:
WebFragmentThree c = WebFragmentThree.newInstance();
return c;
case 3:
WebFragmentFour d = WebFragmentFour.newInstance();
return d;
case 4:
WebFragmentFive f = WebFragmentFive.newInstance();
return f;
}
return null;
}
The newInstance() method in each of the fragments.
public static final WebFragment newInstance()
{
WebFragment f = new WebFragment();
return f;
}
Keep the data you want to keep in a private field within the fragment.
In the OnCreate call setRetainInstance(true) to stop Android destroying your fragment.
Now you can check when the fragment starts if you already have data. If you don't then you can retrieve it from your web service. If you do then skip the retrieval and jump straight to the setting up of your views.
I have a menu with 4 menu options... Each of these options shows the corresponding fragment.
I have 3 functions to add,show and hide a fragment:
private void addFragment(Fragment newFragment, String fragmentName) {
fragmentManager
.beginTransaction()
.add(R.id.content_frame, newFragment,
fragmentName).addToBackStack(null)
.commit();
}
private void hideFragment(Fragment existingFragment) {
if(existingFragment!=null && existingFragment.isVisible()){
fragmentManager.beginTransaction().hide(existingFragment).addToBackStack(null).commit();
}
}
private void showFragment(Fragment existingFragment) {
if(existingFragment!=null){
fragmentManager.beginTransaction().show(existingFragment)
.addToBackStack(null).commit();
}
}
The navitemswitchlogic is as follows:
onNavItemSelected(int id) {
switch (id) {
case 1: //option 1 - hide rest of the fragments and show Option1Fragment
hideFragment(option2Fragment);
hideFragment(option3Fragment);
hideFragment(option4Fragment);
if (Option1FragmentDoesnotExist) { //create option1Fragment and add it to FragmentTransaction
option1Fragment = new option1Fragment();
addFragment(option1Fragment,"option1Fragment");
} else
showFragment(option1Fragment);
break;
case 2:
//same as option 1
}
}
What I expect to happen:
Whats happening now:
I know I am making some mistake adding and retrieving fragments to/from the backstack..But,I am not sure what exactly it is. I tried doing all these as a part of the same transaction as well.. That dint work too.. Also, I dont want to replace the fragments... Hope I framed my question right..
Any help is appreciated.Thanks!
It's a bit late to answer this question now, but for those who now come across the similar issue, here is the explanation.
Each back press will reverse a single fragment transaction. So basically what you have in the example above is that you have 2 transactions on navigation click.
1st one is Hiding fragment and second is showing a new one. Once you press back, it will reverse showing new one but nobody will reveal the hidden one and you are left with the Blank/empty screen.
On the next back press you will reverse hide action, so fragment will now appear.
What you need to do is to execute both hiding and showing in a single transaction. In that way, a single back press will know that you want to reverse adding and hiding a fragment below.
fragmentManager.beginTransaction().hide(existingFragment).add(newFragment).addToBackStack(null).commit();
I want to implement a navigation drawer with multiple listfragments, how can i do this? I spent time searching online but couldnt find anything related to it. Any help would be appreciated!
When implementing the NavigationDrawer, use its onDrawerItemSelected method to switch Fragments:
#Override
public void onDrawerItemSelected(final int pos) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (pos) {
case 0:
fragment = new ListFragmentOne();
break;
case 1:
fragment = new ListFragmentTwo();
break;
case 2:
fragment = new ListFragmentThree();
break;
}
// content_frame is a FrameLayout inside the layout of your activity - this is where the fragment will be put
getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).commit();
mDrawerList.setItemChecked(pos, true);
// do stuff like closing the drawer...
}
I am not sure though if the NavigationDrawer even supports ListFragments. If not, simply use normal Fragments containing a ListView.