添加 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;
}
我有一个 <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;
}