5.3.2 开源Android-PullToRefresh下拉刷新源码分析
开源Android-PullToRefresh下拉刷新源码分析
标签:Android布局下拉刷新pullToRefresh源码 2015-04-02 14:48 2448人阅读 评论(1) 收藏 举报 分类: Android 技术研究(39)版权声明:本文为博主原创文章,未经博主允许不得转载。
PullToRefresh 这个库用的是非常至多,github 今天主要分析一下源码实现.
我们通过ListView的下拉刷新进行分析,其它的类似。
整个下拉刷新 父View是LinearLayout, 在LinearLayout添加了Header View ,Footer View,和ListView
PullToRefreshBase 是父类 扩展了 LinearLayout水平布局 如果我们使用ListView 需要观看子类 PullToRefreshAdapterViewBase -> PullToRefreshListView
初始化代码在PullToRefreshBase init方法中
重点代码:
[java] view plain copy
- //RefreshableView
- //Bypassingtheattrs,wecanaddListView/GridViewparamsviaXML
- mRefreshableView=createRefreshableView(context,attrs);//通过子类传入的View,ListView或者ScrollView等
- addRefreshableView(context,mRefreshableView);//添加view到布局中
- //Weneedtocreatenowlayoutsnow创建Header和Footer视图,默认是INVISIBLE,要添加到父窗口
- mHeaderLayout=createLoadingLayout(context,Mode.PULL_FROM_START,a);
- mFooterLayout=createLoadingLayout(context,Mode.PULL_FROM_END,a);
- handleStyledAttributes(a);//添加loadingView效果,这里是把View添加到ListViewHeaderView里面去
- updateUIForMode();//把布局添加到父View中
[java] view plain copy
- protectedvoidupdateUIForMode(){
- finalLinearLayout.LayoutParamslp=getLoadingLayoutLayoutParams();
- //RemoveHeader,andthenaddHeaderLoadingViewagainifneeded
- if(this==mHeaderLayout.getParent()){
- removeView(mHeaderLayout);
- }
- if(mMode.showHeaderLoadingLayout()){
- addViewInternal(mHeaderLayout,0,lp);//加入View到LinearLayout
- }
- //RemoveFooter,andthenaddFooterLoadingViewagainifneeded
- if(this==mFooterLayout.getParent()){
- removeView(mFooterLayout);
- }
- if(mMode.showFooterLoadingLayout()){
- addViewInternal(mFooterLayout,lp);//加入View到LinearLayout
- }
- //HideLoadingViews
- refreshLoadingViewsSize();//把headerView隐藏起来,其实用的是padding的方式设置为负值就到屏幕顶部的外面了
- //Ifwe'renotusingMode.BOTH,setmCurrentModetomMode,otherwise
- //setittopulldown
- mCurrentMode=(mMode!=Mode.BOTH)?mMode:Mode.PULL_FROM_START;
- }
//这里有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
- publicfinalbooleanonTouchEvent(MotionEventevent){
- if(!isPullToRefreshEnabled()){
- returnfalse;
- }
- //Ifwe'rerefreshing,andtheflagisset.Eattheevent
- if(!mScrollingWhileRefreshingEnabled&&isRefreshing()){
- returntrue;
- }
- if(event.getAction()==MotionEvent.ACTION_DOWN&&event.getEdgeFlags()!=0){
- returnfalse;
- }
- switch(event.getAction()){
- caseMotionEvent.ACTION_MOVE:{
- if(mIsBeingDragged){
- mLastMotionY=event.getY();
- mLastMotionX=event.getX();
- pullEvent();//开始下拉,移动
- returntrue;
- }
- break;
- }
- caseMotionEvent.ACTION_DOWN:{
- if(isReadyForPull()){//按下开始下拉
- mLastMotionY=mInitialMotionY=event.getY();
- mLastMotionX=mInitialMotionX=event.getX();
- returntrue;
- }
- break;
- }
- caseMotionEvent.ACTION_CANCEL:
- caseMotionEvent.ACTION_UP:{//停止下拉的时候
- if(mIsBeingDragged){
- mIsBeingDragged=false;
- if(mState==State.RELEASE_TO_REFRESH
- &&(null!=mOnRefreshListener||null!=mOnRefreshListener2)){
- setState(State.REFRESHING,true);//放下手指开始回调,执行我们的回调任务
- returntrue;
- }
- //Ifwe'realreadyrefreshing,justscrollbacktothetop
- if(isRefreshing()){
- smoothScrollTo(0);
- returntrue;
- }
- //Ifwehaven'treturnedbyhere,thenwe'renotinastate
- //topull,sojustreset
- setState(State.RESET);//恢复到原来的UI状态
- returntrue;
- }
- break;
- }
- }
- returnfalse;
- }
[java] view plain copy
- 看看pullEvent方法
- privatevoidpullEvent(){
- finalintnewScrollValue;
- finalintitemDimension;
- finalfloatinitialMotionValue,lastMotionValue;
- switch(getPullToRefreshScrollDirection()){
- caseHORIZONTAL:
- initialMotionValue=mInitialMotionX;
- lastMotionValue=mLastMotionX;
- break;
- caseVERTICAL:
- default:
- initialMotionValue=mInitialMotionY;
- lastMotionValue=mLastMotionY;
- break;
- }
- //计算下拉移动了多少
- switch(mCurrentMode){
- casePULL_FROM_END://上拉
- newScrollValue=Math.round(Math.max(initialMotionValue-lastMotionValue,0)/FRICTION);
- itemDimension=getFooterSize();
- break;
- casePULL_FROM_START://下拉
- default:
- newScrollValue=Math.round(Math.min(initialMotionValue-lastMotionValue,0)/FRICTION);
- itemDimension=getHeaderSize();
- break;
- }
- //显示HeaderView得到移动的值,可以让LoadingView显示出来
- setHeaderScroll(newScrollValue);
- if(newScrollValue!=0&&!isRefreshing()){
- floatscale=Math.abs(newScrollValue)/(float)itemDimension;
- switch(mCurrentMode){
- casePULL_FROM_END:
- mFooterLayout.onPull(scale);
- break;
- casePULL_FROM_START:
- default:
- mHeaderLayout.onPull(scale);//旋转左边的加载图片,显示文字和图片这个地方最终会执行LoadingLayout中的onPullImpl方法
- break;
- }
- //更新状态包括2中释放按下触摸,还有就是没释放手的触摸
- if(mState!=State.PULL_TO_REFRESH&&itemDimension>=Math.abs(newScrollValue)){
- setState(State.PULL_TO_REFRESH);
- }elseif(mState==State.PULL_TO_REFRESH&&itemDimension<Math.abs(newScrollValue)){
- setState(State.RELEASE_TO_REFRESH);//下拉松手可以松手了
- }
- }
- }
[java] view plain copy
- 再看看setHeaderScroll方法代码
- protectedfinalvoidsetHeaderScroll(intvalue){
- if(DEBUG){
- Log.d(LOG_TAG,"setHeaderScroll:"+value);
- }
- if(DEBUG){
- Log.d(LOG_TAG,"setHeaderScroll:"+value);
- }
- //Clampvaluetowithpullscrollrange
- finalintmaximumPullScroll=getMaximumPullScroll();
- value=Math.min(maximumPullScroll,Math.max(-maximumPullScroll,value));
- if(mLayoutVisibilityChangesEnabled){
- if(value<0){//有位移才显示
- mHeaderLayout.setVisibility(View.VISIBLE);
- }elseif(value>0){<spanstyle="font-family:Arial,Helvetica,sans-serif;">//有位移才显示</span>
- mFooterLayout.setVisibility(View.VISIBLE);
- }else{
- mHeaderLayout.setVisibility(View.INVISIBLE);
- mFooterLayout.setVisibility(View.INVISIBLE);
- }
- }
- if(USE_HW_LAYERS){
- /**
- *UseaHardwareLayerontheRefreshableViewifwe'vescrolledat
- *all.Wedon'tusethemontheHeader/FooterViewsastheychange
- *often,whichwouldnegateanyHWlayerperformanceboost.
- */
- ViewCompat.setLayerType(mRefreshableViewWrapper,value!=0?View.LAYER_TYPE_HARDWARE
- :View.LAYER_TYPE_NONE);
- }
- //回到最原始的scrollTo最常用的移动布局
- switch(getPullToRefreshScrollDirection()){
- caseVERTICAL:
- scrollTo(0,value);
- break;
- caseHORIZONTAL:
- scrollTo(value,0);
- break;
- }
- }
setState(State.REFRESHING, true);//拉倒最顶部 松手,会执行onRefreshing方法,回调我们实现的任务接口 也就是OnRefreshListener
[java] view plain copy
- protectedvoidonRefreshing(finalbooleandoScroll){
- if(mMode.showHeaderLoadingLayout()){
- mHeaderLayout.refreshing();
- }
- if(mMode.showFooterLoadingLayout()){
- mFooterLayout.refreshing();
- }
- if(doScroll){
- if(mShowViewWhileRefreshing){
- //CallRefreshListenerwhentheScrollhasfinished
- OnSmoothScrollFinishedListenerlistener=newOnSmoothScrollFinishedListener(){
- @Override
- publicvoidonSmoothScrollFinished(){
- callRefreshListener();//回调接口执行
- }
- };
- switch(mCurrentMode){
- caseMANUAL_REFRESH_ONLY:
- casePULL_FROM_END:
- smoothScrollTo(getFooterSize(),listener);
- break;
- default:
- casePULL_FROM_START:
- smoothScrollTo(-getHeaderSize(),listener);
- break;
- }
- }else{
- smoothScrollTo(0);//回到原来的位置
- }
- }else{
- //We'renotscrolling,sojustcallRefreshListenernow
- callRefreshListener();//回调接口执行
- }
- }
[java] view plain copy
- privatevoidcallRefreshListener(){
- if(null!=mOnRefreshListener){
- mOnRefreshListener.onRefresh(this);//回调
- }elseif(null!=mOnRefreshListener2){//这个是上拉,下拉都可以的情况,使用onRefreshListener2
- if(mCurrentMode==Mode.PULL_FROM_START){
- mOnRefreshListener2.onPullDownToRefresh(this);
- }elseif(mCurrentMode==Mode.PULL_FROM_END){
- mOnRefreshListener2.onPullUpToRefresh(this);
- }
- }
- }
总结:状态包括下拉刷新,松手刷新,正在刷新,Loading隐藏。移动UI还是用的scrollTo最基本的代码. 动画部分可以看LoadingLayout的2个子类
主要的就这些,还有很多细节没有分析。若有问题请指出谢谢。
更多相关文章
- Android5.1 SystemUI 启动流程
- SwipeRefreshLayout和ListView的EmptyView共存冲突的问题
- android 事件分发,解决由于listview中实时刷新,导致子view点击事件
- Android(安卓)studio多个项目之间怎么实现快速切换?
- android webview js交互之自定义错误加载界面(重新刷新)
- [置顶] Android(安卓)popwindow和fragment结合 左侧弹出下拉菜单
- android下拉五级菜单联动
- Android(安卓)UI之实现Material化的下拉选择列表拒绝丑陋的Spinn
- android禁止状态栏下拉