您的位置:首页 > 其它

xamarin.forms使用stacklayout自定义列表及相关加载状态处理。

2016-11-21 15:15 267 查看
xamarin.forms本身有提供ListView控件,个人觉得不够灵活,而且在和ScrollView嵌套使用时,会存在内外两个滚动条问题,不好处理。

我们可以用ScrollView和StackLayout及TapGestureRecognizer做一个列表功能,可自定义每行item个数及其他的自定义动作,下边做一个单行双item列表,scrollview滚动到底部时,显示加载状态并加载数据:

1、xaml部分
//网络异常时重载按钮
<Button x:Name="btnReload" BorderWidth="0" BackgroundColor="White" HorizontalOptions="Center" IsVisible="false"></Button>
//列表主体
<ScrollView x:Name="svList" Orientation="Vertical" VerticalOptions="StartAndExpand">
<Frame OutlineColor="Red" BackgroundColor="White" HasShadow="False">
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
//筛选条件下返回数据为空时显示
<StackLayout Orientation="Horizontal">
<Label x:Name="lblNoResult"></Label>
</StackLayout>
//列表数据
<StackLayout x:Name="searchList" VerticalOptions="FillAndExpand">
</StackLayout>
</StackLayout>
</Frame>
</ScrollView>
//加载状态提示
<Label x:Name="lblLoading" Text="全力帮您加载中,稍等" HorizontalOptions="Center" IsVisible="false"></Label>
<Label x:Name="lblLoaded" Text="已无更多" HorizontalOptions="Center" IsVisible="false"></Label>

2、xaml.cs

public partial class List : ContentPage
{
//这边异步锁用来防止数据重复加载,不做描述,有兴趣的请自行查找相关资料
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);

public int PageIndex{ get; set; }

public int PageSize { get; set; }

