Reputation: 7013
Been having a hard time figuring out why my MAP property is always null for my _mapFragment. Currently I always get this error when I get to the map page.
10-17 02:29:07.335 E/mono-rt ( 1288): [ERROR] FATAL UNHANDLED EXCEPTION:
System.NullReferenceException: Object reference not set to an instance of an object
10-17 02:29:07.335 E/mono-rt ( 1288): at Xamarin.Forms.Platform.Android.Platform.OnLayout (Boolean changed, Int32 l, Int32 t, Int32 r, Int32 b) [0x00000] in <filename unknown>:0
10-17 02:29:07.335 E/mono-rt ( 1288): at Xamarin.Forms.Platform.Android.PlatformRenderer.OnLayout (Boolean changed, Int32 l, Int32 t, Int32 r, Int32 b) [0x00000] in <filename unknown>:0
10-17 02:29:07.335 E/mono-rt ( 1288): at Android.Views.ViewGroup.n_OnLayout_ZIIII (IntPtr jnienv, IntPtr native__this, Boolean changed, Int32 l, Int32 t, Int32 r, Int32 b) [0x00000] in <filename unknown>:0
10-17 02:29:07.335 E/mono-rt ( 1288): at (wrapper dynamic-method) object:99e4ae3d-31ec-4c07-9bb3-fc4b39557d47 (intptr,intptr,bool,int,int,int,int)
10-17 02:29:07.335 W/ ( 1288): _wapi_connect: error looking up socket handle 0x31
My Custom Renderer
public class MapContentPageRenderer: PageRenderer
{
Android.Views.View view;
private MapFragment _mapFragment;
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
var page = e.NewElement as MapContentPage;
var activity = this.Context as Activity;
view = activity.LayoutInflater.Inflate(Resource.Layout.MapLayout, this, false);
AddView(view);
InitMapFragment(activity);
ZoomToPosition(page.InitLat, page.InitLng);
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
view.Measure(msw, msh);
view.Layout(0, 0, r - l, b - t);
}
private void InitMapFragment(Activity activity)
{
_mapFragment = activity.FragmentManager.FindFragmentByTag("map") as MapFragment;
if (_mapFragment != null) return;
var mapOptions = new GoogleMapOptions()
.InvokeMapType(GoogleMap.MapTypeNormal)
.InvokeRotateGesturesEnabled(false)
.InvokeZoomControlsEnabled(false)
.InvokeCompassEnabled(true);
var fragTx = activity.FragmentManager.BeginTransaction();
_mapFragment = MapFragment.NewInstance(mapOptions);
fragTx.Add(Resource.Id.mapWithOverlay, _mapFragment, "map");
fragTx.Commit();
}
private void ZoomToPosition(double lat, double lng)
{
var latlng = new LatLng(lat, lng);
var cameraPosition = new CameraPosition.Builder().Target(latlng).Zoom(14.0f).Build();
var cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition);
_mapFragment.Map.MoveCamera(cameraUpdate);
}
}
Upvotes: 2
Views: 1462
Reputation: 2231
This solution should be what you are looking for.
You will need a static property where you can set the Bundle when your activity is created.
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
MapView oldView = (MapView)this.Control;
MapView newView = new MapView(this.Context);
newView.OnCreate(Bundle);
newView.OnResume();
this.SetNativeControl(newView);
var map = ((MapView)this.Control).Map
// other map setup goes here. Map is already set up.
ZoomToPosition(page.InitLat, page.InitLng);
}
private void ZoomToPosition(double lat, double lng)
{
var latlng = new LatLng(lat, lng);
var cameraPosition = new CameraPosition.Builder().Target(latlng).Zoom(14.0f).Build();
var cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition);
((MapView)this.Control).Map.MoveCamera(cameraUpdate);
}
Upvotes: 0
Reputation: 2231
Just to throw another option out there.
You could just use Xamarin.Forms.Maps and only use the android renderer - the work's already been done for you.
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/maps/
You can then either write your custom renderer for iOS / Win to use google maps if you wish.
Or just wrap / extend the Xamarin.Forms.Maps.Android.MapRenderer (I havnt tried this approach but I am sure with some work it can be done).
Or you may be happy to use the native maps on each platform as default.
Upvotes: 0
Reputation: 2453
Try implement the IGooglePlayServicesClientConnectionCallbacks, the OnConnect will be called when the google play services are ready.
public class LocationManager : Java.Lang.Object, IGooglePlayServicesClientConnectionCallbacks {
public LocationManager(GoogleMap map)
{
_map = map;
}
private readonly GoogleMap _map;
private void SetCenter()
{
CameraUpdate camLocation = CameraUpdateFactory.NewLatLngZoom(CurrentLatLng, Constants.DefaultZoom);
_map.MoveCamera(camLocation);
}
public void OnConnected(Bundle p0)
{
SetCenter();
}
}
Upvotes: 1
Reputation: 3240
Try using a static mapfragment instead (defined in layout). The thing is that fragTx.Commit(); is not synchronous and figuring out when it actually commits is hard.
Upvotes: 0