hewa jalal
hewa jalal

Reputation: 961

what is the best practice way to save fragment state when you have to replace many of them?

I have a main activity with many fragments that get replaced on each other and there state always get lost I even tried public void onSaveInstanceState(@NonNull Bundle outState) but it still didn't work I even did it in the MainActivity to retain the instance but still nothing now I have a CartOrderFragment that I add items to it from FoodListFragment when a button is clicked, but every time I go to CartOrderFragment and come back to FoodListFragment and try to add more item by clicking on the same button before, the CartOrderFragment lose its list.

MainActivity.java

public class MainActivity extends AppCompatActivity implements
     SetupAccountFragment.SetupAccountListener, FoodListFragment.ListenerFoodListFragment,
    SignInAccountFragment.SignInAccountListener, SignUpAccountFragment.SignUpAccountListener {

FragmentManager fm;
FoodListFragment foodListFragment;
ArrayList<Food> foodsOrdered;
CartOrdersFragment cartOrdersFragment;
Map<Integer, Integer> cartValues = new HashMap<>();


@Override
public void sendOrders(ArrayList<Food> foods) {
    foodsOrdered = foods;
}

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    getSupportFragmentManager().putFragment(outState, "myFragment", cartOrdersFragment);
}

// this get called when the button gets clicked in the FoodListFragment
@Override
public void cartClickListener() {
    cartOrdersFragment = CartOrdersFragment.newInstance(foodsOrdered);

    fm.beginTransaction()
            .replace(R.id.main_container, cartOrdersFragment)
            .addToBackStack(null)
            .commit();
}

  // for sign up fragment
@Override
public void registerNowBtnListener() {
    foodListFragment = new FoodListFragment();
    fm.beginTransaction()
            .replace(R.id.main_container, foodListFragment)
            .commit();
  }
// for sign up fragment

// for sign in fragment
@Override
public void signInBtn() {
    foodListFragment = new FoodListFragment();
    fm.beginTransaction()
            .replace(R.id.main_container, foodListFragment)
            .commit();

}
// for sign in fragment

@Override
public void sendToCart(int value, int adapterPosition) {
    cartValues.put(adapterPosition, value);
    foodListFragment.updateCartValue(getTotalValue());
}


private int getTotalValue() {
    int value = 0;
    for (Integer key : cartValues.keySet()) {
        value += cartValues.get(key);
    }
    return value;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState != null) {
        cartOrdersFragment = (CartOrdersFragment) getSupportFragmentManager().getFragment(savedInstanceState,
                "myFragment");
    }

    fm = getSupportFragmentManager();

    Fragment fragment = fm.findFragmentById(R.id.main_container);

    if (fragment == null) {
        // adding fragment to main container
        fm.beginTransaction()
                .replace(R.id.main_container, onBoardingFragment)
                .replace(R.id.id_container_menu, new FoodCategoryFragment())
                .addToBackStack(null)
                .commit();
    }
}
}

CartOrderFragment.java

public class CartOrdersFragment extends Fragment {

private CartOrdersAdapter cartOrdersAdapter;
private ArrayList<Food> foods;
private static final String KEY_ARRAYLIST = "ArrayList";

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelableArrayList(KEY_ARRAYLIST, foods);
}

public static CartOrdersFragment newInstance(ArrayList<Food> foods) {
    CartOrdersFragment cartOrdersFragment = new CartOrdersFragment();
    Bundle args = new Bundle();
    args.putParcelableArrayList(KEY_ARRAYLIST, foods);
    cartOrdersFragment.setArguments(args);
    return cartOrdersFragment;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    if (savedInstanceState != null) {
        foods = savedInstanceState.getParcelableArrayList(KEY_ARRAYLIST);
    }

    if (getArguments() != null) {
        foods = getArguments().getParcelableArrayList(KEY_ARRAYLIST);
    }
    cartOrdersAdapter = new CartOrdersAdapter(getActivity(), foods);
    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    recyclerView.setAdapter(cartOrdersAdapter);
  }

