PreferenceFragment doesn't get onActivityResult call from in app billing request - android

I have a preference screen that presents the user with a checkbox to disable ads. When the user clicks this for the first time, they are presented with an In App Billing purchase option to disable the ads.
The issue I'm facing here is that I can't see any way to get the onActivityResult callback into the fragment.
So I have a PreferenceActivity loading a PreferenceFragment (of which I can't seem to get a reference). In App Billing requires a call to startIntentSenderForResult, which Fragments don't have, only Activities.
When I launch the purchase flow with startIntentSenderForResult, the Activity's onActivityResult gets called, but I need this in the fragment.
Because I loaded the PreferenceFragment into the PreferenceActivity with the following, I don't think I can get a reference to the Fragment to pass the call down.
#Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.layout.preferences_headers, target);
}
#Override
public Intent getIntent() {
final Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SyncPreferencesFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
What am I missing here? I dont' want to split up all of my purchase logic, so how do I get my Fragment to get the onActivityForResult call?

The original start request must come from the Fragment in order for Android to deliver the result back to the same Fragment. If the Activity starts the request, the result doesn't inherently get handed to every Fragment that may be attached to the manager. In addition to this, you have to ensure that if onActivityResult() is overridden in the top-level Activity, that you call super.onActivityResult() as well, or the message won't get delivered to the Fragment.
The problem with IAB is your given a PendingIntent instead of a standard Intent to fire, and there is no method on Fragment to trigger the initial action even if you can move your code there. To keep things as they are, you may have to do a bit of a swizzle. One thing you could do is make use of a custom interface inside the onAttach() method of your Fragment and use it to allow the Fragment to hand itself up to the Activity. Something like:
public class SyncPreferencesActivity extends PreferenceActivity
implements SyncPreferencesFragment.OnAttachCallback {
SyncPreferencesFragment mTargetFragment;
#Override
public void onAttachSyncFragment(SyncPreferencesFragment fragment) {
mTargetFragment = fragment;
}
}
...with some additions to the corresponding Fragment...
public class SyncPreferencesFragment extends PreferenceFragment {
public interface OnAttachCallback {
public void onAttachSyncFragment(SyncPreferencesFragment fragment);
}
#Override
public void onAttach(Activity activity) {
try {
OnAttachCallback callback = (OnAttachCallback) activity;
callback.onAttachSyncFragment(this);
} catch (ClassCastException e) {
throw new ClassCastException("You Forgot to Implement the Callback!");
}
}
}
With something like this, at least you have a reference to the instance so you can forward the result or anything else that might be necessary. If you want to be super clean, you could also implement a matching "detach" that clears the reference in the Activity.

Here is a simpler way that I am using to get a reference to a Preference Fragment from a Preference Activity:
private WeakReference<GeneralPreferenceFragment> mGeneralFragment;
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
// "id" might be arbitrary and "tag" can be null
Log.d(TAG, "onAttachFragment: "+fragment.getId()+","+fragment.getTag());
// however class can be used to identify different fragments
Log.d(TAG, "onAttachFragment: "+fragment.getClass()+","+(fragment instanceof GeneralPreferenceFragment));
if(fragment instanceof GeneralPreferenceFragment) {
mGeneralFragment=new WeakReference<>((GeneralPreferenceFragment)fragment);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Fragment f=mGeneralFragment.get();
if(f!=null) {
// can now use findPreference, etc
}
}
This method is convenient because it doesn't require the use of interfaces or modifications to the fragment classes.

Related

Android: Callback to fragment from DialogFragment?

I know to pass back something from a fragment to its calling activity you can use onAttach which has the "activity" parameter. You can set the activity to variable and call an interface on it later. So passing data from the fragment back to the activity. All great.
I would like to do the same thing but this time i have a standard fragment and I want to call a DialogFragment and then have the DialogFragment call back to the original fragment but I can't use onAttach is wants a Activity.
Anyone know the best way of doing this ?
Thanks
Obviously you could just make things public in your activity and set them from your fragment. But then you have to keep references to your activity, and possibly have unwanted public variables and/or setters.
You could use EventBus and you would not need any of that.
In your activity you need to register an event
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
// This method will be called when a MessageEvent is posted
public void onEvent(MessageEvent event){
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
Then you can simply call your event from your fragment or anywhere you like,
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
Some more information on EventBus can be found here
And another possibly useful tutorial.
One way to contact another fragment from there is to access it in the implemented method in your Activity:
//In your Activity...
#Override
public void callbackFromFragmentA(){
FragmentB fragment = (FragmentB) getFragmentManager.findFragmentById(android.R.id.content);
if (fragment != null) {
fragment.callFragmentMethod();
}
}

Android managing fragments from activity elegantly

Description of what I'm trying to accomplish:
I have an app that uses a FragmentActivity with a LinearLayout as a container for the fragments. I click different buttons on the FragmentActivity UI and add and remove Fragments to the container in the FragmentActivity. In addition to clicking buttons on the FragmentActivity UI, each Fragment also has buttons that can be clicked which will remove the current fragment and add a different fragment in its place.
The Android way of doing things as I understand it:
I have been reading up on how to do this and as I understand it, the 'proper' way of doing things is to use the FragmentActivity as sort of a relay station and have each fragment do callbacks to the FragmentActivity to communicate events and deal with them.
Scenario:
So let's say that the FragmentActivity is displaying Fragment A and when the user clicks a button in FragmentA I want to stop showing FragmentA and start showing FragmentB. To do this I have created an interface in FragmentA called AListener. In the onAttach() method of FragmentA I use the suggested method of checking that the FragmentActivity implements AListener. When the button in FragmentA is clicked I use one of the callback methods from AListener to communicate the click event to the FragmentActivity. In the FragmentActivity I create an instance of FragmentB and add it to the container in FragmentActivity. Then if some event happens in FragmentB I use the same scheme to communicate the event to the FragmentActivity and do something interesting.
So what's the problem?
For my application I have found this scheme of having Fragments call back to the FragmentActivity and then having the FragmentActivity create a new fragment or call forward to and existing fragment very cumbersome. I have many fragments that need to be displayed by the FragmentActivity and therefore I am implementing an interface for every type of fragment that needs to be displayed (Each fragment is different so they each have their own interface). This causes clashes when I have two interfaces that have the same method signatures and I'm forced to rename one of the methods.
For instance, if I want to attach a listener to a fragment using the onAttach() method of the fragment, then my FragmentActivity must implement the interface. I have found several instances where I have callback methods that have the same name (or I'm forced to name them something similar but different because of a namespace collision). One solution to this would be to use an anonymous classes as callbacks instead of having the FragmentActivity implement the interface. This seems to work well enough, but goes against what the Android documentation says about using the onAttach() method to set the listener.
Are there any elegant ways to approach this problem? It seems to me the tradeoff is that you either force the FragmentActivity to implement an interface for each Fragment that you want to display in it and have the fun problem of watching out for method signature collisions, or you go against the Android documentation and use Anonymous classes to handle the callbacks (not sure of the implications of this).
I am fairly new to Java and feel like I could be missing a concept here that would solve my problem. Can anyone set me straight on how to solve this problem elegantly?
I completely understand your problem since i was dealing it for a long time. Here is the solution i came up right now! It may need some modification based on your need but i it works well.
first of all to to make communicating of event easier in your app use an EventBus! here is the most famous one https://goo.gl/nAEW6
event bus allows you to send event from anywhere to anywhere without need to worry about implementing interfaces, broadcast receivers, threading, etc.
Then add FragmentOrganizer to your app. It's a base class for all of your Fragment Organizers. basically you need one for each activity. Here is the code
public abstract class FragmentOrganizer {
protected FragmentManager fragmentManager;
public FragmentOrganizer(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
openFragment(getInitialFragment());
EventBus.getDefault().register(this);
}
protected abstract Fragment getInitialFragment();
protected abstract void onEvent(Object event);
public abstract boolean handleBackNavigation();
public void freeUpResources(){
EventBus.getDefault().unregister(this);
}
protected Fragment getOpenFragment(){
String tag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1).getName();
return fragmentManager.findFragmentByTag(tag);
}
protected boolean isFragmentOpen(Fragment fragment){
return isFragmentOpen(fragment, true);
}
protected boolean isFragmentOpen(Fragment fragment, boolean useArgs){
String fragmentTag = createFragmentTag(fragment, useArgs);
if (fragmentManager.getBackStackEntryCount() != 0) {
String name = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
if(!useArgs)
name = name.substring(0, name.indexOf("-"));
return name.equals(fragmentTag);
}
return false;
}
private String createFragmentTag(Fragment fragment, boolean addArgs) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(fragment.getClass().getSimpleName());
if(addArgs) {
stringBuilder.append("-");
if (fragment.getArguments() != null)
stringBuilder.append(fragment.getArguments().toString());
}
return stringBuilder.toString();
}
public void openFragment(Fragment fragment) {
if(isFragmentOpen(fragment))
return;
String fragmentTag = createFragmentTag(fragment, true);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.activity_main_fragment_container, fragment, fragmentTag);
transaction.addToBackStack(fragmentTag).commit();
}
}
Now you need to create your fragment organizer that inherit from FragmentOrganizer and implements 3 required methods. here the sample
public class MainFragmentOrganizer extends FragmentOrganizer {
public MainFragmentOrganizer(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
protected Fragment getInitialFragment() {
return HomeFragment.newInstance();
}
#Override
public void onEvent(Object event){
if(event instanceof ClickedOnPhotoEvent){
String photoCode = ((ClickedOnPhotoEvent) event).photoCode;
openFragment(PhotoFragment.newInstance(photoCode));
}
}
#Override
public boolean handleBackNavigation(){
Fragment fragment = getOpenFragment();
if (fragment instanceof HomeFragment){
return false;
} else {
fragmentManager.popBackStack();
return true;
}
}
}
And in your activity you just need to insatiate your FragmentManager and let it do the magic!
fragmentManager = getSupportFragmentManager();
fragmentOrganizer = new MainFragmentOrganizer(getSupportFragmentManager());
#Override
public void onBackPressed() {
//first let fragment organizer handle back. If it does not activity takes cares of it!
if(!fragmentOrganizer.handleBackNavigation()){
finish();
}
}
#Override
protected void onDestroy() {
fragmentOrganizer.freeUpResources();
super.onDestroy();
}
It may seem a lot of code but as you see most of the code encapsulated in FragmentOrganizer base class and it does all the general works so you just have to copy this file from one project to another.
As i said in the beginning i just came up with this solution right now, so it may not be perfect. I Plan to use this in my next project i hope you do to. And if you do i really appritiate if you share your though. have a good time
A co-worker of mine came up with what I consider an elegant solution to this problem.
Remember, what we're trying to achieve is a way for fragments to callback to the parent activity without having the activity implement the interface. Also, we need to be able to automatically set the listener again if the activity is destroyed and then recreated.
Activities have a lifecycle callback called onAttachFragment(Fragment fragment) which is called whenever a fragment is being attached to the activity. So, for instance, when a new fragment is created within the activity, this gets called. It also gets called if an activity that was previously destroyed gets recreated. What you can do is use an interface or an anonymous class to set a listener on the new fragment in onAttachFragment like this:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
//Determine which fragment this is by checking its tag
if(fragment.getTag().contains(TextFrag.FRAG_TAG)){
//set a listener on this fragment using an anonymous class or interface
((TextFrag)fragment).setListener(new TextFragButtonListener() {
#Override
public void onButtonClicked() {
count++;
counterTV.setText(String.valueOf(count));
}
});
}
}
Using this technique we are able to avoid the activity having to implement an interface for the callback and thus we avoid any naming conflicts with our callback methods. Also, if the activity is destroyed, once it is recreated the listener will be automatically reset so our callbacks will still work.
There are probably many other ways to do this and I'd love to here anyone's criticisms of this technique and suggestions for any other techniques.

