I have a situation, I´m not good enough in android dev to implement a way to pass data between Bottom Navigation destinations.
I would need to get text from EditText from CreateFragment to a RecipesFragment, where I would add a new item in the database and the RecyclerView would update (I´m using LiveData + MVVM) based on this data.
My Bottom Navigation Bar
So, I´m asking for a way. I couldn´t find any proper/specific explanation and I think that passing data between fragments in Bottom Navigation or Navigation Drawer is important feauture but hard i guess for beginners like me :(.
Additional info: I host a FrameLayout(id-fragment_container) in my MainActivity in what I change fragments based on location in Bottom Nav selected. (check code)
MainActivity.java
public class MainActivity extends AppCompatActivity {
private BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(navListener);
// Keeps the current fragment opened when the device rotates
if (savedInstanceState == null) {
// Sets the default selected item in NavigationView
bottomNavigationView.setSelectedItemId(R.id.nav_home);
}
}
// Handles switching the view based on NavigationView item selected
private BottomNavigationView.OnNavigationItemSelectedListener navListener =
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.nav_home:
selectedFragment = new HomeFragment();
break;
case R.id.nav_recipes:
selectedFragment = new RecipesFragment();
break;
case R.id.nav_create:
selectedFragment = new CreateFragment();
break;
}
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
selectedFragment).commit();
return true;
}
};
CreateFragment.java
private void saveRecipe() { //trigered on floating button press
String title = edit_title.getText().toString();
String ingredients = edit_ingredients.getText().toString();
if (title.trim().isEmpty() || ingredients.trim().isEmpty()) {
Toast.makeText(getActivity(), "Please insert a title and description", Toast.LENGTH_SHORT).show();
return;
}
// THIS SHOULD PASS DATA TO RecipesFragment INTO RECYCLERVIEW
// TODO
//
}
RecipesFragment.java
public void getRecipeFromCreateFragment() {
// THERE SHOULD BE PASSED DATA FROM CREATE ACTIVITY
// TODO
//
Recipe recipe =
new Recipe("hh", "title", "ingredients", "instructions", "ss", "image");
recipeViewModel.insert(recipe);
Toast.makeText(getActivity(), "Recipe saved", Toast.LENGTH_SHORT).show();
}
UPDATE: Okay, I got help from a fellow redditer and the solution was really easy and I shouldn´t had even thought about passing data between fragments in my case.
The solution is to simply add the recipe to the database via ViewModel in the CreateFragment, there´s really no need to pass the data to the RecipesFragment and then add it there.
HOWEVER, if someone know a better, more recommended way to pass data between fragments in Navigation View (Navigation Drawer) it would be really helpful for the future.
Thanks.
Related
I built a customizable navigation drawer from scratch(didn't make use of the default drawer provided by Android Studio). In my weather app's navigation bar menu https://i.stack.imgur.com/SIjdx.jpg, whenever I select an option on the menu(say settings), it displays the contents of the option along with the bottom navigation view and my Activity's Toolbar contents which comprises of the nav hamburger icon, the edittext and the search button(the activity hosting my 3 fragments) which spoils the app and makes it look very ugly i.e. https://i.stack.imgur.com/gxj5n.jpg (From that screenshot, the entire content should be empty if implemented well). The case is the same for the other bar menu options. All I want is an empty space to work on, I want the app to only display the navigation bar contents without the rest. Example; https://i.stack.imgur.com/3Jtga.png Please how should I do this?
The view of the Navigation Menu is controlled by this code(on line 185):
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.settings_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Settings()).commit();
break;
case R.id.ads_upgrade_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Upgrade()).commit();
break;
case R.id.privacy_policy_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Privacy_Policy()).commit();
break;
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
"fragment" there represents that I'm currently using my fragment's container view on my activity to display the Nav menu contents which I know is wrong for sure, so what should I use in replace? I lack strong experience as it's my first time building an app and I've tirelessly spent 3 hours on my own trying to figure out the issue which proved abortive.
Here is my Activity code:
public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private DrawerLayout drawer;
// Last update time, click sound, search button, search panel.
TextView timeField;
MediaPlayer player;
ImageView Search;
EditText textfield;
// For scheduling background image change(using constraint layout, start counting from dubai, down to statue of liberty.
ConstraintLayout constraintLayout;
public static int count = 0;
int[] drawable = new int[]{R.drawable.dubai, R.drawable.norway, R.drawable.eiffel_tower, R.drawable.hong_kong, R.drawable.statue_of_liberty,
R.drawable.beijing, R.drawable.chicago, R.drawable.colombia, R.drawable.vienna,R.drawable.tokyo};
Timer _t;
private WeatherDataViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// use home activity layout.
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Allow activity to make use of the toolbar
drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
viewModel = new ViewModelProvider(this).get(WeatherDataViewModel.class);
// Trigger action to open & close navigation drawer
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar
, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
timeField = findViewById(R.id.textView9);
Search = findViewById(R.id.imageView4);
textfield = findViewById(R.id.textfield);
// find the id's of specific variables.
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
// host 3 fragments along with bottom navigation.
final NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
assert navHostFragment != null;
final NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView, navController);
// Make hourly & daily tab unusable
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}
return false;
});
navController.addOnDestinationChangedListener((controller, destination, arguments) -> navController.popBackStack(destination.getId(), false));
// For scheduling background image change
constraintLayout = findViewById(R.id.layout);
constraintLayout.setBackgroundResource(R.drawable.dubai);
_t = new Timer();
_t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
// run on ui thread
runOnUiThread(() -> {
if (count < drawable.length) {
constraintLayout.setBackgroundResource(drawable[count]);
count = (count + 1) % drawable.length;
}
});
}
}, 5000, 5000);
Search.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// make click sound when search button is clicked.
player = MediaPlayer.create(HomeActivity.this, R.raw.click);
player.start();
getWeatherData(textfield.getText().toString().trim());
// make use of some fragment's data
Fragment currentFragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
if (currentFragment instanceof FirstFragment) {
FirstFragment firstFragment = (FirstFragment) currentFragment;
firstFragment.getWeatherData(textfield.getText().toString().trim());
} else if (currentFragment instanceof SecondFragment) {
SecondFragment secondFragment = (SecondFragment) currentFragment;
secondFragment.getWeatherData(textfield.getText().toString().trim());
} else if (currentFragment instanceof ThirdFragment) {
ThirdFragment thirdFragment = (ThirdFragment) currentFragment;
thirdFragment.getWeatherData(textfield.getText().toString().trim());
}
}
private void getWeatherData(String name) {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<Example> call = apiInterface.getWeatherData(name);
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(#NonNull Call<Example> call, #NonNull Response<Example> response) {
try {
assert response.body() != null;
timeField.setVisibility(View.VISIBLE);
timeField.setText("First Updated:" + " " + response.body().getDt());
} catch (Exception e) {
timeField.setVisibility(View.GONE);
timeField.setText("First Updated: Unknown");
Log.e("TAG", "No City found");
Toast.makeText(HomeActivity.this, "No City found", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NotNull Call<Example> call, #NotNull Throwable t) {
t.printStackTrace();
}
});
}
});
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.settings_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Settings()).commit();
break;
case R.id.ads_upgrade_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Upgrade()).commit();
break;
case R.id.privacy_policy_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Privacy_Policy()).commit();
break;
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
#Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
// Open/close drawer animation
}
}
}
In case you require any other code to look into the issue, please let me know. I'm just trying to avoid posting too much
EDIT:
My old bottomtabs nav graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/my_nav"
app:startDestination="#id/firstFragment">
<fragment
android:id="#+id/firstFragment"
android:name="com.viz.lightweatherforecast.FirstFragment"
android:label="fragment_first"
tools:layout="#layout/fragment_first" />
<fragment
android:id="#+id/secondFragment"
android:name="com.viz.lightpreciseweatherforecast.SecondFragment"
android:label="fragment_second"
tools:layout="#layout/fragment_second" />
<fragment
android:id="#+id/thirdFragment"
android:name="com.viz.lightpreciseweatherforecast.ThirdFragment"
android:label="fragment_third"
tools:layout="#layout/fragment_third" />
</navigation>
My new nav bar graph:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/bar_nav"
app:startDestination="#id/firstFragment">
<fragment
android:id="#+id/firstFragment"
android:name="com.viz.lightweatherforecast.FirstFragment"
android:label="fragment_first"
tools:layout="#layout/fragment_first" />
<fragment
android:id="#+id/settings_id"
android:name="com.viz.lightweatherforecast.Settings"
android:label="#string/settings"
tools:layout="#layout/settings" />
<fragment
android:id="#+id/ads_upgrade_id"
android:name="com.viz.lightweatherforecast.Upgrade"
android:label="#string/upgrade_to_remove_ads"
tools:layout="#layout/upgrade" />
<fragment
android:id="#+id/privacy_policy_id"
android:name="com.viz.lightweatherforecast.Privacy_Policy"
android:label="#string/privacy_policy"
tools:layout="#layout/privacy_policy"/>
</navigation>
You are using navigation architecture components, so the navController is the one that should control fragment transactions, you are doing that right with BottomNavigationView.
But within the navDrawer you are doing the transaction through the supportFragmentManager which should be done through the navController instead as both handle the navigation differently.
whenever I select an option on the menu(say settings), it displays the contents of the option along with the bottom navigation view
That is because the BottomNavView is a part of the activity, and you need to move it to a fragment; this requires to change the navigation design of your app; to do that change your app navigation like the below:
Main navigation:
<navigation
..... >
<fragment
android:name="......HomeFragment"/>
<fragment
android:name="......SettingFragment"/>
<fragment
android:name="......AdsUpgradeFragment"/>
<fragment
android:name="......PrivacyPolicyFragment"/>
</navigation>
The HomeFragment is the fragment that should hold the BottomNaviagtionView instead of the activity; and when you navigate to the SettingFragment, the navConroller will replace the entire fragment in the navHostFragment, and therefore the BottomNaviagtionView won't be shown.
my Activity's Toolbar contents which comprises of the nav hamburger
icon, the edittext and the search button(the activity hosting my 3
fragments) which spoils the app and makes it look very ugly
Unlike the BottomNaviagtionView, you can't do that with your toolBar that is used as the supportActionBar, because setting supportActionBar more than once in order to change its look; will duplicates it; so you have to accept a single toolbar; but instead you can hide/show the layout that holds the search button & the EditText whenever the destination changes:
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
LinearLayout searchBar = findViewById(R.id.searchbar); // change searchbar according to the layout id that holds the search button and the EditText
if (destination.getId() == R.id.nav_home) {
searchBar.setVisibility(View.VISIBLE);
} else {
searchBar.setVisibility(View.GONE);
}
});
and yes they currently exit the app when clicking back
To exit the app whenever, the bottom back button is pressed in any fragment use OnBackPressedDispatcher() within onCreateView() of those fragment (in your case SettingFragment, PrivacyPolicyFragment, & AdsUpgradeFragment):
And make sure that appBarConfiguration doesn't reference those fragments so, that the UP button can be shown instead of the burger.
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
// Exit the app when back is pressed
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
requireActivity().finishAndRemoveTask();
else requireActivity().finish();
}
});
Also, make sure that your setup the DrawerLayout & its navView in the navController with:
NavigationView navView = findViewById(....);
appBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home) // remove up button from all these fragments >> Keep the up/back button in R.id.settings_id, R.id.settings_id, ads_upgrade_id, privacy_policy_id
.setOpenableLayout(drawer)
.build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
And to make the Home fragment hidden by default in the navDrawer:
navView.getMenu().findItem(R.id.nav_home).setVisible(false); // adjust R.id.nav_home to yours
UPDATE
I currently have a single nav graph. The firstFragment represents the
today, 2nd - hourly and 3rd
I'm using is for my other weather tabs but the one you're suggesting
is for the navbar menu, should I replace yours with mine or just
create a new one for your suggestion?
You should use two navGraphs, the first is for the main navigation which I'd suggested; and the second is for the BottomNavigationView navigation which you already use; that is because we transferred the BottomNavigationView from the activity layout to the main/home fragment layout; and it' recommended the BottomNavigationView should have a separate navGraph;
So, you now need two FragmentContainerView; the first is in the activity layout which reference the navGraph provided in this answer, and the second is in the home fragment layout that references your original navGraph of the BottomNavigationView.
Sample:
I think I understood your problem very well (if I am not wrong). Thing is that what behavior you are getting right now is the normal scenario. You are using the same host activity to host both Navigation Drawer and Bottom Navigation fragments thus when you tried to navigate to another fragment from the same host the presence of the host's direct child view Bottom NavBar is showing. I think you can solve this problem in a few different but pretty simple logical ways.
(Not recommended) Using a different Activity for the Settings page. Just like startActivity(this, <some intent>). But in this way, you will end up creating lots of individual activities.
(Recommended) Using a common Navigation Host activity for navigating to independent pages/fragments. Like: Settings, Contact Us, etc. You can add different fragments into the common_nav_graph and set some actions with or without arguments for navigation. Just add the common_nav_graph as a nested graph inside your current one and set a simple argumented action. The arguments will help you to navigate desired pages/fragments without showing the bottom navigation bar.
Simply hide Bottom navigation when navigating to some fragments from your drawer.
I have trouble finding a good example on how to swipe between fragments with help of bottom navigatiom bar. Since FragmentStatePagerAdapter is deprecated and a new ViewPager2 is now recommended instead of the old ViewPager I want to use ViewPager2 and FragmentStateAdapter in my code instead. I have found an example of how to combine BottomNavigationBar and ViewPager here and I want to do something similar. My code have many similarities to the one in the example with the only difference that I have my code in a fragment instead of an activity. Here is a picture of how my FrontendFragment display look like. I can switch between the views using the bottomnavigationbar but I also want to be able to swipe between the views. Can someone help me or at least direct me on the right way? Here is my code:
FragmentPagerAdapter Class:
public class FragmentPagerAdapter extends FragmentStateAdapter {
private static final int mFragmentCount = 5;
public FragmentPagerAdapter(#NonNull Fragment fragment) {
super(fragment);
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position){
case 0:
return new HomeFragment();
case 1:
return new SearchFragment();
case 2:
return new AddFragment();
case 3:
return new MessageFragment();
case 4:
return new ProfileFragment();
}
return null;
}
#Override
public int getItemCount() {
return mFragmentCount;
}
}
FrontendFragment Class:
public class FrontendFragment extends Fragment implements BottomNavigationView.OnNavigationItemSelectedListener{
private BottomNavigationView mBottomNavigationView;
private ViewPager2 mViewPager2;
private FragmentPagerAdapter mFragmentPagerAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_frontend, container, false);
loadFragment(new HomeFragment());
mBottomNavigationView = v.findViewById(R.id.bottomNavigationBar);
mBottomNavigationView.setOnNavigationItemSelectedListener(this);
return v;
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.home_icon:
selectedFragment = new HomeFragment();
break;
case R.id.search_icon:
selectedFragment = new SearchFragment();
break;
case R.id.add_icon:
selectedFragment = new AddFragment();
break;
case R.id.message_icon:
selectedFragment = new MessageFragment();
break;
case R.id.profile_icon:
selectedFragment = new ProfileFragment();
break;
}
return loadFragment(selectedFragment);
}
private boolean loadFragment(Fragment selectedFragment) {
if(selectedFragment != null){
MainActivity.sFm.beginTransaction().replace(R.id.relLayoutMiddle, selectedFragment).commit();
return true;
}
return false;
}
}
Thanks in advance!
As I'm already using BottomNav with ViewPager2 in one of my app, I can help.
Your code is partially correct which means your FragmentPagerAdapter is fine, but not your FrontEndFragment.
See, the FragmentPagerAdapter has to be set to a ViewPager2 as
//this here is the FrontEndFragment
mViewPager2.setAdapter(new FragmentPagerAdapter(this));
Then, You don't have to do the FragmentTransaction at all, you just have to change the ViewPager2's current item position through the BottomNavigationBar as
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.home_icon:
mViewPager2.setCurrentItem(0);
break;
case R.id.search_icon:
mViewPager2.setCurrentItem(1);
break;
case R.id.add_icon:
mViewPager2.setCurrentItem(2);
break;
case R.id.message_icon:
mViewPager2.setCurrentItem(3);
break;
case R.id.profile_icon:
mViewPager2.setCurrentItem(4);
break;
}
return false;
}
This is all, you don't have to deal with Fragments at all apart from the FragmentPagerAdapter. Also, don't forget to remove the loadFragment(new HomeFragment()); which is not required, nor the function loadFragment() is required.
(Optional), Furthermore, if you want to disable the Swipe Action of the ViewPager2 and want the Fragments to be selected based on the Selected BottomNav item only, then you can set setUserInputEnabled() property of ViewPager2 as false.
Next, to set the BottomNavigationBar's item as selected based on the swipe of the ViewPager2, what you've to do is,
Create a global var
MenuItem previousMenuItem;
Then, set a default item (first) to be selected of BottomNav on activity start as
mBottomNavigationView.getMenu().getItem(0).setChecked(true);
Finally, set an OnPageSelected() callback on ViewPager2 to update the selected Menu Item as:
mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if (previousMenuItem != null) {
previousMenuItem.setChecked(false);
}
else {
mBottomNavigationView.getMenu().getItem(0).setChecked(false);
}
mBottomNavigationView.getMenu().getItem(position).setChecked(true);
previousMenuItem = mBottomNavigationView.getMenu().getItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
What you're doing here is that you're setting default item to previousMenuItem and then on swiping to a different page, deselecting the previousMenuItem and selecting the new one, which means updating the BottomNav based on ViewPager's current item. This is the complete code you require to acheive your objective.
If you want to swipe between views, a simple solution would be to store all views in your parent view, but set all layouts for views except the initial view to android:visibility="gone". Make sure to set the initial view layout to android:visibility="visible" though. Now on you button clicks, you will have to implement onClick such that they turn on/ off view visibilities accordingly. For example, store views in order and control them via array index. But the whole thing you're trying to do is generally not a good design pattern in my opinion.
Why don't you load another Activity onClick instead of crowding your single activity? This will cause load time issues. Even if the views are non-visible, it's just an overall hassle to maintain all that in one place.
I have a app with BottomNavigation (3 Items). The Item 1 load Fragment 1, Item 2 load fragment 2 and Item 3 load fragment 3. When one item is selected in the BottomNavigation, the view of this item keep blue and the text is more big. I have implemented onBackPressed, for it back the fragment history (BackStack). But, when i'm backing, the views in BottomNavigation are stoppe. So, if i'm in fragment 3, and i press back button and it back to fragment 2, the BottomNavigationView show that i'm in fragment 3, and if i press back button again, i go to fragment1, but the BottomNavigationView don't update, it keep in Fragment 3. The screenshorts will show what i'm talking about.
Sorry my bad english and my bad explanation.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
selectedFragment = Fragment1.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.content, selectedFragment).addToBackStack(null).commit();
return true;
case R.id.navigation_dashboard:
selectedFragment = Frament2.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.content, selectedFragment).addToBackStack(null).commit();
return true;
case R.id.navigation_notifications:
selectedFragment = Fragment3.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.content, selectedFragment).addToBackStack(null).commit();
return true;
}
return false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
From the Material Design guidelines for bottom navigation:
On Android, the Back button does not navigate between bottom navigation bar views.
And from the Developer Training for Back Navigation:
Note: You should not add transactions to the back stack when the transaction is for horizontal navigation (such as when switching tabs)
So you should not use the back stack when replacing fragments based on user interaction with your BottomNavigationView.
If you choose to ignore these guidelines, probably the answer is to look into FragmentManager.OnBackStackChangedListener and activate the appropriate navigation item when you pop the back stack.
In my app, am using a navigation drawer (in Fragment A) to navigate to fragments:
public void displayView(int viewId){
Fragment fragment = null;
String title = getString(R.string.app_name);
switch (viewId) {
case R.id.nav_menu:
fragment = new MenuFragment();
title = getString(R.string.menu_title);
viewIsAtHome = false;
break;
case R.id.nav_reservation:
fragment = new ReservationFragment();
title = getString(R.string.reservation_title);
viewIsAtHome = false;
break;
...
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
}
// set the toolbar title
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(title);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
}
}
displayView() is called in onNavigationItemSelected:
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
displayView(item.getItemId());
return true;
}
Now, in ReservationFragment I am displaying a list of reservations and a FloatingActionButton to start Activity B where the user can add a reservation if there are no reservations. When the user is done adding a reservation, I want to display it in the Reservation fragment. This requires me to "go back" to the Fragment.How do I accomplish this since Activity B knows nothing about Activity A?
What I've tried:
I tried creating a method in Activity A like this:
public void navigateToFragment(int viewId) {
displayView(R.id.nav_reservation);
}
and then called this method from Activity B:
saveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new MainActivity().navigateToFragment(R.id.nav_reservation);
//MainActivity is Activity A
}
});
the app crashes due to a nullPointerException in displayView() from the line:
String title = getString(R.string.app_name);
This isn't surprising since am creating a new MainActivity object that knows nothing about the previous state of the Activity, right?
This question mirrors my problem but is based on Settings so I can't really use the answer in my case.
How do I accomplish this task?
There are three quick methods that come to my mind. First you can start activityB with startActivityForResult and handle the result in activityA after user does what he wants in activityB. Second you can set activityA as singleTop and before finishing activityB you can startActivityA with clearTop an intent flag called clear_top(https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP).
Last but not the least, you can connect two activity by binding service in both activities and communicate via that service that you bound.
I am developing an Android Application with navigation activity and there are some list of options in navigation drawer and my First page is also in this activity. I am navigating to another page through the options present in the navigation drawer which are Fragment classes. I need to come back to the first page if I press back button on device. But it goes to pages which I visited before. My Target is to come back to First page from the respective Fragment class.
At times when I hit back button, it also comes to my splash screen, Kindly help me to fix this. Thanks in advance.
This is the code of my FirstPage from where I am navigating to another pages through drawer. I need to come back to this same page again from the drawer (FirsPage)
public void selectDrawerItem(MenuItem menuItem) {
switch(menuItem.getItemId()) {
//1.User Profile
case R.id.navigation_item_attachment:
fragmentClass = ProfileTab.class;
// finish();
break;
//2.FeedBack
//case R.id.navigation_item_images:
/* fragmentClass = SearchHistoryTab.class;
break;*/
case R.id.navigation_refer:
fragmentClass = ReferFrndz.class;
// finish();
break;
case R.id.navigation_daily:
fragmentClass = DailyUsage.class;
break;
//4.About Us
case R.id.navigation_about:
fragmentClass = AboutJc.class;
break;
//4.Contact Us
case R.id.navigation_item_location:
fragmentClass = ContactJc.class;
break;
default:
break;
}
try {
fragment = (Fragment) fragmentClass.newInstance();
}
catch (Exception e) {
e.printStackTrace();
}
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.search_layout, fragment).commit();
menuItem.setChecked(true);
setTitle(menuItem.getTitle());
mDrawerLayout.closeDrawers();
}
I used Finish() but it didn't work. It terminating my entire application.
Splash Screen Code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread mythread = new Thread() {
public void run(){
try{
sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
finally {
.....
}
}
}
};
mythread.start();
}
Update your Fragmenttransaction code to add ProfileTab (FirstPage) in backstack entry.' So you get ProfileTab (FirstPage) back after click back button.
FragmentManager fragmentManager = getSupportFragmentManager();
if(fragment.equals(ProfileTab.class)){
fragmentManager.beginTransaction().replace(R.id.search_layout, fragment).addToBackStack("fragment_tag").commit();
//Only add backstack entry for first page so you get after click on back button
}
else
{
fragmentManager.beginTransaction().replace(R.id.search_layout, fragment).commit();
}
menuItem.setChecked(true);
setTitle(menuItem.getTitle());
mDrawerLayout.closeDrawers();
Use this solution and share your feedback with replay will help us to improve solution.