Xamarin Forms Maps - 如何 refresh/update 地图 - CustomMap Renderer
Xamarin Forms Maps - how to refresh/update the map - CustomMap Renderer
如果您正在搜索完整的折线、图钉、图块、UIOptions(以及即将推出的 3D 效果)renderings/implementations,您应该在我制作的 public github在 XamarinByEmixam23/..../Map.
我搜索了很多,还是有同样的问题:
如何更新、刷新或重新加载 Xamarin.Forms.Maps?
在class定义中(class CustomMap : Map),没有更新地图的方法。也许MVVM逻辑可以解决问题,但我找不到在网络上..
我遵循了地图教程:Working with maps
要自定义它,我遵循了本教程:Highlight a Route on a Map
所以,在这些教程之后(我做了同样的事情,没有改变),我尝试了 2 个 RouteCoordinates,它给了我一条直线...然后我做了一个完美的算法。
方向图
public class DirectionMap
{
public Distance distance { get; set; }
public Duration duration { get; set; }
public Address address_start { get; set; }
public Address address_end { get; set; }
public List<Step> steps { get; set; }
public class Distance
{
public string text { get; set; }
public int value { get; set; }
}
public class Duration
{
public string text { get; set; }
public int value { get; set; }
}
public class Address
{
public string text { get; set; }
public Position position { get; set; }
}
public class Step
{
public Position start { get; set; }
public Position end { get; set; }
}
}
ResponseHttpParser
public static void parseDirectionGoogleMapsResponse(HttpStatusCode httpStatusCode, JObject json, Action<DirectionMap, string> callback)
{
switch (httpStatusCode)
{
case HttpStatusCode.OK:
DirectionMap directionMap = null;
string strException = null;
try
{
directionMap = new DirectionMap()
{
distance = new DirectionMap.Distance()
{
text = (json["routes"][0]["legs"][0]["distance"]["text"]).ToString(),
value = Int32.Parse((json["routes"][0]["legs"][0]["distance"]["value"]).ToString())
},
duration = new DirectionMap.Duration()
{
text = (json["routes"][0]["legs"][0]["duration"]["text"]).ToString(),
value = Int32.Parse((json["routes"][0]["legs"][0]["duration"]["value"]).ToString())
},
address_start = new DirectionMap.Address()
{
text = (json["routes"][0]["legs"][0]["start_address"]).ToString(),
position = new Position(Double.Parse((json["routes"][0]["legs"][0]["start_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["start_location"]["lng"]).ToString()))
},
address_end = new DirectionMap.Address()
{
text = (json["routes"][0]["legs"][0]["end_address"]).ToString(),
position = new Position(Double.Parse((json["routes"][0]["legs"][0]["end_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["end_location"]["lng"]).ToString()))
}
};
bool finished = false;
directionMap.steps = new List<Step>();
int index = 0;
while (!finished)
{
try
{
Step step = new Step()
{
start = new Position(Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["start_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["start_location"]["lng"]).ToString())),
end = new Position(Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["end_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["end_location"]["lng"]).ToString()))
};
directionMap.steps.Add(step);
index++;
}
catch (Exception e)
{
finished = true;
}
}
}
catch (Exception e)
{
directionMap = null;
strException = e.ToString();
}
finally
{
callback(directionMap, strException);
}
break;
default:
switch (httpStatusCode)
{
}
callback(null, json.ToString());
break;
}
}
我只是得到一些私人计算的距离和持续时间,并得到我放入列表<>的每一步;
当一切都完成后,我使用我的回调将我们带回控制器(MapPage.xaml.cs XAML 表单页面(Xamarin 便携式))
现在,一切都变得奇怪了。就像地图没有得到所做的更改
public partial class MapPage : ContentPage
{
public MapPage()
{
InitializeComponent();
setupMap();
setupMapCustom();
}
public void setupMapCustom()
{
customMap.RouteCoordinates.Add(new Position(37.785559, -122.396728));
customMap.RouteCoordinates.Add(new Position(37.780624, -122.390541));
customMap.RouteCoordinates.Add(new Position(37.777113, -122.394983));
customMap.RouteCoordinates.Add(new Position(37.776831, -122.394627));
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(37.79752, -122.40183), Xamarin.Forms.Maps.Distance.FromMiles(1.0)));
}
public async void setupMap()
{
customMap.MapType = MapType.Satellite;
string origin = "72100 Le Mans";
string destination = "75000 Paris";
HttpRequest.getDirections(origin, destination, callbackDirections);
customMap.RouteCoordinates.Add(await MapUtilities.GetMapPointOfStreetAddress(origin));
Position position = await MapUtilities.GetMapPointOfStreetAddress(destination);
//customMap.RouteCoordinates.Add(position);
var pin = new Pin
{
Type = PinType.Place,
Position = position,
Label = "Destination !!",
};
customMap.Pins.Add(pin);
}
private async void callbackDirections(Object obj, string str)
{
if (obj != null)
{
DirectionMap directionMap = obj as DirectionMap;
foreach (Step step in directionMap.steps)
{
customMap.RouteCoordinates.Add(step.start);
System.Diagnostics.Debug.WriteLine("add step");
}
customMap.RouteCoordinates.Add(directionMap.address_end.position);
System.Diagnostics.Debug.WriteLine("add last step");
}
else
{
System.Diagnostics.Debug.WriteLine(str);
}
}
}
我 运行 我的应用程序,一切正常,直到速度很快,因为我的算法等花费了时间,回调来得太晚,然后我需要刷新、重新加载或更新我的地图。 ..反正我以后需要更新我的地图,所以...如果有人能帮忙,欢迎这个!
编辑 1
我查看了您的回答(非常感谢!;))但它不起作用:/
我像你一样更新了 CustomMap
public class CustomMap : Map
{
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(p => p.RouteCoordinates, new List<Position>());
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap()
{
RouteCoordinates = new List<Position>();
}
}
与 CustomMapRenderer (Droid)
相同
public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
{
GoogleMap map;
Polyline polyline;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
{
UpdatePolyLine();
}
}
private void UpdatePolyLine()
{
if (polyline != null)
{
polyline.Remove();
polyline.Dispose();
}
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeColor(0x66FF0000);
foreach (var position in ((CustomMap)this.Element).RouteCoordinates)
{
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
}
polyline = map.AddPolyline(polylineOptions);
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
UpdatePolyLine();
}
}
因此,对于最后一个更改,在我的 MapPage.xaml.cs 中,我按照您的解释对 callbackDirections 进行了更改(我希望我做得很好)
private async void callbackDirections(Object obj, string str)
{
if (obj != null)
{
Device.BeginInvokeOnMainThread(() =>
{
DirectionMap directionMap = obj as DirectionMap;
var list = new List<Position>(customMap.RouteCoordinates);
foreach (Step step in directionMap.steps)
{
list.Add(directionMap.address_end.position);
System.Diagnostics.Debug.WriteLine("add step");
}
System.Diagnostics.Debug.WriteLine("last step");
customMap.RouteCoordinates = list;
System.Diagnostics.Debug.WriteLine("finished?");
});
}
else
{
System.Diagnostics.Debug.WriteLine(str);
}
}
地图仍然没有显示多段线:/ 我只做了这些更改,我没有更改以前代码的任何其他内容。
我没有告诉你,但我不是 MVVM 绑定方面的专家,所以如果我忘记了什么,我很抱歉:/
编辑 2
所以在你的回答和一些阅读,阅读和重新阅读你的答案之后,MapPage.xaml.cs
中有我的 "test code"
public MapPage()
{
InitializeComponent();
//HttpRequest.getDirections(origin, destination, callbackDirections);
Device.BeginInvokeOnMainThread(() =>
{
customMap.RouteCoordinates = new List<Position>
{
new Position (37.797534, -122.401827),
new Position (37.776831, -122.394627)
};
});
//setupMap();
//setupMapCustom();
}
因为它不起作用(对我来说),我查看了我的代码,然后我发现 public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(
p => p.RouteCoordinates, new List<Position>());
已被弃用..
所以我对此很感兴趣 post a different way to implement this binding, but it also said that this way is deprecated SEE HERE...我还看到一些关于绑定的教程,其中说他们将一些代码放入 xaml,让我记住你的
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NAMESPACE;assembly=NAMESPACE"
x:Class="NAMESPACE.Controlers.MapPage">
<ContentPage.Content>
<local:CustomMap x:Name="customMap"/>
</ContentPage.Content>
</ContentPage>
我没有使用 ItemSource="{PolylineBindable}"
示例中的自定义渲染器不是为动态更新路径而设计的。它只是针对这种情况实施的,在第一次初始化地图/绘制路径之前,路径的所有点都是已知的。所以你有这种竞争条件,你 运行 进入,因为你正在从网络服务加载方向。
所以你必须做一些改变:
RouteCoordinates 必须是 BindableProperty
public class CustomMap : Map
{
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(p => p.RouteCoordinates, new List<Position>());
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap ()
{
RouteCoordinates = new List<Position>();
}
}
每当坐标改变时更新折线
- 将折线的创建从
OnMapReady
移动到 UpdatePolyLine
- 从
OnMapReady
和 OnElementPropertyChanged
调用 UpdatePolyLine
public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
{
GoogleMap map;
Polyline polyline;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
{
UpdatePolyLine();
}
}
private void UpdatePolyLine()
{
if (polyline != null)
{
polyline.Remove();
polyline.Dispose();
}
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeColor(0x66FF0000);
foreach (var position in ((CustomMap)this.Element).RouteCoordinates)
{
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
}
polyline = map.AddPolyline(polylineOptions);
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
UpdatePolyLine();
}
}
设置数据
更新位置有点变化。您不必将位置添加到现有列表,而必须(创建一个新列表)并将其设置为 RouteCoordinates
。您可以使用 Device.BeginInvokeOnMainThread
来确保操作在 UI 线程上执行。否则折线将不会更新。
Device.BeginInvokeOnMainThread(() =>
{
customMap.RouteCoordinates = new List<Position>
{
new Position (37.797534, -122.401827),
new Position (37.776831, -122.394627)
};
})
你的情况类似于
var list = new List<Position>(customMap.RouteCoordinates);
list.Add(directionMap.address_end.position);
customMap.RouteCoordinates = list;
待办事项
在 iOS 上,您现在必须实现类似的行为(如 UpdatePolyLine
)
备注
这可能不是最高效的实现,因为您重绘了所有内容而不是添加一个点。 但没关系 只要您没有性能问题:)
因此,经过大量搜索,当然还有 @Sven-Michael Stübe 的回答,您可以拥有适合每个平台的地图 "Android, iOS, WinPhone"。按照我的代码,然后按照@Sven-Michael Stübe 的回答对其进行编辑。
一旦你完成了所有的事情,它就可以工作了(比如@Sven-Michael Stübe),但它也不能工作(比如我)。如果还是不行,尝试更改以下代码:
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(
p => p.RouteCoordinates, new List<Position>());
来自
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create(nameof(RouteCoordinates), typeof(List<Position>), typeof(CustomMap), new List<Position>(), BindingMode.TwoWay);
See the documentation for more information about it. (Deprecated implementation)
那么代码就可以工作了!
PS:最后的折线可能会有些麻烦,它没有正确地沿着道路行驶,我正在努力。
PS2:我还会制作一个视频来解释如何编写您的 customMap 以不必安装 NuGet 包,以便能够在最后编辑所有内容! (第一个是法语的,第二个是英语的,这个post会在制作视频的时候编辑)
再次感谢@Sven-Michael Stübe !!也感谢他的回答:)
基于这些答案,以下是我为使其在 iOS 上运行所做的工作。与 Xamarin 示例不同,这允许在加载地图后更改路线。
首先,根据 @Sven-Michael Stübe 的自定义地图 class 以及来自 @Emixam23 的更新:
public class CustomMap : Map
{
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create(nameof(RouteCoordinates), typeof(List<Position>), typeof(CustomMap), new List<Position>(), BindingMode.TwoWay);
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap()
{
RouteCoordinates = new List<Position>();
}
}
接下来,iOS 自定义渲染器:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace KZNTR.iOS
{
public class CustomMapRenderer : MapRenderer
{
MKPolylineRenderer polylineRenderer;
CustomMap map;
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if ((this.Element == null) || (this.Control == null))
return;
if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
{
map = (CustomMap)sender;
UpdatePolyLine();
}
}
[Foundation.Export("mapView:rendererForOverlay:")]
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlay)
{
if (polylineRenderer == null)
{
var o = ObjCRuntime.Runtime.GetNSObject(overlay.Handle) as MKPolyline;
polylineRenderer = new MKPolylineRenderer(o);
//polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline);
polylineRenderer.FillColor = UIColor.Blue;
polylineRenderer.StrokeColor = UIColor.Red;
polylineRenderer.LineWidth = 3;
polylineRenderer.Alpha = 0.4f;
}
return polylineRenderer;
}
private void UpdatePolyLine()
{
var nativeMap = Control as MKMapView;
nativeMap.OverlayRenderer = GetOverlayRenderer;
CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[map.RouteCoordinates.Count];
int index = 0;
foreach (var position in map.RouteCoordinates)
{
coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
index++;
}
var routeOverlay = MKPolyline.FromCoordinates(coords);
nativeMap.AddOverlay(routeOverlay);
}
}
}
最后,向地图添加折线:
Device.BeginInvokeOnMainThread(() =>
{
customMap.RouteCoordinates.Clear();
var plist = new List<Position>(customMap.RouteCoordinates);
foreach (var point in track.TrackPoints)
{
plist.Add(new Position(double.Parse(point.Latitude, CultureInfo.InvariantCulture), double.Parse(point.Longitude, CultureInfo.InvariantCulture)));
}
customMap.RouteCoordinates = plist;
var firstpoint = (from pt in track.TrackPoints select pt).FirstOrDefault();
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(double.Parse(firstpoint.Latitude, CultureInfo.InvariantCulture), double.Parse(firstpoint.Longitude, CultureInfo.InvariantCulture)), Distance.FromMiles(3.0)));
});
不确定这是最好的方法还是最有效的方法,我对渲染器了解不多,但它似乎确实有效。
我关注了tutorial available on Xamarin Docs and it worked for me with some changes based on
我从 WebService 加载坐标,然后创建一个单独的列表,在此之后,我将新列表设置为自定义地图上的 RouteCoordinates 属性。
对 Android 渲染器进行了一些更改
我正在使用 MVVM。
自定义地图Class:
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create(nameof(RouteCoordinates), typeof(List<Position>), typeof(CustomMap), new List<Position>(), BindingMode.TwoWay);
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap()
{
RouteCoordinates = new List<Position>();
}
ViewModel(代码隐藏,在你的情况下):
private async void LoadCoordinates(string oidAula, CustomMap mapa)
{
IsBusy = true;
var percurso = await ComunicacaoServidor.GetPercurso(oidAula); // Get coordinates from WebService
var pontos = percurso.Select(p => new Position(p.Latitude, p.Longitude)).ToList(); // Create coordinates list from webservice result
var latitudeMedia = percurso[percurso.Count / 2].Latitude;
var longitudeMedia = percurso[percurso.Count / 2].Longitude;
mapa.RouteCoordinates = pontos;
mapa.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(latitudeMedia, longitudeMedia), Distance.FromMiles(1.0)));
IsBusy = false;
}
XAML:
<maps:CustomMap
AbsoluteLayout.LayoutFlags = "All"
AbsoluteLayout.LayoutBounds = "0, 0, 1, 1"
VerticalOptions = "FillAndExpand"
HorizontalOptions = "FillAndExpand"
x:Name = "PercursoMapa" />
Android 渲染器:
public class CustomMapRenderer : MapRenderer
{
bool isDrawn;
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
Control.GetMapAsync(this);
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if ((e.PropertyName == "RouteCoordinates" || e.PropertyName == "VisibleRegion") && !isDrawn)
{
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeColor(0x66FF0000);
var coordinates = ((CustomMap)Element).RouteCoordinates;
foreach (var position in coordinates)
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
NativeMap.AddPolyline(polylineOptions);
isDrawn = coordinates.Count > 0;
}
}
}
此示例有超过 3600 个位置点,折线在设备上正确显示:
Screenshot
如果您正在搜索完整的折线、图钉、图块、UIOptions(以及即将推出的 3D 效果)renderings/implementations,您应该在我制作的 public github在 XamarinByEmixam23/..../Map.
我搜索了很多,还是有同样的问题:
如何更新、刷新或重新加载 Xamarin.Forms.Maps?
在class定义中(class CustomMap : Map),没有更新地图的方法。也许MVVM逻辑可以解决问题,但我找不到在网络上..
我遵循了地图教程:Working with maps
要自定义它,我遵循了本教程:Highlight a Route on a Map
所以,在这些教程之后(我做了同样的事情,没有改变),我尝试了 2 个 RouteCoordinates,它给了我一条直线...然后我做了一个完美的算法。
方向图
public class DirectionMap
{
public Distance distance { get; set; }
public Duration duration { get; set; }
public Address address_start { get; set; }
public Address address_end { get; set; }
public List<Step> steps { get; set; }
public class Distance
{
public string text { get; set; }
public int value { get; set; }
}
public class Duration
{
public string text { get; set; }
public int value { get; set; }
}
public class Address
{
public string text { get; set; }
public Position position { get; set; }
}
public class Step
{
public Position start { get; set; }
public Position end { get; set; }
}
}
ResponseHttpParser
public static void parseDirectionGoogleMapsResponse(HttpStatusCode httpStatusCode, JObject json, Action<DirectionMap, string> callback)
{
switch (httpStatusCode)
{
case HttpStatusCode.OK:
DirectionMap directionMap = null;
string strException = null;
try
{
directionMap = new DirectionMap()
{
distance = new DirectionMap.Distance()
{
text = (json["routes"][0]["legs"][0]["distance"]["text"]).ToString(),
value = Int32.Parse((json["routes"][0]["legs"][0]["distance"]["value"]).ToString())
},
duration = new DirectionMap.Duration()
{
text = (json["routes"][0]["legs"][0]["duration"]["text"]).ToString(),
value = Int32.Parse((json["routes"][0]["legs"][0]["duration"]["value"]).ToString())
},
address_start = new DirectionMap.Address()
{
text = (json["routes"][0]["legs"][0]["start_address"]).ToString(),
position = new Position(Double.Parse((json["routes"][0]["legs"][0]["start_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["start_location"]["lng"]).ToString()))
},
address_end = new DirectionMap.Address()
{
text = (json["routes"][0]["legs"][0]["end_address"]).ToString(),
position = new Position(Double.Parse((json["routes"][0]["legs"][0]["end_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["end_location"]["lng"]).ToString()))
}
};
bool finished = false;
directionMap.steps = new List<Step>();
int index = 0;
while (!finished)
{
try
{
Step step = new Step()
{
start = new Position(Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["start_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["start_location"]["lng"]).ToString())),
end = new Position(Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["end_location"]["lat"]).ToString()), Double.Parse((json["routes"][0]["legs"][0]["steps"][index]["end_location"]["lng"]).ToString()))
};
directionMap.steps.Add(step);
index++;
}
catch (Exception e)
{
finished = true;
}
}
}
catch (Exception e)
{
directionMap = null;
strException = e.ToString();
}
finally
{
callback(directionMap, strException);
}
break;
default:
switch (httpStatusCode)
{
}
callback(null, json.ToString());
break;
}
}
我只是得到一些私人计算的距离和持续时间,并得到我放入列表<>的每一步;
当一切都完成后,我使用我的回调将我们带回控制器(MapPage.xaml.cs XAML 表单页面(Xamarin 便携式))
现在,一切都变得奇怪了。就像地图没有得到所做的更改
public partial class MapPage : ContentPage
{
public MapPage()
{
InitializeComponent();
setupMap();
setupMapCustom();
}
public void setupMapCustom()
{
customMap.RouteCoordinates.Add(new Position(37.785559, -122.396728));
customMap.RouteCoordinates.Add(new Position(37.780624, -122.390541));
customMap.RouteCoordinates.Add(new Position(37.777113, -122.394983));
customMap.RouteCoordinates.Add(new Position(37.776831, -122.394627));
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(37.79752, -122.40183), Xamarin.Forms.Maps.Distance.FromMiles(1.0)));
}
public async void setupMap()
{
customMap.MapType = MapType.Satellite;
string origin = "72100 Le Mans";
string destination = "75000 Paris";
HttpRequest.getDirections(origin, destination, callbackDirections);
customMap.RouteCoordinates.Add(await MapUtilities.GetMapPointOfStreetAddress(origin));
Position position = await MapUtilities.GetMapPointOfStreetAddress(destination);
//customMap.RouteCoordinates.Add(position);
var pin = new Pin
{
Type = PinType.Place,
Position = position,
Label = "Destination !!",
};
customMap.Pins.Add(pin);
}
private async void callbackDirections(Object obj, string str)
{
if (obj != null)
{
DirectionMap directionMap = obj as DirectionMap;
foreach (Step step in directionMap.steps)
{
customMap.RouteCoordinates.Add(step.start);
System.Diagnostics.Debug.WriteLine("add step");
}
customMap.RouteCoordinates.Add(directionMap.address_end.position);
System.Diagnostics.Debug.WriteLine("add last step");
}
else
{
System.Diagnostics.Debug.WriteLine(str);
}
}
}
我 运行 我的应用程序,一切正常,直到速度很快,因为我的算法等花费了时间,回调来得太晚,然后我需要刷新、重新加载或更新我的地图。 ..反正我以后需要更新我的地图,所以...如果有人能帮忙,欢迎这个!
编辑 1 我查看了您的回答(非常感谢!;))但它不起作用:/
我像你一样更新了 CustomMap
public class CustomMap : Map
{
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(p => p.RouteCoordinates, new List<Position>());
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap()
{
RouteCoordinates = new List<Position>();
}
}
与 CustomMapRenderer (Droid)
相同public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
{
GoogleMap map;
Polyline polyline;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
{
UpdatePolyLine();
}
}
private void UpdatePolyLine()
{
if (polyline != null)
{
polyline.Remove();
polyline.Dispose();
}
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeColor(0x66FF0000);
foreach (var position in ((CustomMap)this.Element).RouteCoordinates)
{
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
}
polyline = map.AddPolyline(polylineOptions);
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
UpdatePolyLine();
}
}
因此,对于最后一个更改,在我的 MapPage.xaml.cs 中,我按照您的解释对 callbackDirections 进行了更改(我希望我做得很好)
private async void callbackDirections(Object obj, string str)
{
if (obj != null)
{
Device.BeginInvokeOnMainThread(() =>
{
DirectionMap directionMap = obj as DirectionMap;
var list = new List<Position>(customMap.RouteCoordinates);
foreach (Step step in directionMap.steps)
{
list.Add(directionMap.address_end.position);
System.Diagnostics.Debug.WriteLine("add step");
}
System.Diagnostics.Debug.WriteLine("last step");
customMap.RouteCoordinates = list;
System.Diagnostics.Debug.WriteLine("finished?");
});
}
else
{
System.Diagnostics.Debug.WriteLine(str);
}
}
地图仍然没有显示多段线:/ 我只做了这些更改,我没有更改以前代码的任何其他内容。
我没有告诉你,但我不是 MVVM 绑定方面的专家,所以如果我忘记了什么,我很抱歉:/
编辑 2 所以在你的回答和一些阅读,阅读和重新阅读你的答案之后,MapPage.xaml.cs
中有我的 "test code"public MapPage()
{
InitializeComponent();
//HttpRequest.getDirections(origin, destination, callbackDirections);
Device.BeginInvokeOnMainThread(() =>
{
customMap.RouteCoordinates = new List<Position>
{
new Position (37.797534, -122.401827),
new Position (37.776831, -122.394627)
};
});
//setupMap();
//setupMapCustom();
}
因为它不起作用(对我来说),我查看了我的代码,然后我发现 public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(
p => p.RouteCoordinates, new List<Position>());
已被弃用..
所以我对此很感兴趣 post a different way to implement this binding, but it also said that this way is deprecated SEE HERE...我还看到一些关于绑定的教程,其中说他们将一些代码放入 xaml,让我记住你的
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NAMESPACE;assembly=NAMESPACE"
x:Class="NAMESPACE.Controlers.MapPage">
<ContentPage.Content>
<local:CustomMap x:Name="customMap"/>
</ContentPage.Content>
</ContentPage>
我没有使用 ItemSource="{PolylineBindable}"
示例中的自定义渲染器不是为动态更新路径而设计的。它只是针对这种情况实施的,在第一次初始化地图/绘制路径之前,路径的所有点都是已知的。所以你有这种竞争条件,你 运行 进入,因为你正在从网络服务加载方向。
所以你必须做一些改变:
RouteCoordinates 必须是 BindableProperty
public class CustomMap : Map
{
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(p => p.RouteCoordinates, new List<Position>());
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap ()
{
RouteCoordinates = new List<Position>();
}
}
每当坐标改变时更新折线
- 将折线的创建从
OnMapReady
移动到UpdatePolyLine
- 从
OnMapReady
和OnElementPropertyChanged
调用
UpdatePolyLine
public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
{
GoogleMap map;
Polyline polyline;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
{
UpdatePolyLine();
}
}
private void UpdatePolyLine()
{
if (polyline != null)
{
polyline.Remove();
polyline.Dispose();
}
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeColor(0x66FF0000);
foreach (var position in ((CustomMap)this.Element).RouteCoordinates)
{
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
}
polyline = map.AddPolyline(polylineOptions);
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
UpdatePolyLine();
}
}
设置数据
更新位置有点变化。您不必将位置添加到现有列表,而必须(创建一个新列表)并将其设置为 RouteCoordinates
。您可以使用 Device.BeginInvokeOnMainThread
来确保操作在 UI 线程上执行。否则折线将不会更新。
Device.BeginInvokeOnMainThread(() =>
{
customMap.RouteCoordinates = new List<Position>
{
new Position (37.797534, -122.401827),
new Position (37.776831, -122.394627)
};
})
你的情况类似于
var list = new List<Position>(customMap.RouteCoordinates);
list.Add(directionMap.address_end.position);
customMap.RouteCoordinates = list;
待办事项
在 iOS 上,您现在必须实现类似的行为(如 UpdatePolyLine
)
备注
这可能不是最高效的实现,因为您重绘了所有内容而不是添加一个点。 但没关系 只要您没有性能问题:)
因此,经过大量搜索,当然还有 @Sven-Michael Stübe 的回答,您可以拥有适合每个平台的地图 "Android, iOS, WinPhone"。按照我的代码,然后按照@Sven-Michael Stübe 的回答对其进行编辑。
一旦你完成了所有的事情,它就可以工作了(比如@Sven-Michael Stübe),但它也不能工作(比如我)。如果还是不行,尝试更改以下代码:
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(
p => p.RouteCoordinates, new List<Position>());
来自
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create(nameof(RouteCoordinates), typeof(List<Position>), typeof(CustomMap), new List<Position>(), BindingMode.TwoWay);
See the documentation for more information about it. (Deprecated implementation)
那么代码就可以工作了!
PS:最后的折线可能会有些麻烦,它没有正确地沿着道路行驶,我正在努力。
PS2:我还会制作一个视频来解释如何编写您的 customMap 以不必安装 NuGet 包,以便能够在最后编辑所有内容! (第一个是法语的,第二个是英语的,这个post会在制作视频的时候编辑)
再次感谢@Sven-Michael Stübe !!也感谢他的回答:)
基于这些答案,以下是我为使其在 iOS 上运行所做的工作。与 Xamarin 示例不同,这允许在加载地图后更改路线。
首先,根据 @Sven-Michael Stübe 的自定义地图 class 以及来自 @Emixam23 的更新:
public class CustomMap : Map
{
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create(nameof(RouteCoordinates), typeof(List<Position>), typeof(CustomMap), new List<Position>(), BindingMode.TwoWay);
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap()
{
RouteCoordinates = new List<Position>();
}
}
接下来,iOS 自定义渲染器:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace KZNTR.iOS
{
public class CustomMapRenderer : MapRenderer
{
MKPolylineRenderer polylineRenderer;
CustomMap map;
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if ((this.Element == null) || (this.Control == null))
return;
if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
{
map = (CustomMap)sender;
UpdatePolyLine();
}
}
[Foundation.Export("mapView:rendererForOverlay:")]
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlay)
{
if (polylineRenderer == null)
{
var o = ObjCRuntime.Runtime.GetNSObject(overlay.Handle) as MKPolyline;
polylineRenderer = new MKPolylineRenderer(o);
//polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline);
polylineRenderer.FillColor = UIColor.Blue;
polylineRenderer.StrokeColor = UIColor.Red;
polylineRenderer.LineWidth = 3;
polylineRenderer.Alpha = 0.4f;
}
return polylineRenderer;
}
private void UpdatePolyLine()
{
var nativeMap = Control as MKMapView;
nativeMap.OverlayRenderer = GetOverlayRenderer;
CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[map.RouteCoordinates.Count];
int index = 0;
foreach (var position in map.RouteCoordinates)
{
coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
index++;
}
var routeOverlay = MKPolyline.FromCoordinates(coords);
nativeMap.AddOverlay(routeOverlay);
}
}
}
最后,向地图添加折线:
Device.BeginInvokeOnMainThread(() =>
{
customMap.RouteCoordinates.Clear();
var plist = new List<Position>(customMap.RouteCoordinates);
foreach (var point in track.TrackPoints)
{
plist.Add(new Position(double.Parse(point.Latitude, CultureInfo.InvariantCulture), double.Parse(point.Longitude, CultureInfo.InvariantCulture)));
}
customMap.RouteCoordinates = plist;
var firstpoint = (from pt in track.TrackPoints select pt).FirstOrDefault();
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(double.Parse(firstpoint.Latitude, CultureInfo.InvariantCulture), double.Parse(firstpoint.Longitude, CultureInfo.InvariantCulture)), Distance.FromMiles(3.0)));
});
不确定这是最好的方法还是最有效的方法,我对渲染器了解不多,但它似乎确实有效。
我关注了tutorial available on Xamarin Docs and it worked for me with some changes based on
我从 WebService 加载坐标,然后创建一个单独的列表,在此之后,我将新列表设置为自定义地图上的 RouteCoordinates 属性。
对 Android 渲染器进行了一些更改
我正在使用 MVVM。
自定义地图Class:
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create(nameof(RouteCoordinates), typeof(List<Position>), typeof(CustomMap), new List<Position>(), BindingMode.TwoWay);
public List<Position> RouteCoordinates
{
get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
set { SetValue(RouteCoordinatesProperty, value); }
}
public CustomMap()
{
RouteCoordinates = new List<Position>();
}
ViewModel(代码隐藏,在你的情况下):
private async void LoadCoordinates(string oidAula, CustomMap mapa)
{
IsBusy = true;
var percurso = await ComunicacaoServidor.GetPercurso(oidAula); // Get coordinates from WebService
var pontos = percurso.Select(p => new Position(p.Latitude, p.Longitude)).ToList(); // Create coordinates list from webservice result
var latitudeMedia = percurso[percurso.Count / 2].Latitude;
var longitudeMedia = percurso[percurso.Count / 2].Longitude;
mapa.RouteCoordinates = pontos;
mapa.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(latitudeMedia, longitudeMedia), Distance.FromMiles(1.0)));
IsBusy = false;
}
XAML:
<maps:CustomMap
AbsoluteLayout.LayoutFlags = "All"
AbsoluteLayout.LayoutBounds = "0, 0, 1, 1"
VerticalOptions = "FillAndExpand"
HorizontalOptions = "FillAndExpand"
x:Name = "PercursoMapa" />
Android 渲染器:
public class CustomMapRenderer : MapRenderer
{
bool isDrawn;
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
Control.GetMapAsync(this);
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if ((e.PropertyName == "RouteCoordinates" || e.PropertyName == "VisibleRegion") && !isDrawn)
{
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeColor(0x66FF0000);
var coordinates = ((CustomMap)Element).RouteCoordinates;
foreach (var position in coordinates)
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
NativeMap.AddPolyline(polylineOptions);
isDrawn = coordinates.Count > 0;
}
}
}
此示例有超过 3600 个位置点,折线在设备上正确显示:
Screenshot