一般的APP软件都是需要下拉刷新,下拉加载这两个功能的,今天我们就来学习怎么样实现这两个功能。
我们先来讲一下他们的原理,这里我们将采取的方案是使用组合View的方式,先自定义一个布局继承自LinearLayout,然后在这个布局中加入下拉头和ListView这两个子元素,并让这两个子元素纵向排列。初始化的时候,让下拉头向上偏移出屏幕,这样我们看到的就只有ListView了。然后对ListView的touch事件进行监听,如果当前ListView已经滚动到顶部并且手指还在向下拉的话,那就将下拉头显示出来,松手后进行刷新操作,并将下拉头隐藏。下拉加载更多也是同样的原理。
首先我们需要一个页面布局来存放我们需要实现页面。我们新建一个PullToRefresh.axml的页面:
PullToRefresh.axml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:minWidth="25px" android:minHeight="25px" android:background="#ffffffff"> <PullToRefresharp.Android.Views.ViewWrapper android:layout_width="fill_parent" android:layout_height="fill_parent"> <PullToRefresharp.Android.Widget.ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/listview1" android:divider="#ff999999" android:dividerHeight="1px" /> </PullToRefresharp.Android.Views.ViewWrapper> </FrameLayout>
这是用来存放下拉刷新控件的页面上。
我们还需要一个获取更多的页面。我们新建一个叫listloadmorefooter.axml的页面。
页面布局代码如下:
listloadmorefooter.axml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center_horizontal" android:orientation="horizontal" android:padding="15dp" android:background="#ffffffff"> <ProgressBar android:id="@+id/pb_load_progress" style="@android:style/Widget.ProgressBar.Small.Inverse" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:indeterminate="true" /> <TextView android:id="@+id/tv_load_more" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10.0dp" android:gravity="center" android:text="加载中" /> </LinearLayout>
这样两个布局的页面就弄好了,我们可以在活动中写下拉刷新的代码了。
接下来我们来看一下具体的功能页面的实现代码。
public class PullDownRequest { //定义传送类型 public PtrRequest PtrRequest; public RequestType request; } public class PullDownResult { //定义接收类型 public ResultType Type; //失败内容 public string ErrorMsg; } //自定义枚举类型 public enum RequestType { /// <summary> /// 我的活动 /// </summary> Mine = 0, /// <summary> /// 发布的活动 /// </summary> Manager = 1, /// <summary> /// 已结束的活动 /// </summary> Finish = 2, /// <summary> /// 缺席的活动 /// </summary> Leave = 3, /// <summary> /// 招募活动 /// </summary> All = 4 } public enum ResultType { /// <summary> /// 成功 /// </summary> Success = 0, /// <summary> /// 失败 /// </summary> Error = 1 }
首先我们自定义枚举类型,因为我们这里有多个页面需要下拉刷新,所以就需要判断是哪个页面操作了下拉刷新,在我们调用下拉刷新方法时需要将它作为参数传进去。当然,有传送就必定会有接收,我们也需要知道反馈的结果是正确还是错误的,这就需要再自定义接收类型,假如是错误的,我们还需要知道错误的内容,所以这里就又加了个ErrorMsg。
之前我说过,下拉刷新会更新我们的本身的数据库,有可能添加、修改或删除。所以在这里我们还需要设计一个数据库。设计数据库对各位来说都比较简单,在这里就不再阐述了,所以下面就直接贴出。
public class ActivityDB { public int IndexId { get; set; } public string ActivityName { get; set; } public string TeamName { get; set; } public string UserName { get; set; } public DateTime ActivityStartTime { get; set; } public DateTime ActivityEndTime { get; set; } public string ActivityLocation { get; set; } public string ActivitySummary { get; set; } public string ActivityState { get; set; } public int ActivityAttend { get; set; } public int JoinCount { get; set; } public long Tick { get; set; } public bool IsJoining { get; set; } public int Id { get; set; } }
除此之外,我们还应该新建消息。因为我们需要接收WEB给我们传送的提示,以便让我们知道数据到底有没有更新。下面的代码就是新建消息。
/// <summary> /// 刷新数据 /// </summary> public const int RefreshData = 0x0538; /// <summary> /// 获取更多 /// </summary> public const int LoadMoreData = 0x0539; /// <summary> /// 无数据 /// </summary> public const int NoData = 0x0540;
其实上面我们一直都在做着准备的工作,接下来才是真正下拉刷新的核心,而我在一开始就把下拉刷新的流程给大家做了介绍,所以下面的代码看起来应该没有太大的问题。
public void GetAllActivities(int skip, int count, int messageCode,RequestType stype, Handler handler) { //新建一个线程 Task.Factory.StartNew(() => { try { //判断网络连接 if (NetworkState.IsConnectivityMobile || NetworkState.IsConnectivityWifi) { //将当前页数、条数、GUID、当前本地数据库一起打包 PullDownRequest request = new PullDownRequest { request = stype, PtrRequest = new PtrRequest { Skip = skip,//当前第几页 Count = count,//当前第几条 Guid = Guid.NewGuid().ToString(),//创建一个GUID,目的是使其唯一 LocalData = new List<PtrUpdateParam>()//将本地数据库传递给WEB端进行比较 } }; PtrResponse<ActivityDB> response = null; //打包后利用与WEB端交互的方法传递 response = _reposity.GetActivitiesData(request); //判断数据到底有没有更新 //如果有更新,就执行下面的代码 if (response.UpdateData.Count() >= 0) { //新建一个消息 //新建一个Bundle类型的变量,将从WEB端传来的数据转换成JSON格式赋给它 //再将这个Bundle类型的变量赋给消息 //消息利用handler传递 Message msg = new Message(); msg.What = messageCode; Bundle bundle = new Bundle(); String value = JsonConvert.SerializeObject(response.UpdateData.Select(x => x.Data).ToList(), new JavaScriptDateTimeConverter()); bundle.PutString(MessageCode.DataKey, value); msg.Data = bundle; handler.SendMessage(msg); if (response.UpdateData.Count < 10) { handler.SendEmptyMessage(MessageCode.NoData); } } //如果数据没有更新,就直接传递一个无数据的消息 else { handler.SendEmptyMessage(MessageCode.NoData); } } //如果当前并没有网络,那么也是直接传递一个无数据的消息 else { handler.SendEmptyMessage(MessageCode.NoData); } } //捕捉与WEB端交互发生的错误 catch (WebException ex) { ExceptionManager.HandlerWebException(ex, handler); } //捕捉转换JSON格式时发生的错误 catch (JsonException) { handler.SendEmptyMessage(MessageCode.ConvertError); } //发送一个初始化状态的消息 finally { handler.SendEmptyMessage(MessageCode.ResetState); } }); }
上面就是我一开始所说的下拉刷新的核心,但是这还不算完,当WEB端那里传来消息后,我们肯定需要对此进行操作,更新数据或者获取更多。所以,这就要用到下面的代码。
//根据传来的消息决定到底要执行哪种方法 switch (msg.What) { //获取更多的方法 case MessageCode.LoadMoreData: { //新建一个value,并将传来的key值赋给它 //实例化一个数据库变量,通过value将之前转换成json格式的数据反序列化并赋给数据库变量 //将数据库变量放入本地数据库中,总量产生变化 //通知数据发生变化 //改变获取更多的属性 String value = msg.Data.GetString(MessageCode.DataKey); IList<ActivityDB> more = JsonConvert.DeserializeObject<IList<ActivityDB>>(value, new JavaScriptDateTimeConverter()); ListData = ListData.Concat(more).ToList(); TotalCount += more.Count; NotifyDataSetChanged(); IsLoadMore = false; } break; //刷新数据的方法 case MessageCode.RefreshData: { //清空当前数据库 //实例化一个数据库变量,通过value将之前转换成json格式的数据反序列化并赋给数据库变量 //将数据库变量放入本地数据库中,总量产生变化 //显示“正在加载” //通知数据发生变化 //改变获取更多的属性 ListData.Clear(); String value = msg.Data.GetString(MessageCode.DataKey); IList<ActivityDB> more = JsonConvert.DeserializeObject<IList<ActivityDB>>(value, new JavaScriptDateTimeConverter()); ListData = more; TotalCount = more.Count; ListView.OnRefreshCompleted(); LoadMoreTv.Text = "正在加载"; NotifyDataSetChanged(); IsLoadMore = false; } break; //无数据 case MessageCode.NoData: { //显示“已加载完毕” //将显示更多的加载条设置为隐藏 LoadMoreTv.Text = "已加载完毕"; LoadMoreBar.Visibility = ViewStates.Gone; } break;
大部分的解释都写在代码中了,大家如果不明白的可以仔细的去看一下。
如果对我写的内容不明白的话,可以去看一下http://www.cnblogs.com/yaozhenfa/p/xamarin_android_pulltorefresharp.html 这篇文章。