Xamarin 谷歌地图自动完成搜索

4

我正在尝试向我的自定义搜索栏(使用的是Entry)添加自动完成服务,用于Xamarin Forms地图,但我还没有成功。

我已经尝试过使用XLabs,但无法实现,并且到目前为止我还没有找到其他解决方案。

Xamarin.FormsMaps是否包含任何自动完成功能? 或者Xamarin中是否已经有了任何自动完成功能,我可能错过了?

您有什么建议? 如果您需要任何代码或其他内容,请告诉我。


可能是在Xamarin.Forms中使用Google地点自动完成的重复问题。 - hvaughan3
@hvaughan3 我已经看过那个了,但它只适用于安卓,这就是为什么那个问题没有被标记答案的原因。不过还是谢谢你。 - Mauricio Cárdenas
1个回答

6
这是一篇较长的文章,但下面开始翻译。显然您希望将服务调用代码放在 class 服务中,将所有其他代码放在您的 ViewModel 中。您也可能想要在服务调用方法中添加更多的空值和错误检查。
我遗漏的一个重要部分是谷歌要求在进行搜索时显示谷歌标志。因此,请下载 他们的图标 并将其添加到 UI 中。只有当用户聚焦于 Entry 时才会显示它。
所以这些就是 Places 模型和我的 Address 模型。
public class AddressInfo {

    public string Address { get; set; }

    public string City { get; set; }

    public string State { get; set; }

    public string ZipCode { get; set; }

    public double Longitude { get; set; }

    public double Latitude { get; set; }
}

public class PlacesMatchedSubstring {

    [Newtonsoft.Json.JsonProperty("length")]
    public int Length { get; set; }

    [Newtonsoft.Json.JsonProperty("offset")]
    public int Offset { get; set; }
}

public class PlacesTerm {

    [Newtonsoft.Json.JsonProperty("offset")]
    public int Offset { get; set; }

    [Newtonsoft.Json.JsonProperty("value")]
    public string Value { get; set; }
}

public class Prediction {

    [Newtonsoft.Json.JsonProperty("id")]
    public string Id { get; set; }

    [Newtonsoft.Json.JsonProperty("description")]
    public string Description { get; set; }

    [Newtonsoft.Json.JsonProperty("matched_substrings")]
    public List<PlacesMatchedSubstring> MatchedSubstrings { get; set; }

    [Newtonsoft.Json.JsonProperty("place_id")]
    public string PlaceId { get; set; }

    [Newtonsoft.Json.JsonProperty("reference")]
    public string Reference { get; set; }

    [Newtonsoft.Json.JsonProperty("terms")]
    public List<PlacesTerm> Terms { get; set; }

    [Newtonsoft.Json.JsonProperty("types")]
    public List<string> Types { get; set; }
}

public class PlacesLocationPredictions {

    [Newtonsoft.Json.JsonProperty("predictions")]
    public List<Prediction> Predictions { get; set; }

    [Newtonsoft.Json.JsonProperty("status")]
    public string Status { get; set; }
}

这里我们调用地点API:

public const string GooglePlacesApiAutoCompletePath = "https://maps.googleapis.com/maps/api/place/autocomplete/json?key={0}&input={1}&components=country:us"; //Adding country:us limits results to us

public const string GooglePlacesApiKey = "bTafrOPmO4LpPgAl34r5wQ6LFRWhgTxBW80-3GK";

private static HttpClient _httpClientInstance;
public static HttpClient HttpClientInstance => _httpClientInstance ?? (_httpClientInstance = new HttpClient());

private ObservableCollection<AddressInfo> _addresses;
public  ObservableCollection<AddressInfo> Addresses {
    get => _addresses ?? (_addresses = new ObservableCollection<AddressInfo>());
    set {
        if(_addresses != value) {
            _addresses = value;
            OnPropertyChanged();
        }
    };
}

private string _addressText;
public  string AddressText {
    get => _addressText;
    set {
        if(_addressText != value) {
            _addressText = value;
            OnPropertyChanged();
        }
    };
}

public async Task GetPlacesPredictionsAsync() {

    // TODO: Add throttle logic, Google begins denying requests if too many are made in a short amount of time

    CancellationToken cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token;

    using(HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, string.Format(GooglePlacesApiAutoCompletePath, ConstantKeys.GooglePlacesApiKey, WebUtility.UrlEncode(_addressText)))) { //Be sure to UrlEncode the search term they enter

        using(HttpResponseMessage message = await HttpClientInstance.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false)) {
            if(message.IsSuccessStatusCode) {
                string json = await message.Content.ReadAsStringAsync().ConfigureAwait(false);

                PlacesLocationPredictions predictionList = await Task.Run(() => JsonConvert.DeserializeObject<PlacesLocationPredictions>(json)).ConfigureAwait(false);

                if(predictionList.Status == "OK") {

                    Addresses.Clear();

                    if(predictionList.Predictions.Count > 0) {
                        foreach(Prediction prediction in predictionList.Predictions) {
                            Addresses.Add(new AddressInfo {
                                Address = prediction.Description
                            });
                        }
                    }
                } else {
                    throw new Exception(predictionList.Status);
                }
            }
        }
    }
}

现在让我们来谈谈用户界面(UI)的问题...
    <Entry Text="{Binding AddressText}"
           TextChanged="OnTextChanged" />

    <ListView ItemsSource="{Binding Addresses}">
      <ListView.ItemTemplate>
        <DataTemplate>
          <TextCell Text="{Binding Address}"/>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>

代码后台:

private async void OnTextChanged(object sender, EventArgs eventArgs) {
    if(!string.IsNullOrWhiteSpace(ViewModel.AddressText)) {
        await ViewModel.GetPlacesPredictionsAsync();
    }
}

1
这正是我所需要的,如果可以问一下,你是如何获取坐标的?你使用地理编码器吗? - Mauricio Cárdenas
@MauricioCárdenas 是的,发现得好。我会修复它。还修正了 OnTextChanged 方法名称。 - hvaughan3
@hvaughan3 我有一个问题:有这个“SetPropertyChanged”,但我在其他地方找不到它,所以它会抛出错误:“当前上下文中不存在名称'SetPropertyChanged'”。你能帮我解决一下吗? - Mauricio Cárdenas
2
@hvaughan3 你是真正的MVP - Mauricio Cárdenas
1
在UI代码中有一个单词“Text”,它会出现错误“无法将类型为'Xamarin.Forms.Xaml.ListNode'的对象强制转换为类型'Xamarin.Forms.Xaml.IElementNode'”。哦,花了大约3个小时才找出为什么会出现这个错误! - lsc
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接