Xamarin.Forms JSON 对象进入列表视图

Xamarin.Forms JSON object into Listview

我正在尝试解析这个 JSON 对象并将其绑定到我在 Xamarin.Forms 中的 ListView。

我完全不知道如何处理它,因为我是 Xamarin Forms 的新手。

有更简单的方法吗?

我返回的JSON对象

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret"
  },
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette"
  }
]

这是我处理 REST json 响应的代码

public class RestClient
    {
        public RestClient ()
        {
        }

        public async Task<User[]> GetUsersAsync () {

            var client = new System.Net.Http.HttpClient ();

            client.BaseAddress = new Uri("http://jsonplaceholder.typicode.com");

            var response = client.GetAsync("users");

            var usersJson = response.Result.Content.ReadAsStringAsync().Result;

            var rootobject = JsonConvert.DeserializeObject<Rootobject>(usersJson);

            return rootobject.Users;

        }
    }

Users.cs

    public class Rootobject
        {
            public User[] Users { get; set; }
        }

        public class User
        {
            public string id { get; set; }
            public string username { get; set; }
        }

ListView Form Code
var sv = new RestClient ();
            var es = sv.GetUsersAsync();
            Xamarin.Forms.Device.BeginInvokeOnMainThread (() => {
                Debug.WriteLine("Found " + es.Result.Length + " users");
                listView.ItemsSource = es.Result;
            });

XAML

public ListViewPage ()
        {
            Title = "Users";

            var sv = new RestClient ();
            var es = sv.GetUsersAsync();
            Xamarin.Forms.Device.BeginInvokeOnMainThread (() => {
                Debug.WriteLine("Found " + es.Result.Length + " users");
                listView.ItemsSource = es.Result;
            });


            listView = new ListView ();
            listView.ItemTemplate = new DataTemplate(typeof(TextCell));
            listView.ItemTemplate.SetBinding(TextCell.TextProperty, "username");

            listView.ItemTemplate = new DataTemplate(typeof(ItemCell));

            Content = new StackLayout { 
                Children = {
                    listView
                }
            };
        }

看起来您没有在等待 sv.GetUsersAsync,我不确定结果是否会在不等待操作完成的情况下包含所有数据。

虽然可以使用任何集合和任何对象作为列表视图的数据源,但最好使用 ObservableCollection 并使您的用户 class 实现 INotifyPropertyChanged(查看 Fody.PropertyChanged nuget 包) .

你能和我们分享 xaml 吗?

编辑 1

您定义了两次 ItemTemplate。

listView.ItemTemplate = new DataTemplate(typeof(TextCell)); //first definition
listView.ItemTemplate.SetBinding(TextCell.TextProperty, "username");

listView.ItemTemplate = new DataTemplate(typeof(ItemCell)); //second definition, remove it

此外,最好将 MVVM 与 Xamarin.Forms 一起使用,并将数据加载到视图模型中,而不是页面的构造函数中。 Here 是 Xamarin.Forms 中关于 mvvm 和数据加载的好文章。

编辑 2

你为什么用这么奇怪的方式使用 async/await?与其阅读 Task.Result 属性,不如使用 async/await 对。 Example.

您的异步调用不正确。如果您必须在构造函数中执行此操作(这不是执行此操作的最佳位置),您会希望使用 ContinueWith,因为不应使用 Task.Result。此外,由于结果是一个阻塞调用,您在构造列表视图之前分配了项目源,并且您得到了一个空引用异常。

试试这个:

public class ListViewPage : ContentPage
{
    private readonly ListView listView;

    public ListViewPage()
    {
        Title = "Users";

        this.listView = new ListView {ItemTemplate = new DataTemplate(typeof (TextCell))};
        this.listView.ItemTemplate.SetBinding(TextCell.TextProperty, "username");

        Content = new StackLayout
        {
            Children = { this.listView }
        };

        var sv = new RestClient();
        var es = sv.GetUsersAsync().ContinueWith(t =>
        {
            if (t.Status == TaskStatus.RanToCompletion)
            {
                Debug.WriteLine("Found {0} users.", t.Result.Length);
                Device.BeginInvokeOnMainThread(() => this.listView.ItemsSource = t.Result);
            }
        });
    }
}

一个稍微好一点的选择(但也不完美)是重写出现的方法并且标记为异步。这样您就可以在异步 REST 调用方法上使用 await 。请注意,除非添加其他代码,否则每次出现视图时都会调用它。

    protected override async void OnAppearing()
    {
        base.OnAppearing();

        try
        {
            var sv = new RestClient();
            // activate/show spinner here
            this.listView.ItemsSource = await sv.GetUsersAsync();
            // inactivate/hide spinner here
        }
        catch (Exception exception)
        {
            this.DisplayAlert("Error", exception.Message, "OK");
        }
    }