FragmentTransaction 'add' not working - android

I am having problems adding a fragment to a frame layout placeholder:
TestDialogFragment newFragment = new TestDialogFragment();
int id = R.id.fragment_placeholder; //id of the FrameLayout placeholder for the TestDialogFragment
getFragmentManager().beginTransaction().add(id, newFragment, "testdialogfragment").commit();
I have breakpoints in the onAttach and onCreate methods in TestDialogFragment and they don't get hit, which goes against what I read in the android fragment API Guide. Is there something I'm doing wrong with the add transaction above?
P.S. TestDialogFragment extends Fragment

I fixed this by setting & getting arguments with Bundles instead of interacting directly with the fragment via its public methods after committing the transaction. My mistake was assuming that the fragment was up and ready to use (attached, layout inflated etc) as soon as the commit() statement was finished.

Related

Previous fragment edittext focus issue

I am using multiple fragments.
I am adding fragment over fragment as below
supportfragmentmanager
.beginTransaction()
.add(R.id.container_login, newFragment, newFragment.javaClass.simpleName)
.addToBackStack(newFragment.javaClass.simpleName)
.commitAllowingStateLoss()
Now the issue is, despite of adding new fragment, previous fragment is not losing focus.
Typing in edittext of current fragment types in previous fragment edditext.
Even action next also loses focus in current fragment and moves cursor in previous fragment.
Kindly help.
The fragment does not lose focus because you use .add method which adds the new fragment over the one which already exists in the container.
Use .replace() method which replaces the existing fragment from the container. This is similar to calling remove(Fragment) and then use .add() method.
If you want to add a fragment in one container is impossible but if you want to replace the fragment with another fragment is possible to do it, because one layout is for 1 fragment, not more than one, so you just need to replace the previous fragment with another fragment
I have a simple code if you want to replace the previous fragment with the new fragment
First, you need to add one method with the parameter of the fragment
fun openFragment(fragment: Fragment?) {
val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.container_content, fragment!!)
transaction.commit()
}
If you want to replace the fragment with another fragment just call the method like this openFragment(FragmentClass.newinstance()) soo the previous fragment will be replaced with the new fragment
I hope this code can help you to solve your problem

Why am I getting Fragment not Attached to Activity Error?

I have a thread which triggers a callback method in my Activity which has a fragment. The Activity then calls a method within the Fragment to refresh the UI with new info. In that method, getString() is called. When getstring() is called, I get a 'Fragment not Attached to Activity error'. This UI refreshing error is guarded runOnUIThread. The fragment is added like so:
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.add(fragmentContainerID, fragment, tag)
.commit();
fm.executePendingTransactions();
The activity uses two different layout files for horizontal and vertical orientation. Their are two fragment containers in both layouts. They have the same names in both horizontal and vertical layouts, but they do not match each other. The fragments are added using the above code the first time the Activity is created. On orientation changes, the fragments are automatically added into the layouts because the fragment containers have the same ids in both vertical and horizontal orientation.
When the savedInstanceState is not null, I get a reference to the fragments by using
fm.findFragmentByTag
Can anyone please tell me what I am doing wrong ? The fragments are visibly added to the Activity perfectly fine, and several minutes go by before this thread callback occurs, so I don't see how the Fragment is not "attached".
EDIT: Sigh, after some debugging Ive found that adding fragments in this way is perfectly fine. Code elsewhere in the Activity ( I registered for a callback twice) caused my fragment not attached error. If anyone reading this is getting this error, keep in mind that this issue can be triggered from elsewhere. Also, you have to setRetainInstance to true in the fragment to avoid it being recreated. Tags alone don't do that.
Fragments are added one-per-container so if you are using .add(fragmentContainerID, fragment, tag) ensure firstly that fragmentContainerID is different for each of the fragments added.
However, these sorts of issue are most often caused by a misunderstanding in how orientation changes are handled. When you change orientation and let the oncreate handle the adding: you get a new fragment of the same type what this means is that if your background task has a reference to your fragment - it has a reference to the old (now detached) fragment. What you need to do is to make sure the same fragment gets added to the tag (not a new instance). To do that just don't (re)create a fragment instance when we are only restoring state
public class MyActivity ... {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState == null) {
MyFragment frag = new MyFragment();
frag.setArguments( getIntent().getExtras() ); //if it takes params
fm.beginTransaction().add(fragmentContainerID, frag, tag).commit();
}
//after this point a call to MyFragment frag = (MyFragment) fm.findFragmentByTag(tag); should always return the correct fragment
}
It's probably also a wise idea for your task to look for the fragment right when it does its callback to update the UI (and NOT to hold on to a reference to it). Depending on how you've set up your task it just means changing from FragmentManager fm = getSupportFragmentManager(); then the task is created, to calling it as part of the onPostExecute
When change orientation, fragment automatically added to FragmentManager and forget own tag!!
Use this code to find your fragment:
FragmentManager fragmentManager= getSupportFragmentManager();
for (Fragment fragment : fragmentManager.getFragments())
{
if(fragment instanceof MyFragment)
myFragment = (MyFragment)fragment;
}
After you find the fragments on orientation change, try detaching them and then reattaching them before any operation. Or if that does not work remove them and add them. then continue..... I have even had to sometimes put a time delay of a few millisec after detach and before attach to ensure all OK.

Android how to send data to a running fragment

