开源Android-PullToRefresh下拉刷新源码分析


PullToRefresh 这个库用的是非常至多,github  今天主要分析一下源码实现.

我们通过ListView的下拉刷新进行分析,其它的类似。

整个下拉刷新  父View是LinearLayout, 在LinearLayout添加了Header View ,Footer View,和ListView

PullToRefreshBase 是父类 扩展了 LinearLayout水平布局   如果我们使用ListView 需要观看子类  PullToRefreshAdapterViewBase -> PullToRefreshListView  


 初始化代码在PullToRefreshBase init方法中

重点代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Refreshable View  
  2. // By passing the attrs, we can add ListView/GridView params via XML  
  3. mRefreshableView = createRefreshableView(context, attrs);//通过子类传入的View,ListView或者ScrollView等  
  4. addRefreshableView(context, mRefreshableView);//添加view到布局中  
  5.   
  6. // We need to create now layouts now  创建Header和Footer视图,默认是INVISIBLE,要添加到父窗口  
  7. mHeaderLayout = createLoadingLayout(context, Mode.PULL_FROM_START, a);  
  8. mFooterLayout = createLoadingLayout(context, Mode.PULL_FROM_END, a);  
  9.   
  10.   
  11. handleStyledAttributes(a);//添加loadingView效果,这里是把View添加到ListView HeaderView里面去  
  12. updateUIForMode(); //把布局添加到父View中  

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void updateUIForMode() {  
  2.     final LinearLayout.LayoutParams lp = getLoadingLayoutLayoutParams();  
  3.     // Remove Header, and then add Header Loading View again if needed  
  4.     if (this == mHeaderLayout.getParent()) {  
  5.         removeView(mHeaderLayout);  
  6.     }  
  7.     if (mMode.showHeaderLoadingLayout()) {  
  8.         addViewInternal(mHeaderLayout, 0, lp);//加入View到LinearLayout  
  9.     }  
  10.   
  11.     // Remove Footer, and then add Footer Loading View again if needed  
  12.     if (this == mFooterLayout.getParent()) {  
  13.         removeView(mFooterLayout);  
  14.     }  
  15.     if (mMode.showFooterLoadingLayout()) {  
  16.         addViewInternal(mFooterLayout, lp);//加入View到LinearLayout  
  17.     }  
  18.   
  19.     // Hide Loading Views  
  20.     refreshLoadingViewsSize();//把headerView隐藏起来,其实用的是padding的方式 设置为负值 就到屏幕顶部的外面了   
  21.   
  22.     // If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise  
  23.     // set it to pull down  
  24.     mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_FROM_START;  
  25. }  

//这里有2个LoadingView,一个是加入到LinearLayout中去了,还有一个是加入到ListView本身的Header里面

看看handleStyledAttributes方法 定位到子类复写的地方

FrameLayout frame = new FrameLayout(getContext());
mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a);
mHeaderLoadingView.setVisibility(View.GONE);
frame.addView(mHeaderLoadingView, lp);
mRefreshableView.addHeaderView(frame, null, false);//添加LoadingView到ListView Header上

        //headerView一共有2个LoadingView,一个是被加入到LinearLayout一个是被加入到ListView的HeaderView
addViewInternal方法就是加入到LinearLayout父类中 


看看LoadingLayout 有2种  FlipLoadingLayout 和  RotateLoadingLayout 一般我们用旋转的加载动画
左边一个旋转图片,右边是文字和时间提示
    
第一个LoadingLayout主要显示 :下拉刷新,放开以刷新
第二个LoadingLayout显示松手后的文字:正在载入...

结构是这样



