一般的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;

大部分的解释都写在代码中了,大家如果不明白的可以仔细的去看一下。

如果对我写的内容不明白的话,可以去看一下 这篇文章。