1

我正在Xamarin.Forms.Maps.Polyline运行时创建一个。鉴于该属性是只读的,如何动态附加位置?Polyline.Geopath

在运行时创建折线

按照文档创建折线:Xamarin.Forms Map Polygons and Polylines。此链接是代码中固定位置的教程。如何在运行时动态分配位置。

using Xamarin.Forms.Maps;
// ...
Map map = new Map
{
// ...
};

创建一个对象来存储路由数据(从json中提取的数据)

public class MapRoute
{
    //[JsonPropertyName("timestamp")]
    public long timestamp { get; set; }
    //[JsonPropertyName("lat")]
    public double lat { get; set; }
    //[JsonPropertyName("lng")]
    public double lng { get; set; }

    public MapRoute(long v1, double v2, double v3)
    {
        timestamp = v1;
        lat = v2;
        lng = v3;
    }
}

将路由对象序列化为 JsonString。

public void RouteAppend(MapRoute route)
{
    JsonString.Append(JsonSerializer.Serialize(route));
    JsonString.Append(",");
}

在现实生活中,jsonString中有2个以上的元素(jsonString中存储了1000多个元素)

readonly string jsonString = " [ {\"timestamp\": 1514172600000, \"Lat\": 37.33417925, \"Lng\": -122.04153133}, " + "{\"timestamp\": 1514172610000, \"Lat\": 37.33419725, \"Lng\": -122.04151333} ]";

JsonDocument doc;
JsonElement root;

private IList<Position> pos;

doc = Parse(testString);
root = doc.RootElement;
     
var routes = root.EnumerateArray();
while (routes.MoveNext())
{
    var user = routes.Current;
  
    pos.Add(new Position(Convert.ToDouble(user.GetProperty("lat")), Convert.ToDouble(user.GetProperty("lng"))));
           
}

最后,有一个pos包含很多 的列表Position,我将分配posGeopath。不幸的是,这是不允许的。它是一个只读属性:

// instantiate a polyline
Polyline polyline = new Polyline
{
    StrokeColor = Color.Blue,
    StrokeWidth = 12,
    Geopath = pos // It is a readonly property, cannot assign pos to Geopath
}

// add the polyline to the map's MapElements collection
map.MapElements.Add(polyline);

如何解决这个问题?

4

1 回答 1

1

WhilePolyline.Geopath是一个 get-only 属性,返回IList<Position>的不是只读集合,因此您可以Position在构造后将对象添加到其中。

因此,您可以创建以下工厂方法:

public static class PolylineFactory
{
    const string latitudeJsonName = "Lat";
    const string longitudeJsonName = "Lng";
    
    public static Polyline FromLatLngJson(string jsonString, float? strokeWidth = default, Color strokeColor = default)
    {
        using var doc = JsonDocument.Parse(jsonString); 
        var query = doc.RootElement.EnumerateArray()
            // GetProperty performs property name matching as an ordinal, case-sensitive comparison.
            .Select(e => new Position(e.GetProperty(latitudeJsonName).GetDouble(), e.GetProperty(longitudeJsonName).GetDouble()));

        var polyline = new Polyline();
        if (strokeColor != default)
            polyline.StrokeColor = strokeColor;
        if (strokeWidth != default)
            polyline.StrokeWidth = strokeWidth.Value;
        foreach (var position in query)
            polyline.Geopath.Add(position);
        
        return polyline;
    }
}

或者,在 .NET 5 中JsonSerializer支持使用单个参数化构造函数反序列化为对象,因此如果您MapRoute稍微修改您的类,如下所示,您可以直接反序列jsonString化为List<MapRoute>

public class MapRoute
{
    public long timestamp { get; set; }
    public double lat { get; set; }
    public double lng { get; set; }

    public MapRoute(long timestamp, double lat, double lng)
    {
        // The constructor argument names and property names must match for JsonSerializer to bind to the constructor successfully
        this.timestamp = timestamp;
        this.lat = lat;
        this.lng = lng;
    }
}

public static class PolylineFactory
{
    public static Polyline FromLatLngJson(string jsonString, float? strokeWidth = default, Color strokeColor = default)
    {
        var routes = JsonSerializer.Deserialize<List<MapRoute>>(jsonString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        
        var polyline = new Polyline();
        if (strokeColor != default)
            polyline.StrokeColor = strokeColor;
        if (strokeWidth != default)
            polyline.StrokeWidth = strokeWidth.Value;
        foreach (var route in routes)
            polyline.Geopath.Add(new Position(route.lat, route.lng));
        
        return polyline;
    }
}

(在 .NET Core 3.x 中,您需要添加一个无参数构造函数MapRoute并确保所有属性都是可变的才能成功反序列化。)

无论哪种方式,您都可以按如下方式调用工厂方法:

var polyline = PolylineFactory.FromLatLngJson(jsonString, 12, Color.Blue);

笔记:

  • JsonElement.GetProperty()将属性名称匹配作为序数、区分大小写的比较执行,因此您需要传递"Lat"and"Lng"而不是"lat"and "lng",因为您jsonString是帕斯卡大小写而不是骆驼大小写。

    如果这是您的问题中的一个错误,并且您的 JSON 字符串确实是驼峰式大小写,请latitudeJsonName进行longitudeJsonName适当的修改。

    如果您在获取属性时需要忽略大小写,请参阅使用 TryGetProperty 时忽略大小写

  • Convert.ToDouble()通过使用当前线程区域性的格式约定来解释传入的值。 例如,在许多欧洲语言环境,中,格式化浮点值时使用逗号作为小数分隔符。JsonElement.GetDouble()由于这与 JSON 标准不一致,因此最好使用始终使用正确的小数分隔符的内置方法。

  • JsonDocument是一次性的:

    此类利用池内存中的资源来最大程度地减少垃圾收集器 (GC) 在高使用情况下的影响。未能正确处置此对象将导致内存无法返回到池中,这将增加对框架各个部分的 GC 影响。

    在您的代码中,您不会处理您的doc,但您应该处理。

Demo fiddle #1这里使用JsonDocument,#2这里使用MapRoute.

于 2021-03-05T16:57:18.387 回答