I have a DialogPreference, that I'm trying to get a Fragment that is added to it (Google Maps, if anyone cares). The fragment has an id tag in the XML code, and I'm setting the dialog via setDialogLayoutResource(). However, I'm struggling to get a reference to the fragment (So I can set some settings to it, and get data out from it). I need to get a reference to it before the user can start playing with the data.
What I've tried so far is this:
#Override
protected void onBindDialogView(View view) {
((Fragment) getDialog()
.getOwnerActivity()
.getFragmentManager()
.findFragmentById(R.id.map));
}
This doesn't work because getDialog returns null at this point in the lifecycle. If there's a better way to reference the activity or FragmentManager, I'd love to see it. Thanks for the help!
I managed to get this to work by casting the Context as an Activity.
((Activity) getContext()).getFragmentManager().findFragmentById(R.id.map));
Related
In my application I create a fragment with the keyword new and set it by FragmentTransaction.
Upon rotation a stumbled upon a NullPointerException in the method onActivityCreated() indicating a missing injection, that I do after the call to new. I suspected the fragment was not created by my code und proved this by logging the hashCode(). It looks like a fragment is created automatically by the system upon rotation.
Where does it come from?
Is it created by the fragment manager?
How am I supposed to use it correctly?
How can I access it, to set the missing value?
For now I ignore it by testing for the null value, in which case onActivityCreated() does nothing. Instead use the fragment I create with new. However, this does not feel very satisfying, to throw away an object, that was already created.
Where does it come from? Is it created by the fragment manager?
On Activity recreation, Android will restore the fragments which already exist in activity's fragments manager
How am I supposed to use it correctly?
public void onCreate(Bundle savedInstanceState){
if(savedInstanceState == null){
//activity is created for first time
//commit the fragment
}else{
//Activity is recreated(by means of rotation or something else)
//Dont commit the fragment, fragmet will be restored by the system
}
}
How can I access it, to set the missing value?
Normally, you have to handle this inside the fragment using onSaveInstanceState method. You can get the fragment instance by using, getSupportFragmentManager.findFragmentById(R.id.container) or getSupportFragmentManager.findFragmentByTag(tagName)
I've found a lot of questions about that, but none of these can help me.
I have a "MainActivity" which have 4 fragments.
I need to access to one of these fragments, called "my_fragment", in an other simple activity, let's call "SecondActivity".
So, I try to put a property android:tag="my_fragment" in the LinearLayout markup XML of "my_fragment".
And after that, I do that in "SecondActivity":
Fragment frg = getFragmentManager().findFragmentByTag("my_fragment");
... in order to get my fragment. But frg is always null.
I try a lot of others ways, but in vain. This one seems better and easier to do, but perhaps I'm wrong.
Any help would be appreciate. Thank you in advance.
Fabien
EDIT
Since your answers that indicate that's isn't possible, I want to specify what I need.
I just want to get this fragment for reload it. I found something like that on an other subject on Stackoverflow:
frg= getFragmentManager().findFragmentByTag(my_fragment);
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(frg);
ft.attach(frg);
ft.commit();
By the way, I just try to get the fragment in the fragment himself and it's still return null. With the method :
findFragmentById(R.layout.my_fragment)
it's the same result.
And after, I did :
findFragmentById(this.getId())
But it's make an infinite looper, I don't understand the reason...
EDIT2
Alright, let’s give some details :
I have MainActivity with ViewPager with 4 fragments. It’s not fragments at the xml sense. Sorry if I’m confused, I was training to Android very recently and somethings are not clear for me again. So, before yesterday and the read of #Bruce edit, I was thinking that fragments were the components of a ViewPager. So, #Bruce, this is why I can’t use your solution. I was trying to use findFragmentByTag with the tag applicate on my principal LinearLayout markup of my fragment - that is not, I repeat, an xml markup fragment.
This is my approach :
In my MainActivity, I click on the third fragment. I make a research for find some points around me. After an action of the user, still from the third fragment, I open the SecondActivity for authentification and on the user connection, I close this SecondActivity. Now, I need to reload the fourth fragment that will adapt his components in terms of the user situation, while keeping the same state on the third fragment, with points loaded. It’s why can’t use your solution #menion.asamm : I can’t reinstantiate the MainActivity, even if I simulate a click on the third fragment because it will come back in his initial state, without points loaded.
Thank you both of you #Bruce and #menion.asamm for your time in helping me !
Fragments are always owned by one activity, so you cannot directly access a different activity's fragments. The call you are making is looking for fragments within your SecondActivity.
Why do you want to do this? Once some UI is off screen (MainActivity), you usually don't want to do anything with those UI objects, because Android may have removed them from memory. If there is data in "my_fragment" that is needed by SecondActivity, one approach might be to save the data in SharedPreferences or a database in my_fragment, and then load it in SecondActivity.
EDIT
I'm not sure you're getting that it is important which activity you are running in. Here are two options for how to proceed:
If you just want to run the SAME instance of your fragment that was already running inside MainActivity, then maybe what you want to do is finish your SecondActivity to return to MainActivity.
If you want a NEW copy of the same fragment inside SecondActivity, then you can include the fragment inside SecondActivity's layout (or add it to some container later).
Also, notice that for your call to findFragmentById, the ID needs to be the ID that was specified in the layout file as the value of android:id (not the R.layout.my_fragment). It might be better to use a fragment tag, which you can either specify in your layout file or when you add the fragment.
Mainly I think you need to read Google's guide on fragments.
EDIT2:
Ah, I see, I have a similar fragment-refresh situation in my app. You basically need to get data from SecondActivity back to the fragment inside MainActivity. The approach I use is this:
Save the data from SecondActivity in storage (DB or SharedPreferences).
Finish SecondActivity so that MainActivity and your fragment are shown again.
Override onResume in your fragment to fetch the data you saved in SecondActivity.
Another option is to launch SecondActivity using startActivityForResult, and then process the results in MainActivity, passing them to its fragment.
Regarding how to find the fragment by tag, you first need to set the fragment's tag. If you are declaring your fragment in a layout XML, then you can do it there (and you can also declare
<fragment class="com.xyz.MyFragment"
android:tag="MyFragment"
android:id="#+id/my_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Now from inside MainActivity you can either do findFragmentById(R.id.my_fragment) or findFragmentByTag("MyFragment").
If you are NOT declaring the fragment in XML, but adding it directly, you can set the fragment's tag as part of the add call:
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frag_parent, new MyFragment(), tag);
EDIT3: Ah, you're using ViewPager to hold fragments. Now I understand better. They're still fragments, but getting access to them is indeed tricky, because Android constructs a fragment tag in some internal code. Here is another SO question on this issue:
Retrieve a Fragment from a ViewPager
Hmm if you really need just refresh of fragment attached to different activity, I suggest:
first activity start second activity with
startActivityForResult(intent, MY_CODE);
second activity when wants to refresh fragment in first activity, finish it's state with
Intent data = new Intent();
data.putExtra("REFRESH_FRAGMENT", true);
setResult(RESULT_OK, data);
finish();
back in first activity, you may catch this result by
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check request code
if (requestCode == MY_CODE) {
// check result
if (resultCode == RESULT_OK) {
// check data
if (data != null && data.getBooleanExtra("REFRESH_FRAGMENT", false)) {
refreshFragment();
}
}
}
}
Possible?
I currently have the following situation:
1) The "main view" which contains the EditText I'm trying to update. (Let's call it mainView)
2) A fragment that is opened whenever I click in a button that is contained in the "main view", the
fragment receives mainView as parameter.
3) An OnClickListener which is set to a button that is contained by the fragment. This listener receives the fragment as parameter.
Basically what I need to do is, each time I click on the button that triggers the listener, I need to update the editText, however it doesn't seem to be working. I believe it has something to do with "notifying" the view, but I haven't been able to get it working no matter what I try. After I update the text I close the fragment and
Basically the code is the following:
public void onClick(View v){
String newMessageContent = "hello world";
fragment1.mainView.editText1.setText(newMessageContent);
FragmentManager manager = this.fragment1.getActivity().getFragmentManager();
manager.beginTransaction().replace(R.id.container,this.fragment1.mainView.getPlaceHolderFragment()).commit();
}
Please note that I have simplified the problem a little bit and changed the name of the fragment/views in order for you guys to understand better. The text "hello world" is actually dynamic, and depends no another parameter that is received by the OnClickListener.
After I click the fragment does get replaced, so I know the onClickListener is working correctly, however I believe there's something wrong with the way the data change is being notified.
I've already looked at many SO questions, however none of them have helped me to achieve what I need.
Any help is appreciated, thanks.
I suggest implementing an interface, say, IUpdateFromFragment with method, say, onUpdate(String message), then let activity implement that interface and inside the fragment just call something like ((IUpdateFromFragment)this.getActivity()).onUpdate(newMessageContent);
I realized the problem was that each time I replaced the fargment via the fragmentManager, the method setActivityView was being called again, which replaced the EditText content.
In order to avoid this, I manually removed the fragment (instead of replacing it), doing the following:
FragmentManager manager = this.selectTemplateFragment.getActivity().getFragmentManager();
manager.beginTransaction().remove(this.selectTemplateFragment).commit();
Update the fragment via transaction, then within the fragment1 class OnViewCreated, you can do mainView.editText1.setText("whatever");
The way you're doing this now, I'm surprised isn't throwing an exception since the view isn't inflated yet.
In my application, I use an Activity which holds one Fragment with FragmentTabHost and hence all its tabs are nested Fragments.
Inside an Activity which holds a Fragment with its nested Fragment, we may get a reference to attached one using onAttachedFragment().
But how to get a reference to nested Fragment from FragmentTabHost?
Well, exploring the source code of FragmentTabHost I've found that when it adds a fragment tab, it assignes a tag of TabSpec to nested Fragment.
So to get the reference to this Fragment we should call
getChildFragmentManager().findFragmentByTag(tabSpecTag)
I was trying this for a while, but I was getting null returned from the FragmentManager because I was trying to access the manager in onCreateView() immediately after adding.
Here is a good explanation on what happened
It's also important to note that Fragment tabs that have not yet been selected don't exist yet in the FragmentManager, and so will return null as well. I got around this by calling mTabHost.setCurrentTab(index) before trying get to the Fragment with the FragmentManager. It's not very clean, but it works.
Above solutions are also working but I have one more easy solution,
#Override
public void onTabChanged(final String tabId) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mFragment = getChildFragmentManager().findFragmentByTag("Tagname");
}
},1000);
}
Here you have to implement FragmentTabHost.onTabChangeListener
We have kept a second delay in fetching fragment from the childFragmentManager.
Note : You need to cast mFragment which fragment you have used.
I found a solution that I like a little better because it doesn't involving executing code with a delay (which is always iffy given android hardware fragmentation and different processor speeds).
In your onTabChanged() method, before you try to find the fragment, call executePendingTransactions() on the fragment manager associated with your tabHost. It seems there are some places in the FragmentTabHost source code where they should be calling executePendingTransactions() but fail to do so.
This works every time the tab changes with one exception... the first tab that is selected still comes back null... In my specific case, I was able to handle this exception differently anyway, by putting some code in onResume.
Hope this helps.
I have an application where whenever I exit the application via the home hardware button, it should return to the last state the application is in. However, when I launch the application again, the application shows a white screen with only my header bar. And when I click on the header bar's button, the application crashes with the IllegalStateException where the application cannot find the method for the button clicked.
I am currently implementing with Sherlocks Fragment, where the header bar is an action bar. I'm also using HTC Rhyme, Version 2.3 (Gingerbread). The following is the codes for the addition of fragments into my main app.
Codes to add the fragments within the onCreate method in the activity:
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
Bundle bMain = getIntent().getExtras();
String statusCheck = "";
if (bMain != null) {
statusCheck = bMain.getString("statusCheck");
}
if (statusCheck.equals("web")) {
MyWebViewFragment webfrag = new MyWebViewFragment();
trans.add(R.id.container,webfrag, "WebViewFragment");
} else if(statusCheck.equals("traveloguelist")) {
MyTravelogueListFragment travelfrag = new MyTravelogueListFragment();
trans.add(R.id.container,travelfrag, "TravelogueListFragment");
}
trans.commit();
This is the codes when I change a fragment:
MyTravelogueListFragment travelfrag = new MyTravelogueListFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.container, travelfrag).addToBackStack(null).commit();
[Edit]
I realized after much reading and running that the main issuei have is that upon resuming the application, the activity is actually created again. Thus, some of the parameters i passed in does not get registered, resulting in the wrong display. I THINK this is the error that is causing that to happen:
Previously focused view reported id "myresID" during save, but can't be found during restore.
However, I don't know how you force the application to remember the previous state of the fragment? Or is there any other way around this problem?
I'm still very stuck with this problem. Will really appreciate it if someone can help me!
After much trial and error and many readings, I finally found a way to sort of solve my problem.
From what I understand, this problem will occur due to the Activity's life cycle. The comment by Tseng in this forum was quite comprehensive:
http://forum.unity3d.com/threads/127794-Android-Apps-crashing-on-resume
It seems that during the time when other applications are invoked when a certain activity is onPause/onStop, Android might free up some of its memory the activity is currently holding on to if there is insufficient memory required. In this case, all the current objects or variable the paused activity is having will be destroyed. Thus, when the activity is back on focus, the onCreate is actually invoked again. Thus, the activity will have no idea which fragment I am currently require.
However, I realized that it will always call the saveInstanceState which is essentially a bundle object. So I did the following:
onSaveInstanceState method
#Override
public void onSaveInstanceState(Bundle bundle) {
//activityFrag is a string object that tells me which fragment i am in currently
bundle.putString("statusCheck", activityFrag);
}
onCreate method
if (savedInstanceState != null) {
getSupportFragmentManager().popBackStack(null, getSupportFragmentManager().POP_BACK_STACK_INCLUSIVE);
//return;
statusCheck = savedInstanceState.getString("statusCheck");
} else {
statusCheck = b.getString("statusCheck");
}
What I have done is to remove all the fragments I have stacked thus far to remove any issues where there is missing information needed. So this is like starting anew again. The status check just determine which fragment the user has last visited.
After much testing, it seems like it does solve my problem. though I wouldn't say it is perfect. One of the main downfall I have is that whenever I change my fragment, I have to update and change my statusCheck to make sure the correct fragment will be called. However, I have to admit this way is a little unorthodox and might not be very correct.
If any of you have any better ideas, please feel free to share!
You can try to implement following:
Use putFragment to save all fragments, currently located in FragmentManager, into bundle in onSaveInstanceState;
And then you can use getFragment to get all previously stored fragments back from bundle in onRestoreInstanceState.
Also... you'll probably need some HashMap that will help to determine the hierarchy of the fragments (in case you have containers and contained fragments) to be saved into bundle as well.
Also... when restoring from bundle you'll need to know keys for all fragment you've put there earlier. Probably, the easiest way is simply to organize an array of keys and put them into bundle when saving the fragment into instance.
This way your saving and restoring will be complete and centralized.