React-native-mapbox-gl: Stacked mapbox views are rendered in wrong order

Created on 20 Apr 2017  路  16Comments  路  Source: nitaliano/react-native-mapbox-gl

In our application, we use a card stack for navigation. Several different screens in our application will use mapbox views. If two screens with mapbox views end up on the stack, and mapbox viewports overlap, you can get into a situation where the mapbox view behind ends up drawing over the mapbox view in front, resulting in this kind of situation:
screen
Is there something in the native code which causes a difference in rendering order between react and native?

question

Most helpful comment

We are hitting this issue as well - @dxiao's workaround does work, but for the average app it requires significant architectural work (tracking navigation state outside the navigator) to be feasible.

I would say this workaround is not sufficient reason to close this issue, and the problem should be fixed at react-native-mapbox-gl or mapbox-gl library level.

Having worked with a dozen or more React Native apps, I would say using a JavaScript-based navigator is the default option for most. All of these apps are affected by this bug.

This includes anybody using navigators such as React Navigation, or the (old) built-in Navigator, the newer built-in NavigationExperimental, an a number of third-party libraries like react-native-router-flux, ex-navigator, ex-navigation, etc.

Just my 2 cents. If we come up with a generalized fix for this, happy to submit it upstream.

All 16 comments

@dxiao thanks for the flag! adding @pveugen + @tobrun for 馃憖 .

@dxiao what you are noticing is the surfaces of both mapviews are fighting for the z ordering. Note that this is different from ordering in the viewhierachy. See the following snippet from SurfaceView:

Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen. The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it.

I'm not aware of the capabilities of the current react plugin but this can be solved using a TextureView (as this render a texture directly in the viewhierachy). This option can be enabled with MapboxMapOptions or the related xml attribute. Another way would be to get a reference to the actual SurfaceView and play around with options as setZOrderMediaOverlay or setZorderOnTop.

I'm also not aware of the concept of card stack for navigation. General take away should be to follow the best practices on android, every new screen = new activity or new fragment.

Let me know if you need more information on these topics.

@tobrun thanks for the explanation! Do you mean the textureMode property on MapboxMapOptions? The one that's deprecated?

So I played around with things, ended up not going the TextureView route because the approach is supposedly deprecated (See https://github.com/mapbox/mapbox-gl-native/issues/5000 and https://github.com/mapbox/mapbox-gl-native/issues/8197), and because it does make the map interaction less smooth. Playing around with the z order didn't work: setZorderOnTop correctly orders the two maps, but puts both maps above everything else (including surrounding elements), and setZOrderMediaOverlay did nothing whatsoever, as well as setZ.

Ended up going the route of selectively hiding the map element when the given view is supposed to be hidden, instead of just letting the compositing hide it for me.

And if you curious about the card stack for navigation, see http://facebook.github.io/react-native/releases/0.41/docs/navigation.html#step-4-create-a-navigation-stack

Thanks for the pointers @tobrun

how to know when the given view is supposed to be hidden?

I created a component that took the navigation properties passed to each screen and the navigation state and figured out if it was at the top of the stack.

@dxiao It worked for me, thanks a lot, dude

So we've run into this as well ;( thank you for the guidance

@tobrun just wondering if you have any additional comments on this by chance.

@tobrun - my java is pretty terrible - can you give some guidance on how to get ahold of the surface view? In android docs I saw but have no clue where to call that getHolder().

Ok - after extensive digging, trial and error, etc.... trying to set the SurfaceLayer zOrder won't really help if your using react-navigation.

The core part of the issue with Android is that, when you use react-navigation all of your Android Views are running on the same thread, thus causing the "view punching" issue.

If you use wix's react-navitve-navigation - new screens are pushed to another thread, thus resolving this issue it seems. https://github.com/wix/react-native-navigation

We are hitting this issue as well - @dxiao's workaround does work, but for the average app it requires significant architectural work (tracking navigation state outside the navigator) to be feasible.

I would say this workaround is not sufficient reason to close this issue, and the problem should be fixed at react-native-mapbox-gl or mapbox-gl library level.

Having worked with a dozen or more React Native apps, I would say using a JavaScript-based navigator is the default option for most. All of these apps are affected by this bug.

This includes anybody using navigators such as React Navigation, or the (old) built-in Navigator, the newer built-in NavigationExperimental, an a number of third-party libraries like react-native-router-flux, ex-navigator, ex-navigation, etc.

Just my 2 cents. If we come up with a generalized fix for this, happy to submit it upstream.

I solved this by rendering the map based on the specific the route of React Navigation.

Having the same issue using react-navigation on Android when I push a new route in the stack I see the underlying Map instead of the new one.

Did someone came up with a simpler solution rather than checking what route is being rendered and show/hide the map? (which as someone pointed out means a bit of code to make it work)

Thanks 馃槃

Add textureMode as a prop to your MapView it will render as a TextureView instead of a GLSurfaceView

Hi @nitaliano I wonder how can I add textureMode as a prop to MapView

you don't need to manually add it if you're on 6.1.1 or above TextureView is the default Android view used.

Was this page helpful?
0 / 5 - 0 ratings