is there a way to send some data from an activity to a running fragment?
In my app I'm adding a second fragment over another fragment. I intentionally use the add method instead of the replace method. So now I want to hide my second fragment with
fragmentManager.popBackStack();
and my first fragment reappears. After hiding the second fragment I want to send some data from my activity to the still running frist fragment.
Any idea how to do this? It doesn't work with bundles (put extra), because I don't rebuild the fragment, I just hide the second one!
one simple solution is:
MyFragment oldFragment = (MyFragment) fragmentManager.findFragmentById(R.id.fragment_place);
fragmentManager.popBackStackImmediate();
MyFragment newFragment = (MyFragment)fragmentManager.findFragmentById(R.id.fragment_place);
newFragment.postData(...);
You can use an EventBus library like this one, it's easy to use and very convenient.
You can use tags on the Fragments when you create them to call them when needed.
getFragmentManager().beginTransaction()
.add(R.id.content, new SomeFragment(), SomeFragment.class.getSimpleName())
.commit();
So above I use the simple name of the class to tag the fragment when I create and add it to the activity.
SomeFragment fragment = (SomeFragment) getFragmentManager().findFragmentByTag(SomeFragment.class.getSimpleName());
And I can call it back when I need it and know it is being displayed like above, now I can call send it data like normal by calling a public method in the custom fragment and passing it the data as a param.

Activity destroyed by garbage collection and all fragments in backstack are showing instead of only current activity

So I have enabled the setting to destroy actvities when you navigate away from an activity
Settings=>Developer Options=>Don't Keep activites
This should basically replicate an activity or fragment getting garbaged collected and then I have to restore the data via the bundle savedinstancestate.
So I understand how that works. But it seems when I navigate from fragment 1 to fragment 2 and then put the application in the background and then in the foreground(destroying the activity)
Both fragment 1 and fragment 2 show at the same time. In which only fragment 2 should be showing.
I do not know if this is something standard that I have to manage hiding and showing fragments onsavedinstance. Or if something in my code is breaking things. Below is how I push fragments which I hope is helpful:
public void pushFragmentWithAnimation(FragmentManager fm, int parentId, Fragment currentFrag, Fragment newFrag, int animEntry, int animExit) {
hideSoftKeyboard(currentFrag.getActivity());
FragmentTransaction ft = fm.beginTransaction();
// See: http://developer.android.com/reference/android/app/FragmentTransaction.html#setCustomAnimations(int, int, int, int)
ft.setCustomAnimations(animEntry, animExit, animEntry, animExit);
ft.add(parentId, newFrag, String.format("Entry%d", fm.getBackStackEntryCount())).hide(currentFrag).show(newFrag);
ft.addToBackStack(null);
ft.commit();
}
Fragment 1 is still in the backstack because when I press back I only see fragment 1. Let me know if you know why this is happening.
The lifecycle of XML added Fragments and programmatically added Fragments differ enough to make mixing them a bad idea, as explained in detail here.
The easiest way around this is to make all fragments programmatically added by replacing your XML inflated Fragment with a FrameLayout of the same ID, then in your onCreate add
FragmentManager fragMgr = getSupportFragmentManager();
if (null == fragMgr.findFragmentByTag(FRAG_TAG))
{
fragMgr.beginTransaction().
add(R.id.fragment, new Fragment1(), FRAG_TAG).commit();
}
Where FRAG_TAG is any unique string. This ensures that Fragment1 is only created if it is not already in the layout.
I am not entirely sure why this solution works. I assume its related to if the activity gets killed that it does not keep track of which fragment is currently shown and shows all of the fragments. So basically I needed to replace:
ft.add(parentId, newFrag, String.format("Entry%d", fm.getBackStackEntryCount())).hide(currentFrag).show(newFrag);
with
ft.replace(parentId, newFrag, tag);
Then when I create the initial fragment in the main activity. I only would do that when
if(savedInstanceState==null){
My updated code is below: https://github.com/CorradoDev/FragmentTest/tree/2c53f9f42e835da768f61b0233f3ab5b3adf2448

onCreateView() isn't called immediately after FragmentTransaction.commit()

I have an activity where I dynamically replace fragments:
private void goToFragment(Fragment newFragment, String tag) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, newFragment, tag);
ft.addToBackStack(null);
ft.commit();
}
Now, I want to access the views inside the fragment so I can put data (that I have stored in my activity) into them, immediately after calling goToFragment.
The problem is, the fragment's onCreateView isn't called before the fragment is rendered completely, at least to my understanding.
I know overriding the onAttach(Activity activity) in the fragment is one way to go about it, but then I have to cast it specifically to my activity - and I just want to avoid that because I consider it bad practice for the fragment to be dependent on a specific activity.
As far as I can see, Fragment doesn't have any listeners (as a subject) implemented.
So I figure I have to make my own listener (Using the Observer Pattern to make the fragment a subject and the activity an observer), and then calling it whenever the onCreateView or onAttach is done, and then finally calling back to the fragment with the data that needs to be set. However, I need to do this for several fragments so I would have to make a listener for each fragment, which I again think is bad.
Is there any better/easier way to do this?
FragmentTransaction isn't applied instantly after calling commit(). You may force update manually:
...
mFragmentManager.executePendingTransactions();
AFAIK event callbacks' purpose is custom communication with Fragment beyond it's usual lifecycle.
The correct way to do it would be to define an interface for Activity classes wishing to display your Fragment should implement. That way, on onAttach you don't cast to a specific Activity but to your interface.
See for instance: http://developer.android.com/guide/topics/fundamentals/fragments.html#EventCallbacks
You should use onActivityCreated to set the values.
Set references in onCreateView and then set values to them in onActivityCreated.

Categories

Resources