打造通用下拉刷新上拉加载更多组件
android开发中最常用的就是列表组件,如ListView,recycleView,用到它们感觉就会涉及到数据更新,分页加载。
最开始的时候,刷新组件我是在技术群里头找了一个被人绑定好的库,是绑定的github上一个星星很多的java原生组件。但是demo很简单,对于当时小白的我懵逼了,不晓得咋个用,而且一直觉得banding的库总感觉有问题,就想着直接找一个java的库翻译成C#版本的。功夫不负苦心人,在csdn上找到了一篇http://blog.csdn.net/zhongkejingwang/article/details/38868463
写的很详细,翻译起来也省了不少力,也很感谢原作者。
突然想起一句话,叫做我们不生产代码,只是代码的搬运工!
这里贴上我翻译好的其中几个很重要的组件的代码:
1.PullToRefreshLayout
using System;using System.Threading.Tasks;using Android.Content;using Android.OS;using Android.Util;using Android.Views;using Android.Views.Animations;using Android.Widget;using CNBlog.Droid.Utils;namespace CNBlog.Droid.PullableView { public class PullToRefreshLayout : RelativeLayout { // 初始状态 private const int initStatus = 0; // 释放刷新 private const int releaseToRefresh = 1; // 正在刷新 private const int refreshing = 2; // 释放加载 private const int releaseToLoad = 3; // 正在加载 private const int loading = 4; // 操作完毕 private const int complete = 5; // 当前状态 private int currentStatus = 0; // 刷新回调接口 private OnRefreshListener mListener; // 刷新成功 private const int succeed = 0; // 刷新失败 private const int failed = 1; // 按下Y坐标,上一个事件点Y坐标 private float downY, lastY; // 下拉的距离。注意:pullDownY和pullUpY不可能同时不为0 private float pullDownY = 0; // 上拉的距离 private float pullUpY = 0; // 释放刷新的距离 private float refreshDist = 200; // 释放加载的距离 private float loadmoreDist = 200; private UIScheduling uScheduling; // 回滚速度 private float moveSpeed = 8; // 第一次执行布局 private bool isLayout = false; // 在刷新过程中滑动操作 private bool isTouch = false; // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化 private float radio = 2; // 下拉箭头的转180°动画 private RotateAnimation rotateAnimation; // 均匀旋转动画 private RotateAnimation refreshingAnimation; // 下拉头 private View refreshView; // 下拉的箭头 public View pullView; // 正在刷新的图标 private View refreshingView; // 刷新结果图标 private View refreshStateImageView; // 刷新结果:成功或失败 private TextView refreshStateTextView; // 上拉头 private View loadmoreView; // 上拉的箭头 public View pullUpView; // 正在加载的图标 private View loadingView; // 加载结果图标 private View loadStateImageView; // 加载结果:成功或失败 private TextView loadStateTextView; //请求加载错误View private View errorView; // 实现了Pullable接口的View private View pullableView; // 过滤多点触碰 private int mEvents; // 这两个变量用来控制pull的方向,如果不加控制,当情况满足可上拉又可下拉时没法下拉 private bool canPullDown = true; private bool canPullUp = true; private Context mContext; private Handler updateUIHandler; public void setOnRefreshListener(OnRefreshListener listener) { mListener = listener; } public PullToRefreshLayout(Context context) : base(context) { initView(context); } public PullToRefreshLayout(Context context, IAttributeSet attrs) : base(context, attrs) { initView(context); } public PullToRefreshLayout(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) { initView(context); } private void initView(Context context) { mContext = context; updateUIHandler = new Handler((Message msg) => { // 回弹速度随下拉距离moveDeltaY增大而增大 moveSpeed = (float)(8 + 5 * Math.Tan(Math.PI / 2 / MeasuredHeight * (pullDownY + Math.Abs(pullUpY)))); if (!isTouch) { // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..." if (currentStatus == refreshing && pullDownY <= refreshDist) { pullDownY = refreshDist; uScheduling.Cancel(); } else if (currentStatus == loading && -pullUpY <= loadmoreDist) { pullUpY = -loadmoreDist; uScheduling.Cancel(); } } if (pullDownY > 0) pullDownY -= moveSpeed; else if (pullUpY < 0) pullUpY += moveSpeed; if (pullDownY < 0) { // 已完成回弹 pullDownY = 0; pullView.ClearAnimation(); // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态 if (currentStatus != refreshing && currentStatus != loading) changeStatus(initStatus); uScheduling.Cancel(); RequestLayout(); } if (pullUpY > 0) { // 已完成回弹 pullUpY = 0; pullUpView.ClearAnimation(); // 隐藏上拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态 if (currentStatus != refreshing && currentStatus != loading) changeStatus(initStatus); uScheduling.Cancel(); RequestLayout(); } // 刷新布局,会自动调用onLayout RequestLayout(); // 没有拖拉或者回弹完成 if (pullDownY + Math.Abs(pullUpY) == 0) { uScheduling.Cancel(); } }); uScheduling = new UIScheduling(updateUIHandler); rotateAnimation = (RotateAnimation)AnimationUtils.LoadAnimation( context, Resource.Animator.reverse_anim); refreshingAnimation = (RotateAnimation)AnimationUtils.LoadAnimation( context, Resource.Animator.rotating); // 添加匀速转动动画 LinearInterpolator lir = new LinearInterpolator(); rotateAnimation.Interpolator = lir; refreshingAnimation.Interpolator = lir; } private void initView() { // 初始化下拉布局 pullView = refreshView.FindViewById<View>(Resource.Id.pull_icon); refreshStateTextView = refreshView.FindViewById<TextView>(Resource.Id.state_tv); refreshingView = refreshView.FindViewById<View>(Resource.Id.refreshing_icon); refreshStateImageView = refreshView.FindViewById<View>(Resource.Id.state_iv); // 初始化上拉布局 pullUpView = loadmoreView.FindViewById<View>(Resource.Id.pullup_icon); loadStateTextView = loadmoreView.FindViewById<TextView>(Resource.Id.loadstate_tv); loadingView = loadmoreView.FindViewById<View>(Resource.Id.loading_icon); loadStateImageView = loadmoreView.FindViewById<View>(Resource.Id.loadstate_iv); } /// <summary> /// 完成刷新操作,显示刷新结果。注意:刷新完成后一定要调用这个方法 /// </summary> /// <param name="refreshResult">succeed代表成功, failed代表失败</param> public void refreshFinish(int refreshResult) { refreshingView.ClearAnimation(); refreshingView.Visibility = ViewStates.Gone; switch (refreshResult) { case 0: // 刷新成功 refreshStateImageView.Visibility = ViewStates.Visible; refreshStateTextView.Text = "刷新成功"; refreshStateImageView.SetBackgroundResource(Resource.Mipmap.refresh_succeed); break; case 1: default: // 刷新失败 refreshStateImageView.Visibility = ViewStates.Visible; refreshStateTextView.Text = "刷新失败"; refreshStateImageView.SetBackgroundResource(Resource.Mipmap.refresh_failed); break; } if (pullDownY > 0) { // 刷新结果停留1秒 new Handler((Message msg) => { changeStatus(complete); hide(); }).SendEmptyMessageDelayed(0, 1000); } else { changeStatus(complete); hide(); } } /// <summary> /// 加载完毕,显示加载结果。注意:刷新完成后一定要调用这个方法 /// </summary> /// <param name="refreshResult">succeed代表成功, failed代表失败</param> public void loadmoreFinish(int refreshResult) { loadingView.ClearAnimation(); loadingView.Visibility = ViewStates.Gone; switch (refreshResult) { case 0: // 加载成功 loadStateImageView.Visibility = ViewStates.Visible; loadStateTextView.Text = "加载成功"; loadStateImageView.SetBackgroundResource(Resource.Mipmap.load_succeed); break; case 1: default: // 加载失败 loadStateImageView.Visibility = ViewStates.Visible; loadStateTextView.Text = "加载失败"; loadStateImageView.SetBackgroundResource(Resource.Mipmap.load_failed); pullableView.Visibility = ViewStates.Gone; break; } if (pullUpY < 0) { // 刷新结果停留1秒 new Handler((Message msg) => { changeStatus(complete); hide(); }).SendEmptyMessageDelayed(0, 1000); } else { changeStatus(complete); hide(); } } /// <summary> break;