当UI初始化好,下面看看onTouch 下拉捕获事件


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public final boolean onTouchEvent(MotionEvent event) {  
  2.     if (!isPullToRefreshEnabled()) {  
  3.         return false;  
  4.     }  
  5.     // If we're refreshing, and the flag is set. Eat the event  
  6.     if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {  
  7.         return true;  
  8.     }  
  9.     if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {  
  10.         return false;  
  11.     }  
  12.     switch (event.getAction()) {  
  13.         case MotionEvent.ACTION_MOVE: {  
  14.             if (mIsBeingDragged) {  
  15.                 mLastMotionY = event.getY();  
  16.                 mLastMotionX = event.getX();  
  17.                 pullEvent();//开始下拉,移动   
  18.                 return true;  
  19.             }  
  20.             break;  
  21.         }  
  22.   
  23.         case MotionEvent.ACTION_DOWN: {  
  24.             if (isReadyForPull()) {//按下 开始下拉  
  25.                 mLastMotionY = mInitialMotionY = event.getY();  
  26.                 mLastMotionX = mInitialMotionX = event.getX();  
  27.                 return true;  
  28.             }  
  29.             break;  
  30.         }  
  31.   
  32.         case MotionEvent.ACTION_CANCEL:  
  33.         case MotionEvent.ACTION_UP: { //停止下拉的时候  
  34.             if (mIsBeingDragged) {  
  35.                 mIsBeingDragged = false;  
  36.                 if (mState == State.RELEASE_TO_REFRESH  
  37.                         && (null != mOnRefreshListener || null != mOnRefreshListener2)) {  
  38.                     setState(State.REFRESHING, true);//放下手指开始回调,执行我们的回调任务  
  39.                     return true;  
  40.                 }  
  41.   
  42.                 // If we're already refreshing, just scroll back to the top  
  43.                 if (isRefreshing()) {  
  44.                     smoothScrollTo(0);  
  45.                     return true;  
  46.                 }  
  47.   
  48.                 // If we haven't returned by here, then we're not in a state  
  49.                 // to pull, so just reset  
  50.                 setState(State.RESET); //恢复到原来的UI状态  
  51.   
  52.                 return true;  
  53.             }  
  54.             break;  
  55.         }  
  56.     }  
  57.   
  58.     return false;  
  59. }  


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 看看pullEvent方法  
  2.     private void pullEvent() {  
  3.         final int newScrollValue;  
  4.         final int itemDimension;  
  5.         final float initialMotionValue, lastMotionValue;  
  6.   
  7.         switch (getPullToRefreshScrollDirection()) {  
  8.             case HORIZONTAL:  
  9.                 initialMotionValue = mInitialMotionX;  
  10.                 lastMotionValue = mLastMotionX;  
  11.                 break;  
  12.             case VERTICAL:  
  13.             default:  
  14.                 initialMotionValue = mInitialMotionY;  
  15.                 lastMotionValue = mLastMotionY;  
  16.                 break;  
  17.         }  
  18.         //计算下拉移动了多少  
  19.         switch (mCurrentMode) {  
  20.             case PULL_FROM_END://上拉  
  21.                 newScrollValue = Math.round(Math.max(initialMotionValue - lastMotionValue, 0) / FRICTION);  
  22.                 itemDimension = getFooterSize();  
  23.                 break;  
  24.             case PULL_FROM_START://下拉  
  25.             default:  
  26.                 newScrollValue = Math.round(Math.min(initialMotionValue - lastMotionValue, 0) / FRICTION);  
  27.                 itemDimension = getHeaderSize();  
  28.                 break;  
  29.         }  
  30.   
  31.         //显示HeaderView 得到移动的值,可以让LoadingView显示出来  
  32.         setHeaderScroll(newScrollValue);  
  33.   
  34.         if (newScrollValue != 0 && !isRefreshing()) {  
  35.             float scale = Math.abs(newScrollValue) / (float) itemDimension;  
  36.             switch (mCurrentMode) {  
  37.                 case PULL_FROM_END:  
  38.                     mFooterLayout.onPull(scale);  
  39.                     break;  
  40.                 case PULL_FROM_START:  
  41.                 default:  
  42.                  mHeaderLayout.onPull(scale);//旋转左边的加载图片,显示文字和图片 这个地方最终会执行LoadingLayout中的 onPullImpl方法  
  43.                     break;  
  44.             }  
  45.             //更新状态 包括2中 释放按下触摸,还有就是 没释放手的触摸  
  46.             if (mState != State.PULL_TO_REFRESH && itemDimension >= Math.abs(newScrollValue)) {  
  47.                 setState(State.PULL_TO_REFRESH);  
  48.             } else if (mState == State.PULL_TO_REFRESH && itemDimension < Math.abs(newScrollValue)) {  
  49.                 setState(State.RELEASE_TO_REFRESH);//下拉松手 可以松手了  
  50.             }  
  51.         }  
  52.     }  