public List()
{
InitializeComponent();
//分页初始值
this.PageIndex = 0;
this.PageSize = 10;

//列表初始化加载
this.Appearing += async (sender, e) =>
{
var initResult = await BindSearchResult();
};

//滚动到底部时加载数据
svList.Scrolled += async (sender, e) =>
{
//1、重载按钮是否为显示状态
//2、列表滚动位置计算:内容高度 - 列表高度
if (!btnReload.IsVisible && svList.ScrollY >= svList.ContentSize.Height - svList.Height)
{
var result = await BindSearchResult();
}
};
//重载按钮事件,请求异常或者网络异常时需要重新加载数据
btnReload.Command = new Command(async () =>
{
await BindSearchResult();
});

private async Task<bool> BindSearchResult()
{
//加载状态显示
lblLoading.IsVisible = true;
//重载按钮隐藏
btnReload.IsVisible = false;
//防止短时间内重复加载数据
await semaphoreSlim.WaitAsync();
try
{
//构建请求参数MultipartFormDataContent可以同时传递不同类型的请求参数:StringContent、
//StreamContent等。
var mpContent = new MultipartFormDataContent();
mpContent.Add(new StringContent(this.PageIndex.ToString(), Encoding.UTF8), "pageIndex");
mpContent.Add(new StringContent(this.PageSize.ToString(), Encoding.UTF8), "pageSize");
//Utils.PostAsync<T>()自己封装的restapi请求方法,这边不介绍,UrlConfig类为各种api请求的Url集合
var postResult = await Utils.PostAsync<SearchResult>(UrlConfig.SearchUrl, mpContent, (message) =>
{
btnReload.Text = message + "点击重新加载";
btnReload.IsVisible = true;
lblLoading.IsVisible = false;
});
if (postResult != null && postResult.success)
{
//判断请求结果记录数
if (postResult.data.total > 0)
{
lblNoResult.IsVisible = false;
//左边列表source
var search_l = postResult.data.data.Where((d, i) => i % 2 == 0).Select(d => new
{
//这边为左边列表要绑定的业务数据
}).ToList();
//右边列表source
var search_r = postResult.data.data.Where((d, i) => i % 2 != 0).Select(d => new
{
//这边为右边列表要绑定的业务数据
}).ToList();
//UI构造,这个例子为双item,所以以右边列表个数作为遍历上限
//使用Grid列表item的布局,这边以一张图片和图片名称的Label示例
for (var i = 0; i < search_r.Count(); i++)
{
var grid = new Grid();
var item_l = new StackLayout();
item_l.Children.Add(new Image() { Source = search_l[i].ImagePath, HeightRequest = 120 });
item_l.Children.Add(new Label() { Text = search_l[i].ImageName, HorizontalOptions = LayoutOptions.Center });
//图片ID编号,由列表进入item详情页时传递过去
item_l.Children.Add(new Label() { Text = search_l[i].ID.ToString(), IsVisible = false });
var item_r = new StackLayout();
item_r.Children.Add(new Image() { Source = search_r[i].ImagePath, HeightRequest = 120 });
item_r.Children.Add(new Label() { Text = search_r[i].ImageName, HorizontalOptions = LayoutOptions.Center });
//图片ID编号,由列表进入item详情页时传递过去
item_r.Children.Add(new Label() { Text = search_r[i].ID.ToString(), IsVisible = false });
grid.Children.Add(item_l, 0, 0);
grid.Children.Add(item_r, 1, 0);
//左边列表item点击事件
var tap_l = new TapGestureRecognizer();
tap_l.Tapped += (sender, e) =>
{
//点击进入详情页
var id = ((Label)(((StackLayout)sender).Children[2])).Text;
var name = ((Label)(((StackLayout)sender).Children[1])).Text;
this.Navigation.PushAsync(new Info(int.Parse(id)) { Title = /*自定义详情页标题*/ });
};
item_l.GestureRecognizers.Add(tap_l);
//右边列表item点击事件
var tap_r = new TapGestureRecognizer();
tap_r.Tapped += (sender, e) =>
{
//点击进入详情页
var id = ((Label)(((StackLayout)sender).Children[2])).Text;
var name = ((Label)(((StackLayout)sender).Children[1])).Text;
this.Navigation.PushAsync(new Info(int.Parse(id)) { Title = /*自定义详情页标题*/ });
};
item_r.GestureRecognizers.Add(tap_r);
searchList.Children.Add(grid);
}
//请求个数恰巧为单数时处理,逻辑同上
if (search_l.Count() > search_r.Count())
{
var grid = new Grid();
var item = new StackLayout();
item.Children.Add(new Image() { Source = search_l[search_l.Count() - 1].ImagePath, HeightRequest = 120 });
item.Children.Add(new Label() { Text = search_l[search_l.Count() - 1].ImageName, HorizontalOptions = LayoutOptions.Center });
item.Children.Add(new Label() { Text = search_l[search_l.Count() - 1].ID.ToString(), IsVisible = false });
var tap = new TapGestureRecognizer();
tap.Tapped += (sender, e) =>
{
//点击进入详情页
var id = ((Label)(((StackLayout)sender).Children[2])).Text;
var name = ((Label)(((StackLayout)sender).Children[1])).Text;
this.Navigation.PushAsync(new Info(int.Parse(id)) { Title = /*自定义详情页标题*/ });
};
item.GestureRecognizers.Add(tap);
grid.Children.Add(item, 0, 0);
searchList.Children.Add(grid);
}
}
else
{
//请求返回的数据为空处理
lblNoResult.IsVisible = true;
lblNoResult.Text = string.Format("抱歉,没有找到{0}的信息", /*某某某*/);
}
//关闭加载状态显示
lblLoading.IsVisible = false;
if (!postResult.data.data.Any())
{
lblLoaded.IsVisible = true;
await lblLoaded.FadeTo(0, 2000);
lblLoaded.Opacity = 1;
lblLoaded.IsVisible = false;
}
//当前分页数累加
this.PageIndex++;
return true;
}
return false;
}
finally
{
//释放锁
semaphoreSlim.Release();
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