I'm facing a strange problem inside my app. From a fragment, if i push a button, i start new FragmentActivity that contains a fragment and some other elements, but if i would go back to previous Activity (that contain fragment that start the current activity), i need to push back button twice.
First time fragmentActivity seem close itself, but it reopens again. I close FragmentActivity as always:
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
so what's wrong?
i start new FragmentActivity that contains a fragment and some other elements
You're most likely adding that FragmentTransaction to the backstack, which is why it's consuming the back button press. You shouldn't need to override onBackPressed() at all. Instead, look at the code where you add the Fragment to the new FragmentActivity and ensure you're not calling .addToBackStack() on the initial transaction. For example, typically in onCreate() you would do something like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.your_container_id, new YourInitialFragment())
.commit();
}
}
Related
My code:
public class MainActivity extends AppCompatActivity {
private FragmentA fragmentA;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
fragmentA = FragmentA.newInstance();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_a_container, fragmentA, "FRAGMENT_A");
fragmentTransaction.commit();
}
else {
fragmentA = (FragmentA) getSupportFragmentManager().findFragmentByTag("FRAGMENT_A");
}
}
}
I don't really know what I am doing but this is currently what I do. I define a container for the Fragment and then I use a FragmentTransaction to replace it with a Fragment. The part I am confused about though is the else statement.
Should I be structuring this differently?
I thought configuration changes wiped out Activities and Fragments so why check for the Fragment in some support manager? Does this mean Fragments don't actually get destroyed? At the same time, they DO seem to get destroyed because they appear to reset unless I use onSaveInstanceState or the getArguments() approach.
Edit: What's wrong with doing this:
public class MainActivity extends AppCompatActivity {
private FragmentA fragmentA;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentA = FragmentA.newInstance();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_a_container, fragmentA, "FRAGMENT_A");
fragmentTransaction.commit();
}
}
They do get destroyed and recreated for you on configuration changes by the, in this case, SupportFragmentManager.
To answer your questions:
Should I be structuring this differently?
No, that's exactly how you should create fragments if there is no saved state and retrieve them when there is. See also my answer here;
a) so why check for the Fragment in some support manager?
Because the manager handles the lifecyle of the fragment for you when there is a configuration change.
b) Does this mean Fragments don't actually get destroyed?
No, it does get destroyed. See this diagram for a reference.
Edit to answer some of your questions from the comments:
But any member variables inside that Fragment are completely lost on configuration change unless I save them in that Fragment's onSaveInstanceState, right?
That is correct. Because your fragment is being destroyed, everything not being saved on onSaveInstanceState gets lost.
So then what exactly am I restoring?
You are not restoring anything. You are only retrieving the reference to the fragment that was previously created. You restore your variables on the onRestoreInstanceState() method of your fragment.
What's wrong with doing this (the code from the edit in the question)?
If you do that, you are adding a new fragment instance to the R.id.fragment_a_container container. So the old fragment will get lost together with the state of it you saved on onSaveInstanceState(). It will be a new fragment, with new information in it and the event onRestoreInstanceState() won't be called for it.
I have implemented a simple activity with this code:
public class MainActivity extends AppCompatActivity implements Fragment_1.Operations{
Fragment_1 fragment_1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragmentactivity);
fragment_1=(Fragment_1)getSupportFragmentManager().findFragmentByTag("fragment_1");
}
//called on buttonclick, fired from a button existing in R.layout.fragmentactivity
public void createFragment(View view){
if (getSupportFragmentManager().findFragmentByTag("fragment_1")==null){
fragment_1=new Fragment_1();
FragmentTransaction transaction=getSupportFragmentManager().beginTransaction();
transaction.add(R.id.fragmentactt,fragment_1,"fragment_1");
transaction.commit();
getSupportFragmentManager().executePendingTransactions();
}
else{
fragment_1=(Fragment_1)getSupportFragmentManager().findFragmentByTag("fragment_1");
}
//Simply adding item to the listview contained in fragment_1.
fragment_1.add("Project #1");
fragment_1.add("Project #2");
fragment_1.add("Project #3");
}
//callback of interface "Operations"
#Override
public void buttonClicked() {
FragmentTransaction transaction=getSupportFragmentManager().beginTransaction();
if (fragment_1.isAdded()){
transaction.remove(fragment_1);
//transaction.addToBackStack(null);
transaction.commit();
getSupportFragmentManager().executePendingTransactions();
}
}
}
Well, the doubt came from the fact that no "onSaveInstanceState" needed to be implemented, everything got "saved" without any problems.
So, why should i use putfragment and getFragment? Do these methods need to be called in order to avoid that, when OS kills app process, they would be lost? This is the only reason i can imagine to force onSaveInstanceState and onRestoreInstanceState methods.
Any help is appreciated.
Activity and fragment lifecycles are linked so when any callback method such as onResume is called for the activity, it is called for the fragment too.
putFragment and getFragment help the activity to manage its fragment child's lifecycle. The activity also has to save instance state.
In order to be activity independant, a fragment can manage his own instance state.
I have a two class named Stock Details extends Fragment and another one is Stock info extends activity,
when I was trying to go back to my Stock details pages from Stock info pages it shows "Unfortunately,Application has stopped",but in my program I use
Intent intent = new Intent(this,StockDetails.class);
startActivity(intent);
finish();
for go back to fragment. but it does not work. help me to solve this problem
I am the beginner for android.
kindly help me to go back from an Activity to fragment
you do not need to start activity for StoreDetail as it is a subclass of Fragment not Activity.This is the reason that your app is crashed.
Now moving to your question if you want to go back to the fragment from the activty : Stock INFO you just need to call finish() it will finish the current activity(Stock Info) and the fragment which is in background will be resumed.I had same problem and solved by this way .This is the onCreate Method of Activity(in your case it is for StockInfo class).Have a look:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setmCallBack(new IResultCallback() {
#Override
public void result(Result lastResult) {
if (lastResult!=null) {
finish();// by this line I have killed the current activity
}
else
{
Toast.makeText(getActivity(), "NotScan: ", Toast.LENGTH_SHORT).show();
}
}
});
}
Ok look,
startActivity(intent);
this method name startActivity. So it will start another activity not fragment.
You can See & read the fragment:
http://developer.android.com/guide/components/fragments.html
Add fragment to go back to manually to the backstack.
FragmentTransaction transaction = getFragmentManager().beginTransaction();
YourFragmentName myFragment = new YourFragmentName();
transaction.replace(R.id.fragment_container, myFragment);
transaction.addToBackStack(null);
transaction.commit();
And There is several methods here. Just take a look:-
http://developer.android.com/reference/android/app/FragmentManager.html#popBackStack()
If you want to go back to the fragment , you can do this :
getFragmentManager().popBackStack();
I have a fragment:
public class MyFragment extends Fragment{
...
#Override
public View onCreateView(...){...}
...
}
I instantiate it:
MyFragment myFragment = new MyFragment();
I use the above fragment to replace the current fragment:
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// replace fragment
fragmentTransaction.replace(R.id.fragment_placeholder, myFragment, "myTag");
// NOTE: I did not add to back stack
Now, myFragment is showing on the screen. NOTE: I did not add myFragment to back stack.
My two questions:
1. If now, I press mobile phone back button, which fragment's life cycle callback will be invoked??
2. How can I customize the back button click listener in MyFragment class? (please do not suggest me to do myFragment.getView().setOnclickListener, but do it in MyFragment class)
Question 1: See http://developer.android.com/reference/android/app/Fragment.html#Lifecycle:
"As a fragment is no longer being used, it goes through a reverse series of callbacks:
onPause() - fragment is no longer interacting with the user either because its activity is being paused or a fragment operation is
modifying it in the activity.
onStop() - fragment is no longer visible to the user either because its activity is being stopped or a fragment operation is modifying it
in the activity.
onDestroyView() - allows the fragment to clean up resources associated with its View.
onDestroy() - called to do final cleanup of the fragment's state.
onDetach() - called immediately prior to the fragment no longer being associated with its activity."
Question 2: If you must know that it was the back button specifically that is triggering the callbacks, You can capture the back button press in your Fragment's Activity and use your own method to handle it:
public class MyActivity extends Activity
{
//...
//Defined in Activity class, so override
#Override
public void onBackPressed()
{
super.onBackPressed();
myFragment.onBackPressed();
}
}
public class MyFragment extends Fragment
{
//Your created method
public void onBackPressed()
{
//Handle any cleanup you don't always want done in the normal lifecycle
}
}
androidx.activity 1.0.0-alpha01 is released and introduces ComponentActivity, a new base class of the existing FragmentActivity and AppCompatActivity.
You can now register an OnBackPressedCallback via addOnBackPressedCallback to receive onBackPressed() callbacks without needing to override the method in your activity.
Been searching for this issue for a while to no avail now:
How to determine fragment is being restored from backstack?
I'm using the compatibility library and a ListFragment inside a FragmentActivity. When an item inside ListFragment is selected, a new Fragment is started to replace the ListFragment.
I noticed that when the FragmentActivity gets paused, the Fragment's onSaveInstanceState is called. But when the Fragment is put into the back stack via FragmentTransaction, onSaveInstanceState doesn't get called, then the lifecycle methods onCreateView and onActivityCreated gets called with null savedInstanceState Bundle.
I'm asking this because I want to load some data when the Fragment is created or restored, but not so when user comes back via. backstack.
I've looked at How to check if Fragment was restored from a backstack?
but want to add more details in hopes this would incite an answer.
Edit:
just noticed http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle)
says
Note however: this method may be called at any time before onDestroy(). There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.
So onSaveInstanceState is definitely out of the question...
I think that most simple way is do this for example in onViewCreated() method:
if (savedInstanceState == null && !mAlreadyLoaded) {
mAlreadyLoaded = true;
// Do this code only first time, not after rotation or reuse fragment from backstack
}
Because when android put fragment on backstack, it only destroy its view, but don't kill instance itself, so mAlreadyLoaded will be still true when fragment will be restored from backstack.
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
public void onBackStackChanged() {
Log.i(TAG, "back stack changed ");
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0){
// block where back has been pressed. since backstack is zero.
}
}
});
use this addOnBackStackChangedListener.
When a fragment goes to back-stack onDestroyView() called. Not onDestroy().
And when a fragment pops from back-stack onCreateView() called. Not onCreate().
So add a boolean mIsRestoredFromBackstack to fragment and follow as below:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mIsRestoredFromBackstack = false;
}
#Override
public void onResume()
{
super.onResume();
if(mIsRestoredFromBackstack)
{
// The fragment restored from backstack, do some work here!
}
}
#Override
public void onDestroyView()
{
super.onDestroyView();
mIsRestoredFromBackstack = true;
}
MAJOR EDIT: Oct 15 2013
The previous explanation (kept below for reference) fails when the application is put to the background and brought back to the foreground.
Instead, it is better to compare the current size of the backstack with the one when the fragment was created & put into the backstack.
Take a good look at Figure 2 in http://developer.android.com/guide/components/fragments.html#Creating
What this figure tells you is that when a fragment is restored from the backstack, its onCreate() is not called, while its onCreateView() is.
So, you may want to do something like this:
public class MyFragment extends Fragment {
int mBackStackSize = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBackStackSize = getFragmentManager().getBackStackEntryCount();
}
public boolean isRestoredFromBackstack() {
return mBackStackSize > getFragmentManager().getBackStackEntryCount();
}
}
If you added fragment to backstack, and after some manipulation you hide it using fragmentTransaction.hide(fragment) and then restore it from backstack like fragmentTransaction.show(fragmentManager.findFragmentByTag(fragment.getName())); you can override onHiddenChanged(boolean hidden)
#Override
public void onHiddenChanged(boolean hidden) {
// TODO Auto-generated method stub
super.onHiddenChanged(hidden);
if (!hidden) {
//fragment became visible
//your code here
}
}
In some cases you can use isVisible method to understand is it first showing of a fragment or is it restored from the backstack.