Overdraw in fragments



Do you know you may be forcing your GPU to dra pixels multiple times without having to do so? Overdraw is a common performance issue associated with the amount of times a pixel needs to be drawn on the screen. Colt McAnlis did a great job explaining this issue in this video:



Overdraw is quite easy to solve once you get used to it. However here are some tips that may come in handy:

Fragment Hierarchy:

You might have a flow with multiple fragments added to the `BackStack` (for example a insertion process with multiple phases). You might have noticed that, in some cases (for example if you're using add instead of replace), your fragment is still visible, and the one you're adding is on top of it:

              
Fragment2 (on the right) added on top of Fragment1(on the left). 
getFragmentManager().beginTransaction().add(R.id.container,new Fragment2(),Fragment2.TAG).commit()

A common and quick solution is to set the background of the fragment you're showing to `@android:color/white` or whatever background you're using on your application. Even though that will work it will add a new layout of colour to your view, resulting in colour overdraw.


As you can see, the blue tells us that that pixel is getting drawn 1 extra time. And those red ones are getting drawn 3 times. One way to eliminate this issue is to hide the other fragment instead of painting on top of it. So if you're adding your fragment in a container view, just find the fragment that's currently attached to it and hide it on the same transaction:
Fragment currentFragment;
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.container, new Fragment2());
if ((currentFragment = getFragmentManager().findFragmentById(R.id.container)) != null) {
    transaction.hide(currentFragment);
}
transaction.commit();
 

Fragment background override:

Sometimes you'll need to have a different background on your fragment, however the `windowBackground` will get drawn too. To remove the background colour from the window you can use:
getActivity().getWindow().setBackgroundDrawable(null);

This will remove the background from your theme but it will not restore it once you detach your fragment. You can use this method to restore the window's background :
public static Drawable getWindowBackground(Context context) {
    TypedValue typedValue = new TypedValue();
    Drawable drawable;
    context.getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true);
    if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
        drawable = new ColorDrawable(typedValue.data);
    } else {
        drawable = context.getResources().getDrawable(typedValue.resourceId);
    }
    return drawable;
}

Just call it on your fragment's `onDetach` method:
getActivity().getWindow().setBackgroundDrawable(Utils.getWindowBackground(getActivity()));

Finally you should avoid inserting ViewGroups inside ViewGroups. Try using RelativeLayout for that purpose. If you fail to remove the overdraw problems from your application don't get too disappointed and look at this screenshot of Google Messenger application and it's massive overdraw in such a simple view:


Edit: They fixed their app already so there is no massive overdraw anymore :)

Full code available on my GitHub: https://github.com/kanytu/Overdraw_example

Unknown

Related Posts

3 comments

  1. Hey Pedro,
    A great post! Thanks for sharing. I have been following your posts for the past few days and inspired by your way of writing. Keep up the good work dude.

    ReplyDelete
  2. You have an amazing style in explaining the concepts buddy I just like it very much.

    ReplyDelete
  3. I hope this is one of the best post I have gone through recently and this post will be helpful for the maximum of the people around the globe.

    ReplyDelete