I begin to study the work with the fragments. I want to try to create a test fragment that activates the default loading into the container. The fragment works with the network and the user can initiate data update from the server at any time. The application contains only a few fragments. Problems begin when you need to handle orientation change events, etc. I don’t want to use something like android:configChanges="orientation|screenSize|keyboardHidden" , as Google says that this is a bad practice and generally antipattern. I want to learn how to control the application through onSaveInstanceState . But even when I install the setRetainInstance(true) fragment of Android, it continues to destroy my ProgressDialog or SwipeRefreshLayout indicator (a circle so multi-colored) as the configuration changes.

Here is my fragment class and my solutions. Please help in correcting it or point to incorrect parts of the code.

 public class HomeFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener { private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM2 = "param2"; private static final String TAG = HomeFragment.class.getSimpleName(); // TODO: Rename and change types of parameters private String mParam1; private String mParam2; View rootView; SwipeRefreshLayout swipeRefreshLayout; FragmentOnNetworkRequest onNetworkRequestListener; boolean isNetworkingLoading = false; boolean isSwipeRefreshShowing = false; boolean isProgressDialogShowing = false; ProgressDialog dialog; public HomeFragment() { // Required empty public constructor } public static HomeFragment newInstance(String param1, String param2) { Log.d(TAG, "newInstance"); HomeFragment fragment = new HomeFragment(); Bundle args = new Bundle(); args.putString(ARG_PARAM1, param1); args.putString(ARG_PARAM2, param2); fragment.setArguments(args); return fragment; } @Override public void onAttach(Context context) { super.onAttach(context); try { onNetworkRequestListener = (FragmentOnNetworkRequest) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement onNetworkRequestListener"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate"); setRetainInstance(true); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(TAG, "onCreateView"); if(rootView != null) return rootView; rootView = inflater.inflate(R.layout.fragment_home, container, false); swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.fragment_home_swipe_layout); swipeRefreshLayout.setColorSchemeColors( getResources().getColor(R.color.colorAccent), getResources().getColor(R.color.colorPrimary)); swipeRefreshLayout.setOnRefreshListener(this); Random r = new Random(); int rand = r.nextInt(100 - 1) + 1; TextView textView = (TextView) rootView.findViewById(R.id.textViewHome); textView.setText("HOME FRAGMENT "+rand); return rootView; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); Log.d(TAG, "onViewCreated"); // Fragment was created in first time if(savedInstanceState == null) { showLoadingDialog(); initDataFromServer(); } else { // continue show loading anim isNetworkingLoading = savedInstanceState.getBoolean("isNetworkingLoading"); isProgressDialogShowing = savedInstanceState.getBoolean("isProgressDialogShowing"); isSwipeRefreshShowing = savedInstanceState.getBoolean("isSwipeRefreshShowing"); updateViews(); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, "onSaveInstanceState"); outState.putBoolean("isNetworkingLoading", isNetworkingLoading); outState.putBoolean("isProgressDialogShowing", isProgressDialogShowing); outState.putBoolean("isSwipeRefreshShowing", isSwipeRefreshShowing); } @Override public void onPause() { super.onPause(); forceHideSwipeRefresh(); forceHideLoadingDialog(); } public void updateViews() { Log.d(TAG, "updateViews(): isNetworkingLoading = "+isNetworkingLoading); Log.d(TAG, "updateViews(): isSwipeRefreshShowing = "+isSwipeRefreshShowing); Log.d(TAG, "updateViews(): isProgressDialogShowing = "+isProgressDialogShowing); if(isNetworkingLoading && isSwipeRefreshShowing) showSwipeRefresh(); if(isNetworkingLoading && isProgressDialogShowing) showLoadingDialog(); } public void showSwipeRefresh() { isSwipeRefreshShowing = true; swipeRefreshLayout.post(new Runnable() { @Override public void run() { swipeRefreshLayout.setRefreshing(true); } }); } public void hideSwipeRefresh() { isSwipeRefreshShowing = false; if (swipeRefreshLayout.isRefreshing()) { swipeRefreshLayout.setRefreshing(false); } } public void forceHideSwipeRefresh() { if (swipeRefreshLayout != null) { swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.destroyDrawingCache(); swipeRefreshLayout.clearAnimation(); } } private void initDataFromServer() { isNetworkingLoading = true; onNetworkRequestListener.onNetworkRequest(this); } public void showLoadingDialog() { isProgressDialogShowing = true; dialog = new ProgressDialog(getContext()); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); dialog.setMessage("Loading. Please wait..."); dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); dialog.show(); } public void hideLoadingDialog() { isProgressDialogShowing = false; forceHideLoadingDialog(); } public void forceHideLoadingDialog() { if(dialog != null) { dialog.dismiss(); dialog = null; } } @Override public void onNetworkResponse(String response) { Log.d(TAG, "onNetworkResponse(): response = "+response); isNetworkingLoading = false; hideSwipeRefresh(); hideLoadingDialog(); } @Override public void onRefresh() { isSwipeRefreshShowing = true; initDataFromServer(); } } 

The fragment basically works fine, but I feel something is wrong and not quite right. There are a lot of flags just to check whether a spinning circle, etc., was shown.

    1 answer 1

    Retain fragments, like normal fragments, are redrawn after changing the device configuration. The key difference between retain fragments and ordinary fragments is that retain fragments retain their state (the state of the class fields).

    If you use retain fragments, then it makes no sense to save something using onSaveInstanceState(...) and then restore - the state of the objects will be saved and restored automatically.

    Retain fragments should be used in the case when the fragment stores objects whose state cannot be saved (for example, class objects for working with the Internet, for playing audio).

    • Yes, indeed, I commented out these lines //isNetworkingLoading = savedInstanceState.getBoolean("isNetworkingLoading");//outState.putBoolean("isNetworkingLoading", isNetworkingLoading); The fragment works correctly. Now it’s setRetainInstance() how setRetainInstance() works. I understand that this is true only for those fragments that are not in the backStack? How to deal with the rest of the fragments? Do not tell me how the code for the rest? Because I am going to use it further in all my applications. And is it right that I manually restore the indicators and progressDialog? - Trancer
    • это справедливо только для тех фрагментов, которые не находятся в backStack'е - why did you take it? Regarding the code - I do not see the need to use setRetainInstance(true); in your snippet. - post_zeew
    • с чего Вы это взяли? developer.android.com/reference/android/app/… - Trancer
    • @Trancer; Then yes, only for fragments that are not in the back stack. - post_zeew
    • Still, it does not work when returning to the fragment by the button back to check whether it is necessary to load data from the server and update Views or not. The meaning is as follows: when you first open a fragment, download data from the Internet and apply it to Views , if the fragment is opened using the Back button, then show the fragment with old data, and the user can update the data by SwipeRefreshLayout down - Trancer