Handling onNewIntent in Fragment

I am writing an application that uses NFC to read some data stored on it. My application uses Fragments and Fragment don't come with onNewIntent() method. Since, the data I am reading is done with my separate class which handles NFC related operation, the only thing I need to do is update the TextView inside the Fragment. However this implementation can also be used to pass new Intent to the Fragment.
Here is my current implementation which makes use of an interface. I am calling the listener after new Intent is received and NFC related checks succeeds. This is the FragmentActivity which hosts Fragment.
public class Main extends FragmentActivity implements
ActionBar.OnNavigationListener {
private Bundle myBalanceBundle;
private NFC nfcObj;
private NewBalanceListener newBlanceListener;
#Override
public void onNewIntent(Intent intent) {
setIntent(intent);
}
#Override
protected void onResume() {
getNFCState();
super.onResume();
}
private void getNFCState() {
//Other NFC related codes
else if (nfc_state == NFC.NFC_STATE_ENABLED){
readNFCTag();
}
}
private void readNFCTag() {
//Other NFC related codes
if (getIntent().getAction().equals(NfcAdapter.ACTION_TECH_DISCOVERED)) {
nfcObj.setTag((Tag) getIntent().getParcelableExtra(
NfcAdapter.EXTRA_TAG));
nfcObj.readQuickBalance();
transitQuickReadFragment(nfcObj.getCurrentBalance());
}
}
private void transitQuickReadFragment(String balance) {
// Creates a balance bundle and calls to select MyBalance Fragment if it
// is not visible. Calls listener is it is already visible.
if (actionBar.getSelectedNavigationIndex() != 1) {
if (myBalanceBundle == null)
myBalanceBundle = new Bundle();
myBalanceBundle.putString(Keys.BALANCE.toString(), balance);
actionBar.setSelectedNavigationItem(1);
} else {
newBlanceListener.onNewBalanceRead(balance);
}
}
#Override
public boolean onNavigationItemSelected(int position, long id) {
// Other fragment related codes
fragment = new MyBalance();
fragment.setArguments(myBalanceBundle);
newBlanceListener = (NewBalanceListener) fragment;
// Other fragment related codes
}
// Interface callbacks. You can pass new Intent here if your application
// requires it.
public interface NewBalanceListener {
public void onNewBalanceRead(String newBalance);
}
}
This is MyBalance Fragment which has TextView that needs to be updated whenever NFC is read:
public class MyBalance extends Fragment implements NewBalanceListener {
private TextView mybalance_value;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//Other onCreateView related code
Bundle bundle = this.getArguments();
if (bundle != null)
mybalance_value.setText(bundle.getString(Keys.BALANCE.toString(),
"0.00"));
else
mybalance_value.setText("0.00");
//Other onCreateView related code
}
#Override
public void onNewBalanceRead(String newBalance) {
mybalance_value.setText(newBalance);
}
}
This code works perfectly like expected for my application but, I want to know if there is better way to handle new Intent from Fragments?
This is an old question, but let me answer it in case anybody bumps into it.
First of all you have a bug in your code:
You can't register Fragments as listeners inside Activity the way you do it. The reason is that Activity and Fragments can be destroyed by the system and re-created later from saved state (see documentation on Recreating an Activity). When this happens, new instances of both the Activity and the Fragment will be created, but the code that sets the Fragment as a listener will not run, therefore onNewBalanceRead() will never be called. This is very common bug in Android applications.
In order to communicate events from Activity to Fragment I see at least two possible approaches:
Interface based:
There is an officially recommended approach for communication between Fragments. This approach is similar to what you do now in that it uses callback interfaces implemented by either Fragment or Activity, but its drawback is a tight coupling and lots of ugly code.
Event bus based:
The better approach (IMHO) is to make use of event bus - "master component" (Activity in your case) posts "update" events to event bus, whereas "slave component" (Fragment in your case) registers itself to event bus in onStart() (unregisters in onStop()) in order to receive these events. This is a cleaner approach which doesn't add any coupling between communicating components.
All my projects use Green Robot's EventBus, and I can't recommend it highly enough.
There is at least one alternative: From Activity.onNewIntent documentation:
An activity will always be paused before receiving a new intent, so you can count on onResume() being called after this method.
Note that getIntent() still returns the original Intent. You can use setIntent(Intent) to update it to this new Intent.
FragmentActivity.onNewIntent documentation is different but I don't think it contradicts the above statements. I also make the assumption that Fragment.onResume will be called after FragmentActivity.onResume, even though the documentation seems a little fussy to me, though my tests confirm this assumption. Based on this I updated the Intent in the activity like so (examples in Kotlin)
override fun onNewIntent(intent: Intent?) {
setIntent(intent)
super.onNewIntent(intent)
}
And in Fragment.onResume I could handle the new intent like so
override fun onResume() {
super.onResume()
doStuff(activity.intent)
}
This way the activity don't need to know about what fragments it holds.
No, there is no better way. Fragments can live longer than Activities and are not necessarily tied to them at all so providing new intents would not make sense.
Btw, you have a few bugs in your code :)
if (actionBar.getSelectedNavigationIndex() != 1) {
Magic numbers are bad! use a constant.
if (myBalanceBundle == null)
myBalanceBundle = new Bundle();
myBalanceBundle.putString(Keys.BALANCE.toString(), balance);
actionBar.setSelectedNavigationItem(1);
we already know that the navigationitem is set to 1
} else {
newBlanceListener.onNewBalanceRead(balance);
Add a null check. The user might have never selected a navigation item.

How to pass result from second fragment to first fragment

In my app I have two fragments say fragmentA and FragmentB. When I click on a button in fragmetA, a list is opened in fragmentB. Now when I select an item from list in fragmentB I want the result to be passed to fragmentA. I am using only one TabActivity for all fragments. When list item is selected in fragmentB I am popping out fragmentB from stack so that I can directly go back to fragmentA.
Does anyone knows how to pass result to previous fragment.
Thanks.
Update
Activity is the parent controller and should take responsibility for handling those events raised by its fragments/views, which concern something outside of the scope of fragment/view itself.
A Fragment is to act as a sub-controller of Views it hosts. All the events and communication between its own views, the fragment should handle itself. When there is an event outside of a fragment's scope and responsibilities (like sending data to another fragment), that event should be escalated to its parent controller, the Activity.
Old
From this tutorial : http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
Its better to let the activity apply changes to its fragment than passing values directly between fragments. Let your Activity implement a FragmentListener interface with onQuery(Bundle data) and onResult(Bundle data) methods.
Create a FragmentListener varaible in each of your fragments and then override onAttach() of each fragment as:
public void onAttach(Activity activity) {
super.onAttach(activity);
//---register parent activity for events---
try{
fragmentListener = (FragmentListener) activity;
}catch (ClassCastException e)
{
throw new ClassCastException("Parent activity must implement interface FragmentListener.");
}
}
This will enforce your child fragments to be automatically registered to parent Activity.
Also, remember to release fragmentListener reference in onDetach().
Now you can call your Activity from fragments.
On the other side, your Activity can always search for a fragment using getFragmentManager().findFragmentByTag("fragmentA") or findFragmentById("FragmentA").
If it can find your Fragment, Then it can cast it into your FragmentA class and call its methods. Same can be done with FragmentB or any other fragment..
One of the possible solutions:
public class DetachableResultReceiver extends ResultReceiver {
private Receiver mReceiver;
public DetachableResultReceiver(Handler handler) {
super(handler);
}
public void clearReceiver() {
mReceiver = null;
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
public void onReceiveResult(int resultCode, Bundle resultData);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
First fragment implements DetachableResultReceiver.Receiver and creates DetachableResultReceiver object:
mReceiver = new DetachableResultReceiver(new Handler());
mReceiver.setReceiver(this);
Then you can pass it to second fragment arguments:
Bundle bundle = new Bundle();
bundle.putParcelable(Consts.EXTRA_RECEIVER, receiver);
fragment.setArguments(bundle);
And use it in second fragment:
receiver = getArguments().getParcelable(Consts.EXTRA_RECEIVER);
receiver.send(Consts.SOME_MESSAGE, someData);
In fragmentB.java set an OnClickListener to perform a method in the main class. Pass an arguement in fragmentB.java to the main class that is the variable, and handle the rest of it in your main class. Though fragments shouldn't really be dependent on activities at all. Fragments were made to plug and play anywhere.
This Example Shows EditTextListener:
myAwesomeActivity.java
fragmentA.java
fragmentB.java
fragmentB.java:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
int x = 3;
EditText ed1 = (EditText) getView().findViewById(R.id.editText1);
ed1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEvent.ACTION_UP == event.getAction()) {
((myAwesomeActivity) getActivity()).myMethod(x);
}
return false;
}
});
}
myAwesomeActivity.java:
publiv void myMethod (int x){
//Do whatever you want with variable
}
All you have to do is implement the correct type of listener, but the main point is shown. In one fragment activity, call a method and pass a variable to the main activity. From the main activity you can send it to your other fragment activity if you'd like.
You can also use SharedPreferences to save some string and after return back to the first fragment load it and clear.