public class FoodListFragment extends Fragment {
private FoodListAdapter foodListAdapter;
private BadgeHolderLayout badgeHolderLayout;
private ArrayList<Food> foods;

public interface ListenerFoodListFragment {
    void sendToCart(int value, int adapterPosition);
    void cartClickListener();
    void sendOrders(ArrayList<Food> foods);
}

private ListenerFoodListFragment listenerFoodListFragment;

@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    try {
        listenerFoodListFragment = (ListenerFoodListFragment) context;
    } catch (ClassCastException e) {
        Log.d("FoodListFragment", "onAttach: " + e.getMessage());
    }
}


@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    foods = new ArrayList<>();

    foods.add(new Food("Chicken", "99999$", R.drawable.chicken));
    foods.add(new Food("Burger", "1000$", R.drawable.burger));


    badgeHolderLayout.setOnClickListener(v -> listenerFoodListFragment.cartClickListener());

    foodListAdapter = new FoodListAdapter(getActivity(), foods, listenerFoodListFragment);
    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    recyclerView.setAdapter(foodListAdapter);
}

public void updateCartValue(int value) {
    badgeHolderLayout.setCountWithAnimation(value);
}

}

FoodListAdapter.java

public class FoodListAdapter extends RecyclerView.Adapter<FoodListAdapter.ViewHolder>
    implements Filterable {

private Context context;
private List<Food> foodList;
private List<Food> foodListFiltered;
private ArrayList<Food> orderedFood = new ArrayList<>();
FoodListFragment.ListenerFoodListFragment listenerFoodListFragment;

public FoodListAdapter(Context context, List<Food> foodList,
                       FoodListFragment.ListenerFoodListFragment listenerFoodListFragment) {
    this.context = context;
    this.foodList = foodList;
    this.listenerFoodListFragment = listenerFoodListFragment;
    this.foodListFiltered = foodList;
}
}

public class ViewHolder extends RecyclerView.ViewHolder {

    public ViewHolder(@NonNull View itemView) {
        super(itemView);

        // this is the button that i mentioned
        tvOk.setOnClickListener(v -> {
            Food food = foodList.get(getAdapterPosition());
            orderedFood.add(food);
            listenerFoodListFragment.sendOrders(orderedFood);
        });
        fc.setOnClickListener(v -> fc.toggle(false));
    }
}
}

Upvotes: 0

Views: 500

Answers (1)

Lin
Lin

Reputation: 59

you can use hide and show, try this

protected void onCreate(Bundle savedInstanceState){
    fm.beginTransaction()
      .add(R.id.main_container, fragmentOne)
      .add(R.id.main_container, fragmentTwo)
      .hide(fragmentTwo)
      .addToBackStack(null)
      .commit();
}

private void onBtnTwoClick() {
    fm.beginTransaction().show(fragmentTwo).hide(fragmentOne)
      .addToBackStack(null).commit();
}

private void onBtnOneClick() {
    fm.beginTransaction().show(fragmentOne).hide(fragmentTwo)
      .addToBackStack(null).commit();
}

here is the way with replace

you need a StateHelper

public class FragmentStateHelper {

    private HashMap<String, Fragment.SavedState> fragmentStates;
    private FragmentManager fragmentManager;

    public FragmentStateHelper(FragmentManager fragmentManager) {
        fragmentStates = new HashMap<>();
        this.fragmentManager = fragmentManager;
    }

    public void restoreState(Fragment fragment, String key) {
        Fragment.SavedState savedState = fragmentStates.get(key);
        if (savedState != null) {
            if (!fragment.isAdded()) {
                fragment.setInitialSavedState(savedState);
            }
        }
    }

    public void saveState(Fragment fragment, String key) {
        if (fragment.isAdded()) {
            fragmentStates.put(key, fragmentManager.saveFragmentInstanceState(fragment));
        }
    }
}

save old fragment and restore new one before replace.

@Override
public void signInBtn() {
    //save state here
    foodListFragment = new FoodListFragment();
    //restore foodListFragment here
    fm.beginTransaction()
            .replace(R.id.main_container, foodListFragment)
            .commit();
}

you need save and restore in fragment

@Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(KEY, str);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState != null) {
            str = savedInstanceState.getString(KEY);
        }
    }

you can check here: https://medium.com/@bherbst/saving-fragment-state-yourself-522c3bca78c7

hope it help and happy new year.

Upvotes: 1

Related Questions