简体   繁体   中英

REST API data-binding to ListView in Xamarin Android

First some context, I've been fooling around with Xamarin and API data from HERE .

My Current Solution structure is as follows:

在此处输入图片说明

Main View looks as follows:

在此处输入图片说明

DataModel.cs is as follows:

using Newtonsoft.Json;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using J = Newtonsoft.Json.JsonPropertyAttribute;

namespace CrossPlatformTFL.Model
{
    public partial class DataModel
    {
        [J("disruptions")] public List<object> Disruptions { get; set; }
        [J("modified")] public string Modified { get; set; }
        [J("created")] public string Created { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("crowding")] public Crowding Crowding { get; set; }
        [J("lineStatuses")] public List<LineStatus> LineStatuses { get; set; }
        [J("id")] public string Id { get; set; }
        [J("modeName")] public string ModeName { get; set; }
        [J("routeSections")] public List<RouteSection> RouteSections { get; set; }
        [J("name")] public string Name { get; set; }
        [J("serviceTypes")] public List<ServiceType> ServiceTypes { get; set; }
    }

    public partial class Crowding
    {
        [J("$type")] public string Type { get; set; }
    }

    public partial class LineStatus
    {
        [J("id")] public long Id { get; set; }
        [J("created")] public string Created { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("disruption")] public Disruption Disruption { get; set; }
        [J("reason")] public string Reason { get; set; }
        [J("statusSeverityDescription")] public string StatusSeverityDescription { get; set; }
        [J("lineId")] public string LineId { get; set; }
        [J("statusSeverity")] public long StatusSeverity { get; set; }
        [J("validityPeriods")] public List<ValidityPeriod> ValidityPeriods { get; set; }
    }

    public partial class Disruption
    {
        [J("affectedStops")] public List<AffectedStop> AffectedStops { get; set; }
        [J("created")] public string Created { get; set; }
        [J("additionalInfo")] public string AdditionalInfo { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("affectedRoutes")] public List<AffectedRoute> AffectedRoutes { get; set; }
        [J("categoryDescription")] public string CategoryDescription { get; set; }
        [J("category")] public string Category { get; set; }
        [J("closureText")] public string ClosureText { get; set; }
        [J("isBlocking")] public bool? IsBlocking { get; set; }
        [J("description")] public string Description { get; set; }
        [J("isWholeLine")] public bool? IsWholeLine { get; set; }
    }

    public partial class AffectedStop
    {
        [J("lat")] public long Lat { get; set; }
        [J("commonName")] public string CommonName { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("id")] public string Id { get; set; }
        [J("naptanId")] public string NaptanId { get; set; }
        [J("lon")] public long Lon { get; set; }
        [J("stationNaptan")] public string StationNaptan { get; set; }
        [J("status")] public bool Status { get; set; }
    }

    public partial class AffectedRoute
    {
        [J("id")] public string Id { get; set; }
        [J("destinationName")] public string DestinationName { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("direction")] public string Direction { get; set; }
        [J("originationName")] public string OriginationName { get; set; }
        [J("name")] public string Name { get; set; }
        [J("routeSectionNaptanEntrySequence")] public List<RouteSectionNaptanEntrySequence> RouteSectionNaptanEntrySequence { get; set; }
    }

    public partial class RouteSectionNaptanEntrySequence
    {
        [J("ordinal")] public long Ordinal { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("stopPoint")] public StopPoint StopPoint { get; set; }
    }

    public partial class StopPoint
    {
        [J("lat")] public long Lat { get; set; }
        [J("commonName")] public string CommonName { get; set; }
        [J("additionalProperties")] public List<object> AdditionalProperties { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("children")] public List<object> Children { get; set; }
        [J("icsCode")] public string IcsCode { get; set; }
        [J("hubNaptanCode")] public string HubNaptanCode { get; set; }
        [J("id")] public string Id { get; set; }
        [J("lon")] public long Lon { get; set; }
        [J("lineModeGroups")] public List<object> LineModeGroups { get; set; }
        [J("lineGroup")] public List<object> LineGroup { get; set; }
        [J("lines")] public List<object> Lines { get; set; }
        [J("naptanId")] public string NaptanId { get; set; }
        [J("stationNaptan")] public string StationNaptan { get; set; }
        [J("modes")] public List<object> Modes { get; set; }
        [J("placeType")] public string PlaceType { get; set; }
        [J("status")] public bool Status { get; set; }
    }

    public partial class ValidityPeriod
    {
        [J("fromDate")] public string FromDate { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("isNow")] public bool IsNow { get; set; }
        [J("toDate")] public string ToDate { get; set; }
    }