[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 再看看setHeaderScroll方法代码  
  2.     protected final void setHeaderScroll(int value) {  
  3.         if (DEBUG) {  
  4.             Log.d(LOG_TAG, "setHeaderScroll: " + value);  
  5.         }  
  6.           
  7.         if (DEBUG) {  
  8.             Log.d(LOG_TAG, "setHeaderScroll:" + value );  
  9.         }  
  10.   
  11.         // Clamp value to with pull scroll range  
  12.         final int maximumPullScroll = getMaximumPullScroll();  
  13.         value = Math.min(maximumPullScroll, Math.max(-maximumPullScroll, value));  
  14.   
  15.         if (mLayoutVisibilityChangesEnabled) {  
  16.             if (value < 0) { //有位移才显示  
  17.                 mHeaderLayout.setVisibility(View.VISIBLE);  
  18.             } else if (value > 0) {  <span style="font-family: Arial, Helvetica, sans-serif;">//有位移才显示</span>  
  19.                 mFooterLayout.setVisibility(View.VISIBLE);  
  20.             } else {   
  21.                 mHeaderLayout.setVisibility(View.INVISIBLE);  
  22.                 mFooterLayout.setVisibility(View.INVISIBLE);  
  23.             }  
  24.         }  
  25.   
  26.         if (USE_HW_LAYERS) {  
  27.             /** 
  28.              * Use a Hardware Layer on the Refreshable View if we've scrolled at 
  29.              * all. We don't use them on the Header/Footer Views as they change 
  30.              * often, which would negate any HW layer performance boost. 
  31.              */  
  32.             ViewCompat.setLayerType(mRefreshableViewWrapper, value != 0 ? View.LAYER_TYPE_HARDWARE  
  33.                     : View.LAYER_TYPE_NONE);  
  34.         }  
  35.   
  36.         //回到最原始的scrollTo 最常用的 移动布局  
  37.         switch (getPullToRefreshScrollDirection()) {  
  38.             case VERTICAL:  
  39.                 scrollTo(0, value);  
  40.                 break;  
  41.             case HORIZONTAL:  
  42.                 scrollTo(value, 0);  
  43.                 break;  
  44.         }  
  45.     }  

setState(State.REFRESHING, true);//拉倒最顶部 松手,会执行onRefreshing方法,回调我们实现的任务接口 也就是OnRefreshListener


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onRefreshing(final boolean doScroll) {  
  2.         if (mMode.showHeaderLoadingLayout()) {  
  3.             mHeaderLayout.refreshing();  
  4.         }  
  5.         if (mMode.showFooterLoadingLayout()) {  
  6.             mFooterLayout.refreshing();  
  7.         }  
  8.   
  9.         if (doScroll) {  
  10.             if (mShowViewWhileRefreshing) {  
  11.   
  12.                 // Call Refresh Listener when the Scroll has finished  
  13.                 OnSmoothScrollFinishedListener listener = new OnSmoothScrollFinishedListener() {  
  14.                     @Override  
  15.                     public void onSmoothScrollFinished() {  
  16.                         callRefreshListener();//回调接口执行  
  17.                     }  
  18.                 };  
  19.   
  20.                 switch (mCurrentMode) {  
  21.                     case MANUAL_REFRESH_ONLY:  
  22.                     case PULL_FROM_END:  
  23.                         smoothScrollTo(getFooterSize(), listener);  
  24.                         break;  
  25.                     default:  
  26.                     case PULL_FROM_START:  
  27.                         smoothScrollTo(-getHeaderSize(), listener);  
  28.                         break;  
  29.                 }  
  30.             } else {  
  31.                 smoothScrollTo(0);//回到原来的位置  
  32.             }  
  33.         } else {  
  34.             // We're not scrolling, so just call Refresh Listener now  
  35.             callRefreshListener();//回调接口执行  
  36.         }  
  37.     }  



[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void callRefreshListener() {  
  2.     if (null != mOnRefreshListener) {  
  3.         mOnRefreshListener.onRefresh(this);//回调  
  4.     } else if (null != mOnRefreshListener2) { //这个是上拉,下拉都可以的情况,使用 onRefreshListener2  
  5.         if (mCurrentMode == Mode.PULL_FROM_START) {  
  6.             mOnRefreshListener2.onPullDownToRefresh(this);  
  7.         } else if (mCurrentMode == Mode.PULL_FROM_END) {  
  8.             mOnRefreshListener2.onPullUpToRefresh(this);  
  9.         }  
  10.     }  
  11. }  

总结:状态包括下拉刷新,松手刷新,正在刷新,Loading隐藏。移动UI还是用的scrollTo最基本的代码. 动画部分可以看LoadingLayout的2个子类

主要的就这些,还有很多细节没有分析。若有问题请指出谢谢。



转自:http://blog.csdn.net/birdsaction/article/details/44831737





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值