Justin
Justin

Reputation: 18186

MapView control not loading within ViewPager

I have a MapView control that I'm trying to load inside a ViewPager. The ViewPager is loaded with layout views, NOT fragments, as the ViewPager itself is within a fragment and I wanted to avoid the fragment within a fragment scenario.

Here is the ViewPager's adapter, which loads the map as the 4th screen:

public class AmbientPagerAdapter : PagerAdapter {
        private ViewPagerWithMap pager;
        private CurrentLocationStatistics ambientData;
        private AmbientSearchBuilder searchBuilder;
        private AmbientSearchIndicatorBuilder searchIndicatorBuilder;
        private AmbientIndicatorBuilder indicatorBuilder;
        private AmbientMapBuilder mapBuilder;
        private Bundle savedInstanceState;

        public AmbientPagerAdapter(View view, CurrentLocationStatistics ambientData, Bundle savedInstanceState) {
            this.pager = view.FindViewById<ViewPagerWithMap> (Resource.Id.ambient_pager);
            this.ambientData = ambientData;
            this.savedInstanceState = savedInstanceState;

            SetupEvents ();
        }

        private void SetupEvents () {
            pager.Touch += (object sender, View.TouchEventArgs e) => {
                pager.Parent.RequestDisallowInterceptTouchEvent(true);
                e.Handled = false;
            };
        }

        #region Setup Views

        public override Java.Lang.Object InstantiateItem(View collection, int position) {
            var layoutInflater = (LayoutInflater)ApplicationContext.Activity.GetSystemService (Context.LayoutInflaterService);
            View view = null;
            switch (position) {
            case 0: 
                view = layoutInflater.Inflate (Resource.Layout.AmbientSearch, null);
                searchBuilder = new AmbientSearchBuilder (view, ambientData);
                break;
            case 1: 
                view = layoutInflater.Inflate (Resource.Layout.AmbientSearchIndicators, null);
                searchIndicatorBuilder = new AmbientSearchIndicatorBuilder (view, ambientData);
                break;
            case 2: 
                view = layoutInflater.Inflate (Resource.Layout.AmbientIndicators, null);
                indicatorBuilder = new AmbientIndicatorBuilder (view, ambientData);
                break;
            case 3: 
                view = layoutInflater.Inflate (Resource.Layout.AmbientMap, null);
                mapBuilder = new AmbientMapBuilder (view, ambientData, savedInstanceState);
                break;
            }

            // Add view to pager
            var viewPager = collection.JavaCast<ViewPager>();
            viewPager.AddView(view);

            return view;
        }

        public void Update (CurrentLocationStatistics ambientData) {
            this.ambientData = ambientData;
            if (searchBuilder != null) {
                searchBuilder.Update (ambientData);
            }
            if (searchIndicatorBuilder != null) {
                searchIndicatorBuilder.Update (ambientData);
            }
            if (indicatorBuilder != null) {
                indicatorBuilder.Update (ambientData);
            }
            if (mapBuilder != null) {
                mapBuilder.Update (ambientData);
            }
        }

        #endregion

        #region Infrastructure

        public override void DestroyItem(View collection, int position, Java.Lang.Object view) {
            var viewPager = collection.JavaCast<ViewPager>();
            viewPager.RemoveView (view as View);
            if (position == 3 && mapBuilder != null) {
                mapBuilder.OnDestroy ();
            }
        }

        public override bool IsViewFromObject(View view, Java.Lang.Object obj) {
            return view == obj;
        }

        public override IParcelable SaveState() {
            return null;
        }

        public override void StartUpdate(View arg0) {
        }

        public override void FinishUpdate(View arg0) {
        }

        public void UpdateVisibleItem (int index) {
            pager.SetCurrentItem (index, false);
        }

        public override int Count {
            get {
                return 4;
            }
        }

        #endregion

        public AmbientSearchBuilder SearchBuilder { get { return searchBuilder; } }
        public AmbientMapBuilder MapBuilder { get { return mapBuilder; } }
    }

Once the user swipes to the 3rd screen it instantiates the map, as the ViewPager needs to load the next screen so it can show both at once when swiping over (the default ViewPager behavior.) Could this be part of the issue, that it's loading the map when it's not in view yet?

Below is the class that sets up the MapView. The lifecycle events are called by the fragment hosting the ViewPager.

public class AmbientMapBuilder : AmbientBuilderBase {
        private View view;
        private TouchMapView mapView;
        private PropertyMapAdapter adapter;
        private Geolocation initialLocation;
        private CameraPosition lastCamera;
        private TinyMessageSubscriptionToken filterToken;

        public AmbientMapBuilder(View view, CurrentLocationStatistics ambientData, Bundle savedInstanceState = null) : base(ambientData) {
            this.view = view;
            this.mapView = view.FindViewById<TouchMapView> (Resource.Id.map);
            mapView.OnCreate (savedInstanceState);
            //MapsInitializer.Initialize (ApplicationContext.Activity);
        }

        public void Update (CurrentLocationStatistics ambientData) {
            this.ambientData = ambientData;
        }

        #region Lifecycle Events

        public void OnResume () {
            if (mapView != null)
                mapView.OnResume ();

            if (lastCamera != null) {
                if (adapter != null)
                    adapter.MoveMapToPosition (lastCamera);
                lastCamera = null;
            }
        }

        public void OnPause () {
            mapView.OnPause ();
            lastCamera = mapView.Map.CameraPosition;
            if (filterToken != null) {
                filterToken.Dispose ();
                filterToken = null;
            }
        }

        public void OnSaveInstanceState (Bundle outState) {
            var map = mapView;
            if (map != null)
                map.OnSaveInstanceState (outState);
        }

        public void OnLowMemory () {
            if (mapView != null) {
                mapView.OnLowMemory ();
            }
        }

        public void OnDestroy () {
            if (mapView != null) {
                mapView.OnDestroy ();
            }
        }

        #endregion

        public bool MapIsLoaded { get; set; }

        public PropertyMapAdapter MapAdapter {
            get { return adapter; }
        }

        public View View {
            get { return view; }
        }

        public TouchMapView MapView {
            get { return mapView; }
        }
    }

And lastly, this is the layout xml for the map:

<RPR.Mobile.Droid.Widget.TouchMapView xmlns:map="http://schemas.android.com/apk/res-auto"
        android:id="@+id/map"
        map:cameraTargetLat="37.09024"
        map:cameraTargetLng="-95.712891"
        map:cameraZoom="3"
        map:uiCompass="false"
        map:uiRotateGestures="false"
        map:uiScrollGestures="true"
        map:uiTiltGestures="false"
        map:uiZoomGestures="true"
        map:uiZoomControls="false"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

Below is what the map looks like, it just shows the grid lines and a gray background but no map, and the map can't be interacted with.

enter image description here

Any ideas what the issue is?

Upvotes: 2

Views: 715

Answers (1)

Justin
Justin

Reputation: 18186

This issue arose due to the lifecycle of the fragment not aligning with the instantiation of the mapView in the ViewPager, specifically that the fragment.OnResume method was called before the mapView was setup so mapView.OnResume () was never called. I needed to manually call mapView.OnResume () after mapView.OnCreate () and then everything worked peachy.

Upvotes: 4

Related Questions