I have 3 fragment A, B,C.I wrote piece of code for replacing them and maintaining backstack:
public void addFragment(Fragment fragmentToAdd, String fragmentTag) {
FragmentManager supportFragmentManager = getSupportFragmentManager();
Fragment activeFragment = getActiveFragment();
FragmentTransaction fragmentTransaction = supportFragmentManager
.beginTransaction();
if (null != activeFragment) {
fragmentTransaction.hide(activeFragment);
}
fragmentTransaction.replace(R.id.layout_child_activity, fragmentToAdd,
fragmentTag);
if (supportFragmentManager.getBackStackEntryCount() > 1) {
supportFragmentManager.popBackStack();
}
fragmentTransaction.addToBackStack(fragmentTag);
fragmentTransaction.commit();
}
Here in this piece of code
if (supportFragmentManager.getBackStackEntryCount() > 1) {
supportFragmentManager.popBackStack();
}
I using for pop the latest fragment if stack length is more than 1. Now due to this when length is going greater than 1 than it is calling onCreate view again and again.
Like :
- open A.
- open B.
- open C.(In case of open C. onCreateView of A is called. )
Why I am getting such kind of behavior ? When I am removing that italic code than it is not happening.
Than behavior is normal, coming from the backstack transaction, as the docs say. The backstack never saves Fragments, it just saves the transaction
http://developer.android.com/intl/es/guide/components/fragments.html
What I do, I am not sure if, is it the best way but
when I want to clear all the transactions I do this
1) INSIDE YOUR ACTIVITY check if is there any transactions in the back stack,
and add a flag inside your fragment, in your case is A
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
if(backStackCount > 0) {
Transactions.MUST_DETACH_FROM_BACKSTACK = true;
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
2) Inside your fragment A, get the flag and Remove the fragment onCreateView
and return null like this
public class Transactions extends android.support.v4.app.Fragment{
public static boolean MUST_DETACH_FROM_BACKSTACK = false;
public Transactions() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i("FRAGMENT", "onCreateView "+MUST_DETACH_FROM_BACKSTACK);
// Inflate the layout for this fragment
if (MUST_DETACH_FROM_BACKSTACK) {
MUST_DETACH_FROM_BACKSTACK = false;
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
return null;
}
return inflater.inflate(R.layout.fragment_transactions, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.i("FRAGMENT", "onViewCreated");
if(view != null){
Log.i("FRAGMENT", "ThreadStarted");
startThread(view);
}
}
BUT BE CAREFULL I Get onResume() Called after the
OnCreateView()
even with getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
So if you have any conde onResume method you should handle it properly
I solved this problem by inheriting (1) all fragments from my custom BaseFragment (2).
In this BaseFragment I created a variable: public static boolean removing; (3)
and set it to true (4) before calling popBackStackImmediate() and reset it to false after that. (5)
In the BaseFragment-childs I check the variable. (6)
Example-Code
Activity-class
BaseFragment.removing = true; //(4)
//pop all fragments
while(getSupportFragmentManager().getBackStackEntryCount() > 0){
fragmentManager.popBackStackImmediate();
}
BaseFragment.removing = false; //(5)
BaseFragment (2)
public class BaseFragment extends Fragment{
public static boolean removing = false; //(3)
}
Fragment-Child
public class fragment extends BaseFragment{ //(1)
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
if(!removing){ // (6)
//your code
}
}
}
Remember the life cycle of a fragment
. By the time we get back to this, it will start from oncreateView()
always. But we can still save data that we will then process in the oncreated to fill our view. for you you can do that:
Main-activity.java
public class MainActivity extends AppCompatActivity implements Fragment2.myListener {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate: ");
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Fragment1 fragment1 = Fragment1.newInstance();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragmentContainer, fragment1, Fragment1.TAG);
fragmentTransaction.addToBackStack(Fragment1.TAG);
fragmentTransaction.commit();
}
});
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
Log.e(TAG, "onSaveInstanceState");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
@Override
public void bindCount(int newCount) {
((Fragment1)getSupportFragmentManager().findFragmentByTag(Fragment1.TAG)).setCount(newCount);
}
}
Fragment1.java
public class Fragment1 extends Fragment {
public static final String TAG = "fragment1";
private static final String SAVE_COUNT = "save_count";
private int count;
public Fragment1() {
}
public static Fragment1 newInstance() {
Fragment1 fragment = new Fragment1();
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate: ");
if (savedInstanceState != null) {
count = savedInstanceState.getInt(SAVE_COUNT);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_fragment1, container, false);
Button goToButton = (Button) view.findViewById(R.id.button);
goToButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Fragment2 fragment2 = Fragment2.newInstance();
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragmentContainer, fragment2, Fragment2.TAG);
fragmentTransaction.addToBackStack(Fragment2.TAG);
fragmentTransaction.commit();
}
});
return view;
}
public void setCount(int newCount){
count = newCount;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.e(TAG, "onSaveInstanceState: ");
outState.putInt(SAVE_COUNT, count);
}
}
Fragment2.java
public class Fragment2 extends Fragment {
public static final String TAG = "fragment2";
public Fragment2() {
// Required empty public constructor
}
public static Fragment2 newInstance() {
Fragment2 fragment = new Fragment2();
return fragment;
}
myListener listener;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_fragment2, container, false);
//Here I am just modifying a value that wants to send to fragment1
listener.bindCount(45);//newCount
return view;
}
public interface myListener{
void bindCount(int newCount);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//initialice listener
listener = (myListener) getActivity();
}
}
So ... For the communication between fragments
we will need your container activity
, through interface. As we can see, Fragment2 has an interface
that implements its activity
and when it is executed, it calls a method where we change the count value in fragment1
, which is stored in onSaveInstanceState
, so we can resume any modification that occurs even when oncreateview
is executed again. this can be used for many other data such as arraylist, string, float, long, object,
etc.
sorry my "english"!!!!!