    public partial class RouteSection
    {
        [J("direction")] public string Direction { get; set; }
        [J("destination")] public string Destination { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("destinationName")] public string DestinationName { get; set; }
        [J("originationName")] public string OriginationName { get; set; }
        [J("name")] public string Name { get; set; }
        [J("originator")] public string Originator { get; set; }
        [J("serviceType")] public string ServiceType { get; set; }
    }

    public partial class ServiceType
    {
        [J("name")] public string Name { get; set; }
        [J("$type")] public string Type { get; set; }
        [J("uri")] public string Uri { get; set; }
    }

    public partial class DataModel
    {
        public static ObservableCollection<DataModel> FromJson(string json) => JsonConvert.DeserializeObject<ObservableCollection<DataModel>>(json, Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this ObservableCollection<DataModel> self) => JsonConvert.SerializeObject(self, Converter.Settings);
    }

    public class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
        };
    }
}

And View2ViewModel.cs is as follows:

using CrossPlatformTFL.Model;
using System.Collections.ObjectModel;
using System.Net.Http;

namespace CrossPlatformTFL.ViewModel
{
    public class View2ViewModel
    {
        public ObservableCollection<DataModel> OpenData { get; set; } = new ObservableCollection<DataModel>();

        public View2ViewModel()
        {
            using (HttpClient hc = new HttpClient())
            {
                    //I would prefer to use `await` here but I cant figure it out either
                    var jsonString = hc.GetStringAsync("https://api.tfl.gov.uk/Line/Mode/tube%2Cdlr%2C%20overground/Status?detail=true").Result;
                    OpenData = DataModel.FromJson(jsonString);

                }
            }
        }
    }

My MainPage.xaml is as following:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="CrossPlatformTFL.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:CrossPlatformTFL">

    <ContentPage.Content>
        <StackLayout>
            <Label Text="Page One" />
            <Label Text="Line Two" />
            <Button Clicked="Button_Clicked" Text="Go to page two" />
        </StackLayout>
    </ContentPage.Content>

</ContentPage>

And code behind for MainPage.xaml :

using CrossPlatformTFL.View;
using System;
using Xamarin.Forms;

namespace CrossPlatformTFL
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            Navigation.PushAsync(new View2());
        }
    }
}

My View2.xaml is:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="CrossPlatformTFL.View.View2"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="View2" />
            <ListView ItemsSource="{Binding OpenData}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell Detail="{Binding LineStatuses[0].StatusSeverityDescription}" 
                                  Text="{Binding Name}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

And Finally code behind for View2.xaml

using CrossPlatformTFL.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace CrossPlatformTFL.View
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class View2 : ContentPage
    {
        View2ViewModel vm;

        public View2()
        {
            InitializeComponent();

            vm = new View2ViewModel();
            BindingContext = vm;
        }
    }
}

SO the plan was to Bind the data returned from the API to a list view and display them on View2.

As you can see above I cant use await in the constructor for View2ViewModel.cs thats my problem number one.

I would appreciate ideas how to get around that.

Second problem is also on View2ViewModel.cs , specifically on

var jsonString = hc.GetStringAsync("https://api.tfl.gov.uk/Line/Mode/tube%2Cdlr%2C%20overground/Status?detail=true").Result;

On android the code breaks here and I dont have any information on that in the debugger:

在此处输入图片说明

Now surprisingly the code runs fine on UWP and displays api data on View2 : 在此处输入图片说明

Please help me to understand how can I await the API call and why its working on UWP but not on android?

You can download the debug the project HERE

PS My AndroidManifest.xml is as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="15" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application android:label="CrossPlatformTFL.Android" android:icon="@drawable/icon"></application>
</manifest>

Edit 2: applied try catch to see the exception. Here is the exception message: 在此处输入图片说明

public partial class View2 : ContentPage
{
    View2ViewModel vm = null;

    public View2()
    {
        InitializeComponent();
    }

    public override async void OnAppearing() {

        if (vm == null) {
          vm = new View2ViewModel();
          await vm.FetchData();
          BindingContext = vm;
        }
    }
}

public class View2ViewModel
{
    public ObservableCollection<DataModel> OpenData { get; set; } = new ObservableCollection<DataModel>();

    public async Task FetchData()
    {
        using (HttpClient hc = new HttpClient())
        {
          var jsonString = await hc.GetStringAsync("https://api.tfl.gov.uk/Line/Mode/tube%2Cdlr%2C%20overground/Status?detail=true");
                OpenData = DataModel.FromJson(jsonString);

            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM