popBackStack causes calling oncreateView of fragme

2020-07-03 03:04发布

问题:

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 :

  1. open A.
  2. open B.
  3. 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.

回答1:

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



回答2:

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
      }
  }
}


回答3:

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"!!!!!