Android replace fragment doesn't hide underlying fragment - java

I have the following problem:
My app consists of several fragments that are dynammicly added. There is one fragment with a push button and an textedit (called 'fragA').
If I click the push button I want to show an different fragment with some text (called 'fragB'). I do this with the following code (in fragA class):
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Fragment howFragment = new HowFragment();
FragmentTransaction transaction = null;
transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.flQuestion, howFragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
Now the problem is this:
When i push the button on fragA the fragment (fragB) is created and show on the screen but when I push on the location where the pushbutton was on fragA it makes an new fragment (fragB). Also if i push where the textedit on fragA was located it opens an keyboard on fragB..
It looks like FragB is just overlaying fragA without replacing it.
I also want to achieve that when i swype to the next fragment that fragB is removed and fragA is just showed normally (state when not pressed button)
Update #
When trying to add and remove this is the following logcat output:
FATAL EXCEPTION: main
java.lang.IllegalArgumentException: No view found for id 0x7f090015 (com.example.eindwerkappv1:id/flQuestion) for fragment HowFragment{419c93c8 #3 id=0x7f090015}
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:903)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4441)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
at dalvik.system.NativeStart.main(Native Method)

I would guess your fragA fragment is defined in your XML layout?
According to the documentation, this is what happens when you use the <fragment> tag:
the system inserts the View returned by the fragment directly in place
of the element.
This is why you cannot remove the previous fragment, as it does not exist.
If you want to changes fragment from code, you have to add the first fragment from code too.
You need to use a container like a FrameLayout, and add the first fragment to this container in the onCreate() of your activity, using FragmentTransaction.add().
Then FragmentTransaction.replace() method should work.

So why don't you just call the .remove() on the EditText Fragment and .add() the new TextView Framgent instead on osing the .replace()
do some thing like this:
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(addCommentFragment)
.add(R.id.containerForFragments, commentFragment, "comment"+
String.valueOf(numOfComments)).commit();
this worked for me.

I found the solution now.
I still use the replace method but I had to catch the ontouch event in the new fragment:
Fragment over another fragment issue
See previous link for the answer

Related

OnBackPressed for each item in the NavigatorView

There is a NavigatorView in which I am using the NavController. The whole problem is that when replacing the Fragmenta, I immediately set fragmentTransaction.addToBackStack (String.valueOf (FragmentRink3)); and when navigating to another NavController when clicking on OnBackPressed, is applied to the fragment from the previous NavController where the replacement was made. It's hard to explain. Is it possible to somehow do so that each of the lower navigation menu has its own, I don’t know, a list of clicks, according to which the sequence of transitions to fragments was determined? I know I explained very poorly, I will attach a gif to show the problem clearly.
As you can see, by clicking on the button, the fragment was replaced, then I went to the next menu and clicked OnBackPressed, and at this moment the fragment, which was replaced in the previous menu, went back. How to fix it?
I will replace the fragment as follows:
btn_close.setOnClickListener(v ->
{
FragmentRink4 fragment = new FragmentRink4(); // you fragment
FragmentManager fragmentManager = ((FragmentActivity) v.getContext()).getSupportFragmentManager(); // instantiate your view context
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.nav_controller_rink1, fragment);// your container and your fragment
fragmentTransaction.addToBackStack(String.valueOf(FragmentRink3));
fragmentTransaction.commit();
});
Here is a link to the project.

IllegalStateException in NavigationComponent

I use Navigation Component from Jetpack in my app. I have 2 fragments, e.g. FirstFragment and SecondFragment and I have navigation in navigation graph from the first to second. Everything works correctly this way. I want to add child fragment to FirstFragment. So in onCreateView method of FirstFragment class I added the line
getChildFragmentManager().beginTransaction().add(R.id.fragment_container, new SimpleFragment()).commit();
This way I have an inner fragment in FirstFragment. Navigation to SecondFragment from FirstFragment still works correctly, but when I press the back button in SecondFragment I get this error
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.abc.def, PID: 28856
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:4937)
at android.view.ViewGroup.addView(ViewGroup.java:4768)
at android.view.ViewGroup.addView(ViewGroup.java:4708)
at android.view.ViewGroup.addView(ViewGroup.java:4681)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1353)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1642)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1736)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1800)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3096)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3050)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2688)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1369)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2633)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2377)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2333)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2230)
at androidx.fragment.app.FragmentManager$3.run(FragmentManager.java:414)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
I get this error if I either press the back button or call navController.navigateUp() or navController.popBackStack(). If I remove the line
getChildFragmentManager().beginTransaction().add(R.id.fragment_container, new SimpleFragment()).commit();
from FirstFragment I have no error, so this means that there is a problem with child fragment manager, right? What is wrong here?
If I understand this correctly without knowing what you have without looking at your layout of "fragment_container" tells me that its type of ViewGroup which can have only one child at this point if you don't provide more information to it.
What that means is fragment_container already has a child and which you need to remove before you add your SimpleFragment.
Lets assume you have your first layout of fragment something like this
<someView>
<fragment_container/> // hear you are loading your first fragment and trying load the SimpleFragment as well which is causing problem.
</someView>
what you need is
<someView>
<fragment_container/> only load First Fragment "first_fragment_layout.xml"
</someView>
And in layout of First fragment
first_fragment_layout.xml
<someView>
<viewBlasBla/>
<viewBlasBla/>
<fragment_container/> load SimpleFragment here
</someView>
as you can see, you are indeed trying to do nested fragment which is not a Good Practice. try to avoid this if possible :).
you can reach me here
The problem was in SimpleFragment. I was keeping reference to the root view of that fragment (had View mRootView field), and didn't recreate it in case it wasn't null.

Fragment declared target fragment that does not belong to this FragmentManager

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.

Issues with code in dynamically created fragments

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.

Keeping the back stack after it is being cleared by opening a new activity

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();
}
}

Categories