Overlapping Fragments with VideoViews

john-robert-marasigan-238147 image of 2 silhouettes walking on a blue ceiling

Recently, in a client project, I ran into an issue with VideoViews overlapping each other, even though they were part of different Fragments. As is the case with most Views, when adding a Fragment on top of another, the top Fragment will have its Views drawn on top of the bottom Fragment. This however, is not the case when it comes to VideoViews, which require you to call videoView.setZOrderMediaOverlay(true) on the VideoView you would like to appear on top.

Sample Project

I have created a sample project here which shows the issue and where the code snippets below will be taken from. I have reported the issue and though I probably wouldn’t consider this a bug in the Android framework, I would consider it unexpected behavior. There is no need to download the sample project as the code snippets below will be enough explanation. Let’s dive in.

In the onCreate of the Activity, the MainFragment is added:

The layout of our MainFragment is:

onViewCreated in MainFragment will setup the VideoView and begin playing the sample video.

Here is what the MainFragment looks like:

Main Fragment Video View fully visible

Next up, we simply need to add another Fragment that contains a VideoView to the content_layout container, the same container holding our MainFragment. In the sample project clicking the FAB will execute the following Fragment transaction:

The NewFragment instantiates a layout very similar to MainFragment except it has a colored background and an offset VideoView so that it only partially overlaps with the VideoView from MainFragment:

Just like in MainFragment, in onViewCreated the NewFragment finds a reference it its VideoView and begins playing it.  The NewFragment plays an animated video instead of a regular video to better illustrate the issue. For reference, if the NewFragment was not added on top of our MainFragment, but rather existed on its own, it would look like this:

New Fragment Video View fully visible

It doesn’t look like this however, which leads us to the problem.

Problem

After the NewFragment has been instantiated, it looks like this:

Main Fragment Video View overlapping New Fragment Video View

Observe now that NewFragment is completely covering MainFragment. The “Main Fragment” text that used to be at the top has been replaced with “New Fragment.” The white background of MainFragment has also been completely covered by the red background of NewFragment. The only remaining part of the MainFragment that is still visible is the VideoView. The reason that it is only partially visible is because the VideoView in NewFragment is lower than the VideoView of the MainFragment.

If the NewFragment VideoView overlaid exactly the VideoView of the MainFragment, it would look like this:

New Fragment Video View fully visible

Observe that the MainFragment VideoView is still completely visible while the NewFragment VideoView displaying the animated video is completely obscured.

The reason that the MainFragment VideoView is only visible where it overlaps with the NewFragment VideoView is explained rather eloquently in the SurfaceView documentation, “the SurfaceView punches a hole in its window to allow its surface to be displayed.” This hole punching is what differentiates a SurfaceView (VideoView and GLSurfaceView both extend SurfaceView) from a regular View. This Stack Overflow answer offers further explanation from an Android framework engineer discussing stacking SurfaceViews:

“Sorry, you can’t do this — because surface views are very special and not really views (the surface is a separate window Z-ordered with your own), their Z-ordering does not match your views. A surface view is a big, heavy object; you are not intended to treat SurfaceView like a regular view in this way.”

Solution

To ensure the VideoView of the NewFragment appears on top, simply callsetZOrderMediaOverlay(true)on the VideoView in the NewFragment. This method “Controls whether the surface view’s surface is placed on top of another regular surface view in the window (but still behind the window itself). This is typically used to place overlays on top of an underlying media surface view.”

It may sound obvious, but if you are running into a problem there is a good chance an API is available that will help you out. Always review the API documentation to determine if this is the case. Be sure to not only look at the documentation of the class you are having trouble with, but also any super superclasses that your class extends. This was especially important in my case because VideoView didn’t expose any methods that would have solved this issue, I had to go a level deeper to its superclass SurfaceView.

Alternative Solutions

Besides using setZOrderMediaOverlay, there are a couple other solutions that didn’t end up fitting my needs, but may fit yours.

Stopping playback of MainFragment VideoView and setting it to GONE will prevent the NewFragment VideoView from being overlapped.  

Instead of adding the NewFragment during the transaction, do a replace:

This solution didn’t work for me because I wanted to retain my MainFragment.

Failed Attempts

Before executing the FragmentTransaction to add the NewFragment, I grabbed the View of the MainFragment and set it to GONE. This had no effect on the VideoView issue.

As part of the transaction to add the NewFragment, I hid the MainFragment. This also had no effect on the VideoView issue.

If you enjoyed this post, you can follow me on Twitter @dickclucas, where I talk about Android, technology, and other related topics. Thanks for reading and please feel free to reach out in the comments section below or directly to our team at Raizlabs!


Do you have a project in mind? We’d love to work with you. If you’d like an opportunity to work on projects with us, check out our Careers page. We’re hiring!

One response to “Overlapping Fragments with VideoViews”

  1. Great article! Thanks! Had the same issue!

Leave a Comment