22
33import android .content .Context ;
44import android .util .Log ;
5+ import android .view .Choreographer ;
56import android .view .View ;
67import android .view .ViewGroup ;
78import android .widget .FrameLayout ;
1920import com .esri .arcgisruntime .geometry .GeometryEngine ;
2021import com .esri .arcgisruntime .geometry .Point ;
2122import com .esri .arcgisruntime .layers .Layer ;
23+ import com .esri .arcgisruntime .loadable .LoadStatusChangedEvent ;
24+ import com .esri .arcgisruntime .loadable .LoadStatusChangedListener ;
2225import com .esri .arcgisruntime .location .LocationDataSource ;
2326import com .esri .arcgisruntime .mapping .ArcGISMap ;
2427import com .esri .arcgisruntime .mapping .LayerList ;
@@ -93,6 +96,8 @@ final class ArcgisMapController implements DefaultLifecycleObserver, PlatformVie
9396
9497 private boolean disposed = false ;
9598
99+ private boolean loadedCallbackPending = false ;
100+
96101 ArcgisMapController (
97102 int id ,
98103 Context context ,
@@ -258,6 +263,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
258263 }
259264 break ;
260265 case "map#setMap" : {
266+ invalidateMapIfNeeded ();
261267 final Viewpoint viewpoint = mapView .getCurrentViewpoint (Viewpoint .Type .CENTER_AND_SCALE );
262268 changeMapType (call .arguments );
263269 if (viewpoint != null ) {
@@ -395,11 +401,13 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
395401 }
396402 break ;
397403 case "layers#update" : {
404+ invalidateMapIfNeeded ();
398405 layersController .updateFromArgs (call .arguments );
399406 result .success (null );
400407 }
401408 break ;
402409 case "markers#update" : {
410+ invalidateMapIfNeeded ();
403411 List <Object > markersToAdd = call .argument ("markersToAdd" );
404412 markersController .addMarkers (markersToAdd );
405413 List <Object > markersToChange = call .argument ("markersToChange" );
@@ -410,12 +418,14 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
410418 }
411419 break ;
412420 case "map#clearMarkerSelection" : {
421+ invalidateMapIfNeeded ();
413422 selectionPropertiesHandler .reset ();
414423 markersController .clearSelectedMarker ();
415424 result .success (null );
416425 }
417426 break ;
418427 case "polygons#update" : {
428+ invalidateMapIfNeeded ();
419429 List <Object > polygonsToAdd = call .argument ("polygonsToAdd" );
420430 polygonsController .addPolygons (polygonsToAdd );
421431 List <Object > polygonsToChange = call .argument ("polygonsToChange" );
@@ -426,6 +436,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
426436 }
427437 break ;
428438 case "polylines#update" : {
439+ invalidateMapIfNeeded ();
429440 List <Object > polylinesToAdd = call .argument ("polylinesToAdd" );
430441 polylinesController .addPolylines (polylinesToAdd );
431442 List <Object > polylinesToChange = call .argument ("polylinesToChange" );
@@ -522,6 +533,42 @@ private void destroyMapViewIfNecessary() {
522533 }
523534 }
524535
536+ /**
537+ * Invalidates the map view after the map has finished rendering.
538+ *
539+ * <p>gmscore GL renderer uses a {@link android.view.TextureView}. Android platform views that are
540+ * displayed as a texture after Flutter v3.0.0. require that the view hierarchy is notified after
541+ * all drawing operations have been flushed.
542+ *
543+ * <p>Since the GL renderer doesn't use standard Android views, and instead uses GL directly, we
544+ * notify the view hierarchy by invalidating the view.
545+ *
546+ * <p>To workaround this limitation, wait two frames. This ensures that at least the frame budget
547+ * (16.66ms at 60hz) have passed since the drawing operation was issued.
548+ */
549+ private void invalidateMapIfNeeded () {
550+ if (mapView == null || mapView .getMap () == null || loadedCallbackPending ) {
551+ return ;
552+ }
553+
554+ loadedCallbackPending = true ;
555+ mapView .getMap ().addDoneLoadingListener (() -> {
556+ loadedCallbackPending = false ;
557+ postFrameCallback (() -> postFrameCallback (
558+ () -> {
559+ if (mapView != null ) {
560+ mapView .invalidate ();
561+ }
562+ }));
563+ });
564+ }
565+
566+ private static void postFrameCallback (Runnable f ) {
567+ Choreographer .getInstance ()
568+ .postFrameCallback (frameTimeNanos -> f .run ());
569+ }
570+
571+
525572 private void handleTimeAwareLayerInfos (MethodChannel .Result result ) {
526573 final ArcGISMap map = mapView .getMap ();
527574 if (map == null || map .getOperationalLayers ().size () == 0 ) {
0 commit comments