打造通用下拉刷新上拉加載更多組件
android開(kāi)發(fā)中最常用的就是列表組件,如ListView,recycleView,用到它們感覺(jué)就會(huì)涉及到數(shù)據(jù)更新,分頁(yè)加載。
最開(kāi)始的時(shí)候,刷新組件我是在技術(shù)群里頭找了一個(gè)被人綁定好的庫(kù),是綁定的github上一個(gè)星星很多的java原生組件。但是demo很簡(jiǎn)單,對(duì)于當(dāng)時(shí)小白的我懵逼了,不曉得咋個(gè)用,而且一直覺(jué)得banding的庫(kù)總感覺(jué)有問(wèn)題,就想著直接找一個(gè)java的庫(kù)翻譯成C#版本的。功夫不負(fù)苦心人,在csdn上找到了一篇http://blog.csdn.net/zhongkejingwang/article/details/38868463
寫(xiě)的很詳細(xì),翻譯起來(lái)也省了不少力,也很感謝原作者。
突然想起一句話(huà),叫做我們不生產(chǎn)代碼,只是代碼的搬運(yùn)工!
這里貼上我翻譯好的其中幾個(gè)很重要的組件的代碼:
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 { // 初始狀態(tài) 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; // 當(dāng)前狀態(tài) private int currentStatus = 0; // 刷新回調(diào)接口 private OnRefreshListener mListener; // 刷新成功 private const int succeed = 0; // 刷新失敗 private const int failed = 1; // 按下Y坐標(biāo),上一個(gè)事件點(diǎn)Y坐標(biāo) private float downY, lastY; // 下拉的距離。注意:pullDownY和pullUpY不可能同時(shí)不為0 private float pullDownY = 0; // 上拉的距離 private float pullUpY = 0; // 釋放刷新的距離 private float refreshDist = 200; // 釋放加載的距離 private float loadmoreDist = 200; private UIScheduling uScheduling; // 回滾速度 private float moveSpeed = 8; // 第一次執(zhí)行布局 private bool isLayout = false; // 在刷新過(guò)程中滑動(dòng)操作 private bool isTouch = false; // 手指滑動(dòng)距離與下拉頭的滑動(dòng)距離比,中間會(huì)隨正切函數(shù)變化 private float radio = 2; // 下拉箭頭的轉(zhuǎn)180°動(dòng)畫(huà) private RotateAnimation rotateAnimation; // 均勻旋轉(zhuǎn)動(dòng)畫(huà) private RotateAnimation refreshingAnimation; // 下拉頭 private View refreshView; // 下拉的箭頭 public View pullView; // 正在刷新的圖標(biāo) private View refreshingView; // 刷新結(jié)果圖標(biāo) private View refreshStateImageView; // 刷新結(jié)果:成功或失敗 private TextView refreshStateTextView; // 上拉頭 private View loadmoreView; // 上拉的箭頭 public View pullUpView; // 正在加載的圖標(biāo) private View loadingView; // 加載結(jié)果圖標(biāo) private View loadStateImageView; // 加載結(jié)果:成功或失敗 private TextView loadStateTextView; //請(qǐng)求加載錯(cuò)誤View private View errorView; // 實(shí)現(xiàn)了Pullable接口的View private View pullableView; // 過(guò)濾多點(diǎn)觸碰 private int mEvents; // 這兩個(gè)變量用來(lái)控制pull的方向,如果不加控制,當(dāng)情況滿(mǎn)足可上拉又可下拉時(shí)沒(méi)法下拉 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) { // 正在刷新,且沒(méi)有往上推的話(huà)則懸停,顯示"正在刷新..." 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(); // 隱藏下拉頭時(shí)有可能還在刷新,只有當(dāng)前狀態(tài)不是正在刷新時(shí)才改變狀態(tài) if (currentStatus != refreshing && currentStatus != loading) changeStatus(initStatus); uScheduling.Cancel(); RequestLayout(); } if (pullUpY > 0) { // 已完成回彈 pullUpY = 0; pullUpView.ClearAnimation(); // 隱藏上拉頭時(shí)有可能還在刷新,只有當(dāng)前狀態(tài)不是正在刷新時(shí)才改變狀態(tài) if (currentStatus != refreshing && currentStatus != loading) changeStatus(initStatus); uScheduling.Cancel(); RequestLayout(); } // 刷新布局,會(huì)自動(dòng)調(diào)用onLayout RequestLayout(); // 沒(méi)有拖拉或者回彈完成 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); // 添加勻速轉(zhuǎn)動(dòng)動(dòng)畫(huà) 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> /// 完成刷新操作,顯示刷新結(jié)果。注意:刷新完成后一定要調(diào)用這個(gè)方法 /// </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) { // 刷新結(jié)果停留1秒 new Handler((Message msg) => { changeStatus(complete); hide(); }).SendEmptyMessageDelayed(0, 1000); } else { changeStatus(complete); hide(); } } /// <summary> /// 加載完畢,顯示加載結(jié)果。注意:刷新完成后一定要調(diào)用這個(gè)方法 /// </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) { // 刷新結(jié)果停留1秒 new Handler((Message msg) => { changeStatus(complete); hide(); }).SendEmptyMessageDelayed(0, 1000); } else { changeStatus(complete); hide(); } } /// <summary> break;