I'm looking for a way to se a default fragment in my navigation drawer which is not the item # 0. I've seen some solutions based on checking if savedInstanceState is null or not but I feel like I don't have access to this in my onNavigationDrawerItemSelected method. Here is my class:
public class Main extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the Main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new HomeFragment();
break;
case 1:
fragment = new HomeFragment();
break;
case 2:
fragment = new HomeFragment();
break;
default:
fragment = new DiscoverFragment();
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, fragment).commit();
}
}
The answer is rather simple. Since the DrawerLayout leaves you to manage fragments on your own, you can just use the basic FragmentManager APIs that would be used in a normal FragmentActivity.
I use the following method to make it a little simpler, which will replace the current fragments even if there are none currently added to the container. Just call this method with the fragment you want to be default in your onCreate() method.
public void setFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit();
}
Note that it is technically more correct to use .add(fragment) for the first call rather than .replace since you aren't replacing anything, but it really doesn't matter because internally, it basically just removes any existing fragments and adds the new one using .add() for you
http://developer.android.com/reference/android/app/FragmentTransaction.html#replace(int,android.app.Fragment,java.lang.String)
Related
I'm implementing BottomNavigationView for navigation in an Android app. I am using Fragments to set the content for each tab.
I know how to set up one fragment for each tab and then switch fragments when a tab is clicked. But how can I have a separate back stack for each tab?
Here is the code to set up one fragment:
Fragment selectedFragment = ItemsFragment.newInstance();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.content, selectedFragment);
transaction.commit();
For an example, Fragment A and B would be under Tab 1 and Fragment C and D under Tab 2. When the app is started Fragment A is shown and Tab 1 is selected. Then Fragment A might be replaced with Fragment B. When Tab 2 is selected Fragment C should be displayed. If Tab 1 is then selected Fragment B should once again be displayed. At this point, it should be possible to use the back button to show Fragment A.
And Here is the code to set up next fragment in the same tab:
Fragment selectedFragment = ItemsFragment.newInstance();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.content, selectedFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
Finally, I found the solution, it was inspired by a previous answer on StackOverflow: Separate Back Stack for each tab in Android using Fragments
I only have replaced TabHost with BottomNavigationView and here is the code:
Main Activity
public class MainActivity extends AppCompatActivity {
private HashMap<String, Stack<Fragment>> mStacks;
public static final String TAB_HOME = "tab_home";
public static final String TAB_DASHBOARD = "tab_dashboard";
public static final String TAB_NOTIFICATIONS = "tab_notifications";
private String mCurrentTab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
mStacks = new HashMap<String, Stack<Fragment>>();
mStacks.put(TAB_HOME, new Stack<Fragment>());
mStacks.put(TAB_DASHBOARD, new Stack<Fragment>());
mStacks.put(TAB_NOTIFICATIONS, new Stack<Fragment>());
navigation.setSelectedItemId(R.id.navigation_home);
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
selectedTab(TAB_HOME);
return true;
case R.id.navigation_dashboard:
selectedTab(TAB_DASHBOARD);
return true;
case R.id.navigation_notifications:
selectedTab(TAB_NOTIFICATIONS);
return true;
}
return false;
}
};
private void gotoFragment(Fragment selectedFragment)
{
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content, selectedFragment);
fragmentTransaction.commit();
}
private void selectedTab(String tabId)
{
mCurrentTab = tabId;
if(mStacks.get(tabId).size() == 0){
/*
* First time this tab is selected. So add first fragment of that tab.
* Dont need animation, so that argument is false.
* We are adding a new fragment which is not present in stack. So add to stack is true.
*/
if(tabId.equals(TAB_HOME)){
pushFragments(tabId, new HomeFragment(),true);
}else if(tabId.equals(TAB_DASHBOARD)){
pushFragments(tabId, new DashboardFragment(),true);
}else if(tabId.equals(TAB_NOTIFICATIONS)){
pushFragments(tabId, new NotificationsFragment(),true);
}
}else {
/*
* We are switching tabs, and target tab is already has atleast one fragment.
* No need of animation, no need of stack pushing. Just show the target fragment
*/
pushFragments(tabId, mStacks.get(tabId).lastElement(),false);
}
}
public void pushFragments(String tag, Fragment fragment, boolean shouldAdd){
if(shouldAdd)
mStacks.get(tag).push(fragment);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
public void popFragments(){
/*
* Select the second last fragment in current tab's stack..
* which will be shown after the fragment transaction given below
*/
Fragment fragment = mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);
/*pop current fragment from stack.. */
mStacks.get(mCurrentTab).pop();
/* We have the target fragment in hand.. Just show it.. Show a standard navigation animation*/
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
#Override
public void onBackPressed() {
if(mStacks.get(mCurrentTab).size() == 1){
// We are already showing first fragment of current tab, so when back pressed, we will finish this activity..
finish();
return;
}
/* Goto previous fragment in navigation stack of this tab */
popFragments();
}
}
Home fragment example
public class HomeFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
Button gotoNextFragment = (Button) view.findViewById(R.id.gotoHome2);
gotoNextFragment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((MainActivity)getActivity()).pushFragments(MainActivity.TAB_HOME, new Home2Fragment(),true);
}
});
return view;
}
}
This behavior is supported by the new Navigation Architecture Component (https://developer.android.com/topic/libraries/architecture/navigation/).
Essentially, one can use NavHostFragment, which is a fragment that controls its own back stack:
Each NavHostFragment has a NavController that defines valid navigation within the navigation host. This includes the navigation graph as well as navigation state such as current location and back stack that will be saved and restored along with the NavHostFragment itself.
https://developer.android.com/reference/androidx/navigation/fragment/NavHostFragment
Here is an example: https://github.com/deisold/navigation
Edit: Turns out Navigation Architecture Component doesn't support seperate back stacks anyway, as pointed out by the commenters. But as #r4jiv007 mentioned, they are working on it and has offered an "official hack" in the meantime: https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample
It is worth noting that the behavior you describe goes against the Google guidelines. https://material.io/guidelines/components/bottom-navigation.html#bottom-navigation-behavior
Navigation through the bottom navigation bar should reset the task state.
In other words, having Fragment A and Fragment B "inside" Tab 1 is fine, but if the user opens Fragment B, clicks Tab 2, and then clicks Tab 1 again, they should see Fragment A.
Suppose you have 5(A, B, C, D, E) BottomNavigationView menu item, then in Activity create 5 FrameLayout(frmlytA, frmlytB, frmlytC, frmlytD, frmlytE) in parallel overlapping manner as the container for each of these menu items. When BottomNavigation Menu item A is pressed then hide all the other FrameLayouts(Visibility = GONE) and just show(Visibility = VISIBLE) the FrameLayout 'frmlytA' which will host the FragmentA and over this container do the further transactions like (FragmentA -> FragmentX -> FragmentY). And then If user clicks BottomNavigation Menu item B then just hide this(frmlytA) container and show 'frmlytB'. Then if user again presses the menu item A then show 'frmlytA' it should retain the earlier state. So, like this you can switch between the container FrameLayouts and can maintain the back stack of each container.
Instead of using replace method use add fragment,
Instead of this method
ft.replace(R.id.content, selectedFragment);
Use this
ft.add(R.id.content, selectedFragment);
Fragment selectedFragment = ItemsFragment.newInstance();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.(R.id.content, selectedFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
From a while i had been using viewpager to display webview pages in activity, but now i have change from viewpager to navigation drawer with RecyclerView, to display webview, i have multiple webview loading in my activity.
Suppose activity starts on webview 1 and then i click on webview 2 if i go back to webview 1 it will be reloading(refresh) and i want to prevent that from happening.
Please help.
Here is my main activity.
public class MainActivity extends AppCompatActivity implements FragmentDrawer.FragmentDrawerListener {
private Toolbar toolbar;
private FragmentDrawer drawerFragment;
private static String TAG = MainActivity.class.getSimpleName();
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.tool_bar);
if (toolbar != null) {
toolbar.setTitle(R.string.app_name);
setSupportActionBar(toolbar);
}
drawerFragment = (FragmentDrawer)
getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);
drawerFragment.setUp(R.id.fragment_navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), toolbar);
drawerFragment.setDrawerListener(this);
displayView(0);
}
#Override
public void onDrawerItemSelected(View view, int position) {
displayView(position);
}
private void displayView(int position) {
Fragment fragment = null;
String title = getString(R.string.app_name);
switch (position) {
case 0:
fragment = new TopRatedFragment();
title = getString(R.string.title_home);
break;
case 1:
fragment = new GamesFragment();
title = getString(R.string.title_friends);
break;
case 2:
fragment = new MoviesFragment();
title = getString(R.string.title_messages);
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container_body, fragment);
fragmentTransaction.commit();
getSupportActionBar().setTitle(title);
}
}
}
Are you saying that you want to restore the same fragment? This is from developer.android.com
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
This enables the back navigation button to restore the same fragment that already loaded the webview. You are creating a new one each time in the switch statement.
Once you get the back button working how you want it you can easily learn how to manually pull the specific fragment off of the stack.
Edit:
I do not think you are refreshing the webview. You are creating a new fragment each time you select an item by calling the empty constructor. You need to only create each fragment one time and then use the same instance of that fragment when you go back to that webview. Using the back stack will enable it to stay loaded while you leave the application and come back to it later.
Also try using the static method YourFragment.newInstance(params) which returns a new instance of YourFragment. Do this instead of the empty constructor, as that is considered incorrect practice.
I've got an Activity that acts like a presentation, I want to put some buttons so I can go directly to particular fragment (the one I choose from the MainActivity).
I've tried with getSupportFragment and Activity.getSupportFragment but nothing works. And, obviously, neither with a simple intent.
Thanks.
This is the MainActivity "Splash":
public class Splash extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.lay_splash);
}
}
The start of the FragmentActivity. It's not complete, so it contain errors. This is the Activity that contains the Fragments
public class FragmentActivity extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
To switch between Views, I use:
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment views=null;
switch (position) {
case 0:
//news
views= new news();
break;
case 1:
//time
views=new time();
break;
case 2:
//Ins
views=new Ins() ;
break;
}
fragmentManager.beginTransaction()
.replace(R.id.container, views)
.commit();
}
Ok, i can try giving you a solution: instead of creating a new view, you can add all fragments to the SplashActivity following this guide.
Than set all fragment to be with View.GONE.
Once an user click a button, change the View property of the related fragment to Visible and all others to Gone.
Doing this everytimes an user clicks a button, all Fragments disappears except the one you need.
This should work but i suggest to have a look to official documentation or this guide on how to use NavigationDrawer propertly.
Hope i helped, good luck!
The app won't build properly as I think its looking for fragment cruisespeed_Fragment which is actually a fragmentactivity when building a navigation drawer. The reason cruisespeed_Fragment is a fragment activity is it is a tabbed layout which I put together using this tutorial.
In MainActivity.java:
#Override
public void onNavigationDrawerItemSelected(int position) {
Fragment objFragment = null;
switch (position) {
case 0:
objFragment = new project_Fragment();
break;
case 1:
objFragment = new cruisespeed_Fragment();
break;
case 2:
objFragment = new satflow_Fragment();
break;
case 3:
objFragment = new network_Fragment();
break;
}
// update the main content by replacing fragments
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, objFragment)
.commit();
}
And in cruisespeed_Fragment.java:
public class cruisespeed_Fragment extends FragmentActivity implements ActionBar.TabListener {
private ViewPager viewPager;
private CSPagerAdapter mAdapter;
private ActionBar actionBar;
// Tab titles
private String[] tabs = {"Top Rated", "Games", "Movies"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initilization
viewPager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
mAdapter = new CSPagerAdapter(getFragmentManager());
viewPager.setAdapter(mAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Adding Tabs
for (String tab_name : tabs) {
actionBar.addTab(actionBar.newTab().setText(tab_name)
.setTabListener(this));
}
}
The error I'm getting is Error:(62, 31) error: incompatible types
required: Fragment
found: cruisespeed_Fragment
For some reason logcat isn't outputting anything at the moment but once I get that sorted again I will update.
Without testing your code personally it's difficult for me to speak conclusively, but my understanding is that FragmentActivity is an Activity used to host an android.support.v4.app.Fragment, not an android.app.Fragment
As such, you wont be able to launch a FragmentActivity with the following code:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, objFragment)
.commit();
You will need to use an Intent. Also, ensure you are not accidentally mixing support Fragments with regular Fragments. This post goes into good detail on the differences.
FragmentActivity extends Activity and NOT Fragment you need to have your _Fragment() classes extend from some form of Fragment
I'm currently utilizing the Navigation Drawer for my Android APP. In my first fragment, I've a fragment that loads data using Facebook's Graph API. Thus, when my App is first loaded, it first goes to the first fragment.
Then, I use the Navigation Drawer to click on another Fragment and view it.
And then finally, I reuse the Navigation Drawer to proceed back to the first Fragment and view it.
My issue that I'm facing is, how do I proceed to utilize the Fragment that has been created once instead of re-creating it whenever the Navigation Drawer Item is selected. My code for the switching of the fragments are as shown below.
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new SelectionFragment();
break;
case 1:
fragment = new HomeFragment();
break;
case 2:
fragment = new PhotosFragment();
break;
case 3:
fragment = new CommunityFragment();
break;
case 4:
fragment = new PagesFragment();
break;
case 5:
fragment = new SplashFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
I noticed that there is indeed a "new" instance of the Fragment every time whenever the option is selected. How do I go about implementing the logic of creating the Fragment instance ONCE and reusing it, so that I do not have to continuously load the Fragment over and over again.
To anyone who encounters the same issue with me,I've managed to find a solution.
In the container frame,I've to define specific fragment views that I'll be utilizing as shown below.
In each Fragment view,I've to "link" it with the actual Fragment itself as shown below via the "android:name" attribute.Do take note of the the path to the Fragment,example for in my case it's com.example.confesssionsrp.SelectionFragment.
<fragment
android:id="#+id/selectionFragment"
android:name="com.example.confessionsrp.SelectionFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In the the MainActivity(or the Activity where we're viewing the fragments),create variables for each of the Fragments as shown below.
Following that,in the Oncreate of the MainActivity(or your specific Activity),initialize the various fragments as shown below.
Proceed onto creating a new Method called "ShowFragment" as shown below.
private void showFragment(int fragmentIndex, boolean addToBackStack) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
for (int i = 0; i < fragments.length; i++) {
if (i == fragmentIndex) {
transaction.show(fragments[i]);
if (Session.getActiveSession().isClosed()) {
mDrawerLayout
.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
} else {
transaction.hide(fragments[i]);
}
}
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.commit();
}
From then on in the switching of the fragments,manually call upon the "ShowFragment" method with the selected Fragment as shown below.
Doing all of this overall,will not reset the Fragment each time we view it,and therefore is the solution to the answer.Thank you for anyone who has helped me so far :)!
I am using the following code:
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getFragmentManager();
if(position==0){// selection of tabs content
fragmentManager
.beginTransaction()
.replace(R.id.container,
SimulatorFragment.newInstance(position + 1)).commit();
}else if(position==1){
fragmentManager
.beginTransaction()
.replace(R.id.container,
HudFragment.newInstance(position + 1)).commit();
}else if(position==2){
// Display the fragment as the main content.
fragmentManager
.beginTransaction()
.replace(R.id.container,
SettingsBasicFragment.newInstance(position +1)).commit();
}else{
}
}
You can replace by a new instance the first time and store the fragment, if it is not null, then replace by the stored fragment.
The activity must implement NavigationDrawerFragment.NavigationDrawerCallbacks
The fragment constructor and newInstance methods look like this:
public final class HudFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section number.
* #param simulation
*/
public static HudFragment newInstance(int sectionNumber) {
HudFragment fragment = new HudFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public HudFragment() {
}
To switch fragments by code I use this method inside the NavigationDrawerFragment:
/**
* Select a different section
* #param position
*/
public void select(int position){
selectItem(position);
}
private void selectItem(int position) {
mCurrentSelectedPosition = position;
if (mDrawerListView != null) {
mDrawerListView.setItemChecked(position, true);
}
if (mDrawerLayout != null) {
mDrawerLayout.closeDrawer(mFragmentContainerView);
}
if (mCallbacks != null) {
mCallbacks.onNavigationDrawerItemSelected(position);
}
}
The second option is to start with the example of navigationDrawer that Android SDK offers. I selected that template of activity when creating the project and almost all the code of my previous answer is produced automatically.
If you want to keep the fragments after device rotation or similars it is a different thing, you need then to retain the fragments. If not, you just need to save the new instance of the fragment in a variable and check if it is null to create a new one or use the old one.
In case someone want's a different approach to this: you could find the fragment on the stack:
// check if this fragment is on the backstack to avoid creating a new one
Fragment foundFragment = fragmentManager.findFragmentByTag("unique_fragment_tag");
if (foundFragment != null) {
fragmentManager.popBackStackImmediate("unique_fragment_tag", 0);
return;
}