Refreshing my fragment not working like I thought it should

I have this nice method in my ListFragment I call to fill out the details of my other fragment:
private void showClientDetails(int pos) {
myCursor.moveToPosition(pos);
int clientId = myCursor.getInt(0);
if(mIsTablet) {
// Set the list item as checked
getListView().setItemChecked(mCurrentSelectedItemIndex, true);
// Get the fragment instance
ClientDetails details = (ClientDetails) getFragmentManager().findFragmentById(R.id.client_details);
// Is the current visible recipe the same as the clicked? If so, there is no need to update
if (details == null || details.getClientIndex() != mCurrentSelectedItemIndex) {
// Make new fragment instance to show the recipe
details = ClientDetails.newInstance(mCurrentSelectedItemIndex, clientId, mIsTablet);
// Replace the old fragment with the new one
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.client_details, details);
// Use a fade animation. This makes it clear that this is not a new "layer"
// above the current, but a replacement
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
}
}
It is called when the user clicks on a client in the ListFragment view:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCurrentSelectedItemIndex = position;
showClientDetails(position);
}
This works great, but then another FragmentActivity can change the data that this displays so I thought this would work:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
//update the client list incase a new one is added or the name changes
if(requestCode==1899)
{
myCursor.requery();
theClients.notifyDataSetChanged();
showClientDetails(mCurrentSelectedItemIndex); //now if client was edited update their details in the details fragment
}
}
Now I know the line:
if (details == null || details.getClientIndex() != mCurrentSelectedItemIndex) {
Prevents the block of code being reached when its called in my onActivityResult. So if I remove that if statement, then things freak out and the ft.commit() has a hissy fit and gives me the error:
`07-08 16:53:31.783: ERROR/AndroidRuntime(2048): Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
So I guess what I am trying to do isn't as cut and dry as it sounds, it makes no sense to me since I can do that onListItemClicked all day and the fragment displays the details of the newly clicked client constantly very well...
I even tried this in my onActivityResult:
//simulate a click event really fast to refresh the client details
showClientDetails(0);
showClientDetails(mCurrentSelectedItemIndex);
that does nothing, is it im trying to call something from the onActivityResult which isn't a Ui thread or what not?
I do have this in my code of the ListFragment as well
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("currentListIndex", mCurrentSelectedItemIndex);
}
Is this what the error is complaining about?
The other FragmentActivity's action is performing a task which requires the Fragment to save its state via a call to onSaveInstanceState in preparation for a new instance being reconstructed. I've seen this for example when I was firing off an activity from a fragment which filled the entire screen as this resulted in the view being detached from the fragment, state needing to be saved etc.
You basically cannot call commit between onSaveInstanceState and the new instance of the fragment being recreated. See commit.
As for the solution, then either re-think to try and avoid the commit being called when it is or alternatively call commitAllowingStateLoss if you think it's OK for the UI to change unexpectedly on the user.
I think the answer is to not do any fragment transactions in the onActivityResult. I believe what happens is that when the onActivityResult is called the activity it is in hasn't yet resumed and restarted its fragments.
Use a handler to post back the function call to the activity.
handler.post(new Runnable() {
#Override
public void run() {
showClientDetails(mCurrentSelectedItemIndex);
}
});
you can do an other thing which no very good but it works.
finish (you activity)
do an intent of that same class by :
Intent mIntent = new Intent(getApplicationContext(), YouClass.class);
startActivity(mIntent);

Categories

Resources