一般的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;
大部分的解释都写在代码中了,大家如果不明白的可以仔细的去看一下。
如果对我写的内容不明白的话,可以去看一下 这篇文章。