I have made an activity A which has a fragment X in it. In fragment X, EditText item has on click event which opens fragment Y. This fragment displays a list of names. I press a name in the list, fragment Y closes and sends the selected name of to fragment X EditText. Here's the code I wrote:
YFragment y = new YFragment();
y.setTargetFragment(x.class, code);
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame, y)
.addToBackStack(null)
.commit();
In fragment Y I have the code to send the data but the problem is in this block of code above. If I comment out the setTargetFragment line the code will work but no use as data will not be sent. If I run the app this error occurs:
java.lang.IllegalStateException: Fragment y{46d3d31 #3 id=0x7f090069}
declared target fragment x{e2c16 #0 id=0x7f090104
android:switcher:2131296516:0} that does not belong to this
FragmentManager!
To use setTargetFragment(), both the new Fragment and the target Fragment must be hosted within the same FragmentManager. The most common case where this would not happen is if you are using Activity.getSupportFragmentManager() or Fragment.getFragmentManager() alongside Fragment.getChildFragmentManager().
In my solution, replace
getChildFragmentManager()
with
Activity.getSupportFragmentManager() or Fragment.getFragmentManager()
that worked for me. Thank Mr.Ben P
To new users where the "getFragmentManager" only work for it but don't want use deprecated method, the method "getParentFragmentManager" work too.
Related
First of all, I'm pretty new to Android development.
I'm building an simple Android app with 1 Activity which contains 3 fragments.
Lets name the fragments A, B and C. Each fragment contains some input fields and a "next" button. The last fragment contains a "finish" button which sends the data (collected in the fragments) to an API and navigates to fragment A again to start over again.
In fragment A and B, i navigate to the next fragment by calling this function:
private void replaceFragment(Fragment someFragment) {
FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
transaction.replace(R.id.nav_host_fragment_content_main, someFragment);
transaction.addToBackStack(null);
transaction.commit();
}
This works fine so far. In fragment C i post the collected data to the API and call this code afterwards:
FragmentManager fm = getParentFragmentManager();
fm.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
This works perfect but only once. After i'm "redirected" from fragment C to fragment A and go again via B to C and click Finish in fragment C, the mentioned code is triggered but I'm not redirected to A anymore.
When i kill the app and restart the app, it works once again. What I'm doing wrong?
Update
During debugging i can see that the BackStack is a list with BackStackEntries. This list always contains 2 entries when I'm in frament C and i thought fm.popBackStackImmediate(0,... "redirects" to entry 0 of the BackStack. Now i notice that each BackStackEntry has a property mIndex which is increasing all the time. Probably is the id parameter in the popBackStackImmediate not the position in the BackStack but refers to the BackStackItem its mIndex.
Is this true and if yes, how can i get the ID of the correct back stack item in this case?
It seems that the id in fm.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE); is not the position of the BackStackEntry in the BackStack but the id of the BackStackEntry. This id is increasing never reused so its continuously increasing.
The BackStackEntry of fragment A is always on position 0 in my case so i can simply get its id like:
Integer $homeFragmentBackStackId = fm.getBackStackEntryAt(0).getId();
And than do:
fm.popBackStackImmediate($homeFragmentBacStackId,FragmentManager.POP_BACK_STACK_INCLUSIVE);
In my activity page, I have a Button to call a Fragment. FragmentManager tries to call that fragment's newInstance function.
It used to work well but from some point in the project, I needed to change its name to something else because its name was testObjectBlaBla and I needed to release it to Play Store so I changed its package name. But now it gives
java.lang.IllegalArgumentException: No view found for id 0x7f0900d0
(com.myblabla.example:id/fragment_container) for fragment
MarkerSpecification_Vehi{18103143 #0 id=0x7f0900d0 BLANK_FRAGMENT}
I tried to change the package name back to the old one but still but I can't change the name every time I want to go live.
MarkerSpecification_Acco fragment =MarkerSpecification_Acco.newInstance();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.enter_from_right,R.anim.exit_to_right,R.anim.enter_from_right,R.anim.exit_to_right);
transaction.addToBackStack(null);
transaction.add(R.id.fragment_container,fragment,"BLANK_FRAGMENT").commit();
Not sure if this is the true piece because exception not giving any error in my own code but in fragmentManager.java class instead. Probably bacause trying to search the old package name com.myblabla.example instead of com.project.project. But I think it is the only place where the code tries to find a fragment.
How about you change the calling of the fragment to this:
Fragment frag = new MarkerSpecification_Acco();
Also check in your XML File if your container reference has changed to your new Fragment name.
I'm building an app with multiple pages to it, each of the main pages is displayed in a fragment in the main activity. Whenever the user wants to change page, the fragment within the activity is changed.
I want to write the code for each fragment within its own java file, as opposed to writing the code for all of the fragments within the main activity.
I've tried putting each bit of code within the fragment's onCreate and onCreateView methods (including trying onStart, onPause and whole other load of ones) but I've noticed that when the fragment is created the code just isn't running.
I've seen other questions on here with similar issues but none of the answers offered me an actual solution.
My question is this, how can I write the code for each fragment within that fragment's java file and actually get it to run? I understand this would be much easier if my fragments were created statically in the activity but these are not, they are created at runtime.
For reference, each fragment is displayed in the activity like so:
fragmentManager.beginTransaction().replace(R.id.container, HomeFragment.newInstance(position)).commit();
Try this,
From main activity you can call a static method defined in your fragment class as:
AuthFragment.NewInstance(LoginActivity.this, constantsObj);
This only line will go in your fragment calling activity.
Now in your fragment, inside the static method, find and create the fragment as,
public static void NewInstance(LoginActivity activityContext, Constants constantsObj) {
clsConstantsObj = constantsObj;
urlToCatch = clsConstantsObj.toString();
urlToCatch = urlToCatch.replace("/params", "");
FragmentManager fragmentManager;
FragmentTransaction fragmentTransaction;
activityContextInFragment = activityContext;
fragmentManager = activityContext.getSupportFragmentManager();
AuthFragment findAuthFragment = (AuthFragment) fragmentManager
.findFragmentByTag("authFragment");
if (findAuthFragment == null) {
AuthFragment authFragment = new AuthFragment();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.mainviewRegionwise, authFragment, "authFragment");
fragmentTransaction.commit();
}
}
Notice the "R.id.mainviewRegionwise", which is the id of your main container which could be a FrameLayout or any other container.
I am new to Andoid development and I have created a snippet for replacing fragments programetically.
I followed the guide on android developers.
I have create a method named selectFrag and fired it on button click:
public void selectFrag(View view)
{
Fragment fr;
if(view == findViewById(R.id.showsecond)) {
fr = new secondfragment();
} else {
fr = new firstfragment();
}
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_place,fr);
ft.addToBackStack(null);
ft.commit();
}
Code works perfectly and I understand everything except addToBackStack(null).
I experimented and understood that this method is for adding the fragment to the back button's stack, so that if we click back button, it do not leave the screen and show the previous work.
But, I do not understand what null shows here. I searched on web but I only knew that it is a TAG and we could use something like this.
So, my question is very simple :What is meant by null here? or What does null do?
(sorry for my bad English.)
From documentation, it is pretty clear:
public abstract FragmentTransaction addToBackStack (String name)
Add this transaction to the back stack. This means that the transaction will be remembered after it is committed, and will reverse its operation when later popped off the stack.
Parameters
name // An optional name for this back stack state, or null.
So your parameter is optional and represents the name of the fragment.
If you just want to add this transaction to the back stack and don't need to access it later then you can put null as the name.
In this context, null in plain English means "I don't need a name for this fragment". That is why it says the name is optional. If you do put a name you can use that name later. If you put a null that just means "add this fragment to the back stack and I don't need it anymore".
The use of the name is to identify that specific fragment. This can be useful for example if you want to obtain that fragment from the FragmentManager:
addToBackStack (FRAGMENT_NAME);
getFragmentMangager().findFragmentByTag(FRAGMENT_NAME);
Just want to clarify, the 'name' used in addToBackStack(name) can't be used for retrieving the fragment by calling fragmentManager.findFragmentByTag(tag). The 'tag' is different from the 'name'.
The 'tag' used in add/replace(id, fragment, tag) is used to retrieve the fragment by calling fragmentManager.findFragmentByTag(tag).
However, the 'name' used in addToBackStatck(name) is used to control to which fragment you want to pop the fragment back stack by calling popBackStatck/Immediate(name, flags). So if I have a fragment stack with named fragments: A, B, C and D with A at the bottom. When you call popBackStack(B, XXX_EXCLUSIVE), then your fragment back stack will be like: A and B after the call. Without the name, you can't do that.
I think you should not use .addToBackStack(null);
if you are trying to popthe Backstack chances are that it might throw
java.lang.IllegalStateException as it wont be having valid reference to Popout the last item which is added. This needs to be checked... i am not 100% sure.
In the main activity of my app there is a container that hosts fragments.
When a user clicks a button in the "default" fragment (the first fragment that is displayed), the fragment changes, and so do the actionbar buttons.
One of the buttons in the actionbar of this new fragment open another activity.
In that activity, when a user clicks the back button, the activity closes, and the fragment that was shown in the MainActivity (the fragment that opened the new activity) is still there (which is fine).
However, if a user clicks the back button again, it does not return to the previous fragment. While it does return when the activity does not open.
It turns out that opening the activity clears the backstack (verified by Logging the count from the FragmentManager class), while I'm not quite sure whether this is supposed to behave like this or not, it kinda makes sense. Unfortunately, it is not the behavior I desire.
MainActivity: Fragment A (default) ---> Fragment B ---> Acivity B
Therefore, my question is how can I keep the backstack after the activity resumes, if at all?
I tried searching for similar questions, but all questions I found actually asked how to clear the backstack.
Try that:
#Override
public void onBackPressed() {
Intent intent = new Intent(A_Acticity.this, B_Activity.class);
startActivity(intent);
}
Hope it helped! :)
Reading the documentation, there is a way to pop the back stack based on either the transaction name or the id provided by commit. Using the name may be easier since it shouldn't require keeping track of a number that may change and reinforces the "unique back stack entry" logic.
Since you want only one back stack entry per Fragment, make the back state name the Fragment's class name (via getClass().getName()). Then when replacing a Fragment, use the popBackStackImmediate() method. If it returns true, it means there is an instance of the Fragment in the back stack. If not, actually execute the Fragment replacement logic.
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(backStateName);
ft.commit();
}
}