This is the last part of this 6 part series about Fragment Oriented Architecture in Android applications. In the previous post I talked about managing sessions in fragment oriented application. In this post I am going to talk about retaining view hierarchy of a Fragment after removing it from container and then coming back to it by popping the backstack.

(Sample application’s source code and README)

When a fragment gets replaced by another fragment and the transaction is added to back stack, the expectation after a popBackStack() is to return to the previous fragment with its UI state intact. Activity backstack takes care of this expectation quite cleanly until a low-memory situation occurs. But in case of fragments, this isn’t the default behaviour. In a vanilla implementation, the replaced fragment’s view-hierarchy would get recreated upon returning back to it. Reason is that during a replace operation, all the destructive life-cycle methods get called till onDestroyView(), which wipes out the view-hierarcy. Upon returning back, all the constructive lifecycle methods right from onCreateView() get called, thus, recreating the view-hierarchy totally afresh. Reason for this flow is to keep ‘Fragments’ memory friendly. Without the view-hierarchy, a fragment is just a java object with a bunch of instance variables.

fragment_lifecycle

So, the good news is we still have the instance variables intact. For instance, if user updates text in an EditText to “Some text”, we can keep this value in an instance variable before a replace operation and upon returning back, this value can be set as the text of EditText in new view hierarchy. Same can be done with scroll-state, switch state, etc. This is the recommended way and pretty clean. But this solution can get exponentially complicated with increase in complexity of fragment’s view-hierarchy.

Dirty solution is to retain the view-hierarchy upon a replace operation. This of course dumps the ‘memory-friendly’ tag but helps a great deal while implementing complicated views. This is done by keeping a reference to the root-view of fragment before replace operation and return it from onCreateView() instead of inflating a fresh view-hierarchy. This can be done by the following additional code in BaseFragment.

// PERSISTENT ROOT VIEW

public abstract class BaseFragment extends Fragment {
    public boolean hasInitializedRootView = false;
    private View rootView;

    public View getPersistentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState, int layout) {
	if (rootView == null) {
            // Inflate the layout for this fragment
            rootView = inflater.inflate(layout, null);
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)rootView.getParent()).removeView(rootView);
        }

    return rootView;
}

getPersistentView() method in above code keeps a reference to the view-hierarchy and never inflates it again. This method can be called from onCreateView() callback methods of inheriting fragments that have a complex UI.

// UTILISING PERSISTENT ROOT VIEW IN FRAGMENT

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return getPersistentView(inflater, container, savedInstanceState, R.layout.fragment_layout);
}


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

    if(!hasInitializedRootView) {
	hasInitializedRootView = true;

        // Do initial setup of UI
        doInitialSetUpOfUI();
    }
}

In sample application, last two sections are using this technique to retain scroll position of list. NestedListFragment is an even more complex situation where the list is a nested child fragment. Handling this situation in recommended ways is quite a pain.

This, of course, is not an ideal solution. I’m still looking for a better one. Please direct me if you hit upon a better solution.

Share this:

Privacy Preference Center