添加 children 时 xamarin 滚动视图跳转

xamarin scrollview jumping when adding children

我有一个 <ScrollView/>,其中包含一个 <Grid/>(属于 <images/>),当用户接近滚动视图的底部时,我连接到一个网站并下载下一组图像(实际上,JSON 包含 ImageSource 的链接),它创建一个 "endless" 图像滚动框。

我的问题是,当我下载下一组图像时,应用程序会暂时挂起,然后滚动框会跳转以赶上新图像集的添加。 我怎样才能避免这种情况"Jumping"?

    private async void OnScrolled(object sender, ScrolledEventArgs e)
    {
        ScrollView scroller = (ScrollView)sender;
        //threshhold == bottom of scrollveiw + height of one image (aka just before it's visible)
        double threashold = (e.ScrollY + scroller.Height) + preview_size;

        //if we touch the threshhold...
        if (threashold > scroller.ContentSize.Height)
        {
            //one row of images
            int TilePreload = (Tiles.Count + ColCount);

            //if the next row exceeds the total available post count, download and append more posts
            if (TilePreload >= Posts.Count)
            {
                //we have reached the end of our postlist, we must get more!
                var results = await Task.Run(()=>FetchResults<List<CPost>>());
                Posts.AddRange( results);
            }

            //then, add the tiles to UI 
            //AddRow();// <- jumpy

            //calling this as a task results in no tiles added, and eventually an execption
            await Task.Run( () => AddRow() );
        }
    }

//将 for 循环作为函数分开,因此它可以 运行 作为任务(如果需要)

    public void AddRow()
    {
        for (int i = 0; i < RowCount; i++)
        {
            //wrapper for assigning Image to Grid
            //aka ImageSourec = some URL
            AddTile(i);
        }
    }

注意:FetchResults<T>(); 或多或少是

的包装
//fyi using System.Net.Http;
public static string GetResponse(string page, Dictionary<String, String> arguments, bool IsPost = false)
{
    HttpMethod Method = IsPost ? HttpMethod.Post : HttpMethod.Get;

    var request = new HttpRequestMessage(Method, page)
    {
        Content = new FormUrlEncodedContent(arguments)
    };

    HttpResponseMessage httpResponse = Client.SendAsync(request).Result;
    if (httpResponse.IsSuccessStatusCode)
    {
        return httpResponse.Content.ReadAsStringAsync().Result;
    }
    return null;
}

如果您要更新 UI,则需要在 UI 主线程上完成。

当你打电话时

await Task.Run( () => AddRow() );

这意味着 AddRow(及其调用的任何方法)未 运行在 UI 线程上运行并将导致崩溃。

你可能会尝试这样的事情(未测试):

private async void OnScrolled(object sender, ScrolledEventArgs e)
{
    await Task.Run(async () => 
    {
        ScrollView scroller = (ScrollView)sender;
        //threshhold == bottom of scrollveiw + height of one image (aka just before it's visible)
        double threashold = (e.ScrollY + scroller.Height) + preview_size;

        //if we touch the threshhold...
        if (threashold > scroller.ContentSize.Height)
        {
            //one row of images
            int TilePreload = (Tiles.Count + ColCount);

            //if the next row exceeds the total available post count, download and append more posts
            if (TilePreload >= Posts.Count)
            {
                //we have reached the end of our postlist, we must get more!
                var results = await Task.Run(()=>FetchResults<List<CPost>>()).ConfigureAwait(false);
                Posts.AddRange( results);
            }
        }
    });

    //then, add the tiles to UI 
    AddRow();
}  

此外,为什么 GetResponse 不是异步方法? (使用 .Result 阻塞线程)因此为什么 FetchResults>() 不是异步的?

要使 GetResponse 异步:

public static async Task<string> GetResponse(string page, Dictionary<String, String> arguments, bool IsPost = false)
{
    HttpMethod Method = IsPost ? HttpMethod.Post : HttpMethod.Get;

    var request = new HttpRequestMessage(Method, page)
    {
        Content = new FormUrlEncodedContent(arguments)
    };

    HttpResponseMessage httpResponse = await Client.SendAsync(request).ConfigureAwait(false);
    if (httpResponse.IsSuccessStatusCode)
    {
        return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
    }
    return null;
}

按照你的方式,由于使用 .Result,你有很多线程跳转和代码阻塞线程 因此,通过将所有不需要 运行ning 的代码放在任务中的 UI 线程上,您可以避免 UI 线程上的任何代码 运行ning,直到您需要它,即当您添加 UI 元素时。

使用.ConfigureAwait(false)意味着当该任务结束时,以下代码将不会编组回调用线程,从而节省了一些线程编组,这需要时间。当未调用 .ConfigureAwait(false) 时,默认值为 .ConfigureAwait(true) ,这意味着“完成此任务后,将以下代码编组回调用此任务的线程。因此,通过执行上述操作,您应该避免一些线程延迟,希望这会解决神经质问题。

尽管您可能需要进行测试,因为使用上述方法,OnScrolled 事件将在该工作完成时继续触发。因此,您可能只想标记 运行 一次获取新项目的代码,例如:

bool _isGettingNewItems;
private async void OnScrolled(object sender, ScrolledEventArgs e)
{
    // Don't run the code to get new items if it is already running
    if (_isGettingNewItems) 
        return;

    _isGettingNewItems = true;
    await Task.Run(async () => 
    {
        ...
    });

    //then, add the tiles to UI 
    AddRow();

    // finished getting new items, so set the boolean back to false
    _isGettingNewItems = false;
}