I have one activity containing one container that will receive 2 fragments.
Once the activity initialises i start the first fragment with:
showFragment(new FragmentA());
private void showFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment, fragment.getTag())
.addToBackStack(fragment.getTag())
.commit();
}
Then when the user clicks on FragmentA, I receive this click on Activity level and I call:
showFragment(new FragmentB());
Now when I press back button it returns to fragment A (thats correct) and when i press again back button it show empty screen (but stays in the same activity). I would like it to just close the app (since the activity has no parent).
There are a lot of posts related with Fragments and backstack, but i can't find a simple solution for this. I would like to avoid the case where I have to check if im doing back press on Fragment A or Fragment B, since i might extend the number of Fragments and I will need to maintain that method.
You are getting blank screen because when you add first fragment you are calling addToBackStack() due to which you are adding a blank screen unknowingly!
now what you can do is call the following method in onBackPressed() and your problem will be solved
public void moveBack()
{
//FM=fragment manager
if (FM != null && FM.getBackStackEntryCount() > 1)
{
FM.popBackStack();
}else {
finish();
}
}
DO NOT CALL SUPER IN ONBACKPRESSED();
just call the above method!
addToBackStack(fragment.getTag())
use it for fragment B only, not for fragment A
I think your fragment A is not popped out correctly, try to use add fragment rather replace to have proper back navigation, however You can check count of back stack using:
FragmentManager fragmentManager = getSupportFragmentManager();
int count = fragmentManager.getBackStackEntryCount();
and you can also directly call finish() in activity onBackPressed() when you want your activity to close on certain fragment count.
I'm adding fragments to my activity in way that I first hide current fragment and then add new one. My problem is that when I show my new fragment and start interacting with him, it also interacts with previous one.
The code which I use to add new and hide current fragment is:
public void add(int containerViewId, Fragment fragment, boolean addToBackStack){
FragmentTransaction ft = fragmentManager.beginTransaction().hide(currentFragment).add(containerViewId, fragment);
if (addToBackStack){
ft.addToBackStack(null);
}
ft.commit();
currentFragment = fragment;
backStackCount++;
}
What is going on, and how to hide fragment so I can interact only with the last one added? replace is not an option because I don't want to remove current fragment.
I also had similar problem. I don't know what possibly is creating this issue but what I did to resolve it is that I set an onclick listener to the outermost layout of my fragment.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/top_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:clickable="false"
android:orientation="vertical"
tools:context=".Fragments.TopicsFragment">
...other components
</LinearLayout>
In fragment:
LinearLayout topLayout = (LinearLayout) fragmentView.findViewById(R.id.top_layout);
topLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do nothing
}
});
Also you might see I have added a background #FFFFFF to this outermost layout, because in mycase the content of previous fragment was also visible behind the new one. So this solved that problem too.
My Android app consists three fragments: A, B and C. They're loaded in the two containers defined in the MainActivity layout.
When the app is started, it shows the fragmentA loaded in the left_container and the fragmentC in the right_container.
If you press the button in the fragmentA, a FragmentTransaction changes FragmentC by FragmentB.
At the moment everything OK. But the trouble appears when I try to get a reference to the loaded fragmentB using findFragmentByTag(), because it returns null. I've used the method replace in the FragmentTransaction and I've finished it with commit(), but there isn't way to call FragmentB method. My code:
MainActivity.java:
public class MainActivity extends Activity{
static String fragmentTag = "FRAGMENTB_TAG";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Adds the left container's fragment
getFragmentManager().beginTransaction().add(R.id.left_container, new FragmentA()).commit(); //Adds the fragment A to the left container
//Adds the right container's fragment
getFragmentManager().beginTransaction().add(R.id.right_container, new FragmentC()).commit(); //Adds the Fragment C to the right container
}
/**
* Called when the button "Activate Fragment B" is pressed
*/
public void buttonListener(View v){
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.right_container, new FragmentB(),fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
ft.commit(); //Finishes the transaction
//!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();
}
}
FragmentB.java:
public class FragmentB extends Fragment {
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container,false);
}
/**
* Gets a reference to the text_fragment_b TextView and calls its method setText(), changing "It doesn't work" text by "It works!"
*/
public void testView(){
TextView tv = (TextView)getView().findViewById(R.id.text_fragment_b);
tv.setText("It works!");
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout android:id="#+id/left_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>
<FrameLayout android:id="#+id/right_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>
</LinearLayout>
fragment_b.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="5sp">
<TextView
android:id="#+id/text_fragment_b"
android:text="It doesn't works!"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Please help me! I'm a beginner in Android development!
I've fixed it! I called getSupportFragmentManager().executePendingTransactions() after doing the transaction and it worked! After calling that method I can get the fragment using both findFragmentById() and findFragmentByTag() methods.
if you use setRetainInstance(true) than you can't use findFragmentByTag() in onCreate from the Activity. Do it at onResume
see the documentation: setRetainInstance
I'll start by apologising since I'm still very new myself...
I think the problem may be in the declaration of the fragmentTag static String not properly getting access from the class's instances, just change that line to:
private final static String FRAGMENT_TAG = "FRAGMENTB_TAG"; // using uppercase since it's a constant
Also, I would be more explicit when declaring instances, for example:
public void buttonListener(View v){
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.right_container, new FragmentB(), FRAGMENT_TAG);
ft.commit();
FragmentB fragB = (FragmentB) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
fragB.testView();
}
I hope you get this sorted, as I seen this question posted earlier and was surprised that it hadn't got any activity yet.
Also, here are a couple of links to the android documentation on replace:
Android Training - Replace
Android Reference - Replace
I had the same problem and realized that there is a really simple way to fix this. When using a tag please do make sure to add the
fragmentTransaction.addToBackStack(null);
method so that your Fragment is resumed instead of destroyed as mentioned in the developer guides.
If you don't call addToBackStack() when you perform a transaction that removes a fragment, then that fragment is destroyed when the transaction is committed and the user cannot navigate back to it. Whereas, if you do call addToBackStack() when removing a fragment, then the fragment is stopped and is later resumed if the user navigates back.
You can find this at the end of this section.
Every time I tried to reference back to my created Fragment, it turns out it had already been destroyed so I lost about 30 minutes trying to figure out why my Fragment was not being found through a simple findFragmentByTag(); call.
Hope this helps!
Be sure you are adding or replacing the fragment in the proper way
Next statement will add the fragment but it will return null when using getFragmentManager().findFragmentByTag(tag):
transaction.add(R.id.mainContent, fragment);
This way it will work;
transaction.add(R.id.mainContent, fragment, tag);
We are also seeing this problem but the cause is slightly different. The suggested solution by https://stackoverflow.com/a/21170693/1035008 doesn't work for us.
void updateFragment(Fragment newFragment) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
// We have these 2 lines extra
Fragment current = getChildFragmentManager().findFragmentByTag(fragmentTag);
if (current != null) { ft.remove(current); }
ft.replace(R.id.right_container, newFragment, fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
ft.commit(); //Finishes the transaction
//!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();
}
And after reading the documentation about replace:
Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.
I realize that the remove call was not necessary since it is done by replace automatically. So after delete ft.remove(current), it works fine.
In my case I used the code to replace and add to BackStack, but set wrong tag:
val fragment = { SomeFragment.newInstance() }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, WrongAnotherFragment.TAG)
Of course, supportFragmentManager.findFragmentByTag(SomeFragment.TAG) didn't find SomeFragment.
For me probably it was a newbie mistake that I was calling super.onCreate(savedInstanceState); after I was trying to access the Fragment using findFragmentByTag.
I moved super.onCreate(savedInstanceState) up in the order and it started working for me.
I have strange behavior happening when I try to use the backstack of nested fragments in My app.
Say I have this fragment, FragmentA, and it is placed programatically inside of another rootFragment with a linearlayout designed to hold fragments.
rootFragment's layout contains:
<LinearLayout
android:id="#+id/map_fragmentHolder"
android:layout_height="0dp"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_weight="1.5" />
I use my switchToFragment method (outlined below) to replace FragmentA with a fragment of type FragmentB.
public void switchToFragment(Fragment fragment, boolean addtoBackStack) {
//Perform the fragment switch
FragmentTransaction childTransaction = rootFragment.getChildFragmentManager().beginTransaction();
childTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE |
FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
childTransaction.replace(R.id.map_fragmentHolder, fragment);
if(addtoBackStack){
if(currentFragment != null)
fragmentStack.push(currentFragment);
childTransaction.addToBackStack(null);
}
currentFragment = fragment;
childTransaction.commit();
}
This works perfectly when the fragment replacing the currentFragment is of different types. However for some strange reason, if I try to switch in another fragment of type FragmentB when there is one currently added, the back stack suddenly stops working. Instead of replacing all fragments within the linearlayout, the old fragment is placed below the current one.
Here is how I call the back stack to pop. It is done this way because the onBackPressed() method does not handle backstacks on nested fragments by default.
public boolean onBackPressed() {
if(fragmentStack.size() == 0)
return false;
//ChildFragmentManagers don't pop the backstack automatically, so we gotta do it manually...
rootFragment.getChildFragmentManager().popBackStack();
currentFragment = fragmentStack.pop();
return true;
}
I have managed to make my app work if I simply replace my onBackPressed method with the following, however I would really like to know what is happening, and why it only happens when I am replacing the fragment with one of the same type.
public boolean onBackPressed() {
if(fragmentStack.size() == 0)
return false;
switchToFragment(fragmentStack.pop(), false);
return true;
}
Any ideas or insights would be greatly appreciated.
In my app, I have only one Activity which hosts several fragments.
The layout of my activity is(main.xml):
<LinearLayout...>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/fragment_placeholder">
</FrameLayout>
</LinearLayout>
My only Activity:
public class MyActivity extends FragmentActivity{
...
#Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.main);
//I dynamically add fragments into fragment_placeholder of the layout
FirstFragment firstFragment = new FirstFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_placeholder, FirstFragment, "first");
fragmentTransaction.commit();
}
}
I my above activity, I dynamically add the first fragment to layout. The other fragments replace this fragment accordingly.
I know when user press back button to exit the app, by default, my app will still run on background.
what I want however is to kill the app process when user seeing the firstFragment and press the back button (exits the app). But how can I kill my app technically(programmatically) in Android?
I end up killing my app process by:
android.os.Process.killProcess(android.os.Process.myPid())
you can override the back button behaviour by using below code and kill your activity
public void onBackPresed(){
finish();
}