ffIn Main Activity,
private void navigateTo(int position) {
Log.v(TAG, "List View Item: " + position);
switch(position) {
case 0:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, new Home(), Home.TAG).commit();
break;
case 1:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame,
TabbedActivity.newInstance(),
TabbedActivity.TAG).commit();
break;
case 2:
getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, History.newInstance(), History.TAG).commit();
}
}
When i navigate to my home fragment through navigation drawer(based on the method above), it run onCreate method everytime. From my understanding, fragment cycle should run oncreateview instead oncreate method when you navigate between the screen through navigation drawer(since onpause>oncreateview). Can anyone provide a solution for me that can make sure onCreate only run once?
You are creating new instances each time you call navigateTo.
You should do something like this...
FragmentManager fragManager = getSupportFragmentManager();
// try to find an existing instance
Fragment frag = fragManager.findFragmentByTag(Home.TAG);
if (frag == null) {
// if none were found, create it
frag = new Home();
}
fragManager.beginTransaction().replace(R.id.content_frame, frag, Home.TAG).commit();
Related
I have the slider menu displayview method as:
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new HomeFragment();
FragmentManager fm = getSupportFragmentManager();
String tag = fragment.getTag(); // instance method of a to get a tag
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frame_container, fragment, tag);
ft.addToBackStack(tag);
ft.commit();
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).commit();
// update selected item and title, then close the drawer
drawerListView.setItemChecked(position, true);
drawerListView.setSelection(position);
setTitle(navMenuTitles[position]);
drawer.closeDrawer(drawerListView);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
I have a fragment called home_fragment whose code behind is HomeFragment.java
When I click on this fragment first it works fine, but on clicking next from the siding menu at index 0 I get an error as:
Attempt to write to field 'int android.support.v4.app.Fragment.mNextAnim' on a null object reference
What am I doing wrong? Thanks.
This is where I'm having error:
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.removeFragment(f, transition, transitionStyle);
} break;
This code is present in BackStackRecord.java
You shouldn't be calling container.removeAllViewsInLayout().
The FragmentManager will handle all the view manipulation on the container when you call FragmentTransaction.replace(), including removing the current fragment view. You are probably getting an error because the FragmentManager expects a fragment's view to be in the container, but you've removed it.
All you need is this:
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new HomeFragment();
break;
case 1:
// set fragment to other nav target
fragment = ...
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
drawerListView.setItemChecked(position, true);
drawerListView.setSelection(position);
setTitle(navMenuTitles[position]);
drawer.closeDrawer(drawerListView);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
Also I noticed you are using getFragmentManager(). Please check your class imports and make sure that you have the right call.
If HomeFragment extends android.app.Fragment, use getFragmentManager().
If HomeFragment extends android.support.v4.app.Fragment, use getSupportFragmentManager().
My activity is composed of 3 nested Fragments. There is my MainFragment that is displayed by default, ProductFragment that can be called from it, then DetailFragment can be called from ProductFragment.
I can go back and forth between my ProductFragment and DetailFragment. By doing so, the popStackBack method is accumulating similar fragments. Then, if I click on the back button, It will go back through all the Fragments as many time I called them.
What is the proper way to avoid the same Fragment to be kept in the back stack ?
EDIT :
I firstly call my main fragment :
if (savedInstanceState == null) {
getFragmentManager()
.beginTransaction()
.add(R.id.container, new SearchFragment(), "SEARCH_TAG")
.commit();
}
Here is the code that calls the fragments from the activity :
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_bottom, R.animator.exit_to_top, R.animator.enter_from_bottom, R.animator.exit_to_top);
ft.replace(R.id.container, new FactFragment(), "FACT_TAG");
ft.addToBackStack("FACT_TAG");
ft.commit();
Then, on back click :
#Override
public void onBackPressed() {
getFragmentManager().popBackStack();
}
I tried to get the tag of my current fragment and execute some specific code related to it but it doesn't work well. I also tried to addToBackStack() only when current Fragment wasn't already added to the backStack but it messed up my fragment view.
Use fragment's method isAdded() to evaluate the insertion. For example:
if(!frag.isAdded()){
//do fragment transaction and add frag
}
Here is my solution. Maybe dirty but it works. I implemented a method that returns the tag of the fragment that is displayed before clicking the on back button :
public String getActiveFragment() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getFragmentManager()
.getBackStackEntryAt(getFragmentManager()
.getBackStackEntryCount() - 1)
.getName();
return tag;
}
Then, on my onBackPressed() method :
// Get current Fragment tag
String currentFrag = getActiveFragment();
if(currentFrag.equals("PRODUCT_TAG")) {
// New transaction to first Fragment
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_right, R.animator.exit_to_left, R.animator.enter_from_right, R.animator.exit_to_left);
ft.replace(R.id.container, new SearchFragment(), "MAIN_TAG");
ft.commit();
} else {
// Go to Fragment-1
getFragmentManager().popBackStack();
}
Here is my handy and simple solution to check for duplicate insertion through fragment manager
at first, I check if it is first time intention for adding fragment and then I check if the fragment is presented using fragment manager
Fragment fragment = getSupportFragmentManager().findFragmentByTag("firstFragment");
if (fragment == null) {
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}else if(!fragment.isAdded()){
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}
Here is my solution:
Fragment curFragment = fragmentManager.findFragmentById(R.id.frameLayout);
if(curFragment != null
&& curFragment.getClass().equals(fragment.getClass())) return;
// add the fragment to BackStack here
Xamarin.Android (C#) version:
var curFragment = fragmentManager.FindFragmentById(Resource.Id.frameLayout);
if (curFragment != null
&& curFragment.GetType().Name == fragment.GetType().Name) return;
// add the fragment to BackStack here
I did a navigation drawer, and I’ve set some items and normally when I select an item the current fragment will be changed by a new one, it's not the case for me, the first Activity still displayed even if the fragments change. This is my onNavigationDrawerItemSelectedmethod and it's clear I change the fragment every time i click on a new one.
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
// TODO : Added
switch(position) {
/*case 0 :
fragmentManager.beginTransaction()
.replace(R.id.container, new ProfilFrgement())
.commit();
break;*/
case 1:
fragmentManager.beginTransaction()
.replace(R.id.container, new SpotFragement())
.commit();
break;
case 2:
fragmentManager.beginTransaction()
.replace(R.id.container, new SessionsFragement())
.commit();
break;
/*case 3:
fragmentManager.beginTransaction()
.replace(R.id.container, new EventsFragment())
.commit();
break;*/
}
}
I think my problem is a change always the same container. So what I need its getting the current fragment ID
Sorry for too late but I have a solution to get fragment ID. You can use name of Fragment Class as an Identifier not the ID. Use a static String variable to store class name.
public class Activity{
...
static String currentFragment = null;
...
...
#Override
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_one:
setFragment(new oneFragment());
break;
case R.id.nav_two:
setFragment(new twoFragment());
break;
}
return true;
}
public static void setFragment(#NonNull Fragment f) {
if(!f.getClass().getName().equals(currentFragment)){
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container, f);
fragmentTransaction.commit();
currentFragment = f.getClass().getName();
}
...
}
What is current fragment ID here? Ur id is R.id.container which is only one rt? then what is the question about "How can i get the current Fragment ID"?
If question is to find which is the current fragment is shown to user? If yes then
Fragment ft = getSupportFragmentManager().findFragmentById(R.id.container);
if (ft instanceof SpotFragement) {
// navigation drawer item 1 and current fragment is SpotFragment.
} else if (ft instanceof SessionsFragement) {
}
Make sure that your container styling is correct.
Check following values of container once -
android:layout_width
android:layout_height
I have a navigation drawer that works well. By well I mean I can navigate through all the fragments tied to the Navigation drawer (A,B,C,D). My issue arises here. Lets say am in Fragment A, in this fragment I have a button which on clicking should ideally replace the fragment with another one say Frag Z, and once I am in Frag Z I can return back to frag A.
To achieve this I use the below code. However the new fragment Z appears transparent on top of Fragment A. What could be the problem:
Bundle bundle = new Bundle();
bundle.putInt("int", 1);
bundle.putString("str","string" );
fragmentTransaction = getFragmentManager().beginTransaction();
FragZ fragmentz = new FragZ();
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.some_container, fragmentz);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
EDIT:
I have found a workaround to this as shown below,however it does not completely solve my problem since the newly displayed fragment is still shown with the navigation drawer capable of being toggled,but it atleast removed the overlaying of the fragments. I would still want a solution to launching an independent fragment
Bundle bundle = new Bundle();
bundle.putInt("int", 1);
bundle.putString("str","string" );
FragZ fragmentz = new FragZ();
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
fragment.setArguments(bundle);
ft.replace(R.id.main, fragment);
ft.addToBackStack(null);
ft.commit();
Create a method in your activity to launch fragments. Whenever you want to display the fragment, just call in with a number or string to represent the fragment. You can use the same function to launch fragments from your navigation drawer.
For example:
public void displayView(int position) {
Fragment fragment = null;
currentFragmentNumber = position;
switch (position) {
case 0:
fragment = new frag1();
break;
case 1:
fragment = new frag2();
break;
case 2:
fragment = new frag3();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment)
.addToBackStack("tag").commit();
} else {
// error in creating fragment
Log.e(TAG, "Error in creating fragment");
}
}
You can call this function from your activity by using:
((**ActivityName**) getActivity()).displayView(0);
I am using fragment in my application. When I change the orientation the fragment removes and my main activity gets visible from which I have added this fragment. below is the code -
Fragment fragment = null;
switch (position) {
case 0:
fragment = new ManageDataFragment();
break;
case 1:
fragment = new DownloadFragment();
break;
case 2:
fragment = new UserInfoFragment();
break;
case 3:
fragment = new AboutAppFragment();
break;
case 4:
fragment = new ShareAppFragment();
break;
case 5:
fragment = new SettingsFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).commit();
// update selected item and title, then close the drawer
setTitle(menutitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
I am using this fragment from navigationDrawer like in Gmail application.
What should I do that my fragment remains even I change the orientation of device?
Thanks in Advance
I don't think the accepted answer is the correct so I'm writing this one:
When replacing the fragment you should use the method replace with three arguments so you can find your fragment later:
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment, TAG_FRAGMENT).commit();
Where TAG_FRAGMENT is some string tag.
Then in onCreate if the activity was restarted you can find your fragment and add it to the container:
if(savedInstanceState != null) {
fragment = getFragmentManager().findFragmentByTag(TAG_FRAGMENT);
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment, TAG_FRAGMENT).commit();
}
I had exactly the same problem as mentioned above and I fixed it in the AndroidManifest by adding this:
<activity
...
android:configChanges="orientation|screenLayout|screenSize|layoutDirection">
Hope this helps!
Probably should save position using onSaveInstanceState() and test the bundle argument to onCreate().