项目。我们经常会有上拉和下拉刷新的需求。几乎所有的listView和RecyclerView都会伴随上拉下拉刷新的需求。如果我们使用一些开源的控件,改变了控件之后,我们需要更新它。现在撸起袖子写一个通用的刷新控件思路:写一个继承RelativeLayout的RefreshLayout,添加head和tail控件作为刷新控件,通过事件分发进行刷新操作,通过动画控制控件移动目的:让他所有的子-控件可用,甚至一个TextViewpublicclassRefreshLayoutextendsRelativeLayout{/***滑动控件时的速度比*/privatefinalintV_REFRESH=2;*/privatebooleanmIsRefreshDuring;/***可以下拉刷新*/privatebooleanmCanDownPull;/***可以上拉刷新*/privatebooleanmCanUpPull;/***判断是否是触摸后的第一个动作*/privatebooleanmIsFirstMove;/***Y轴平移距离*/privateintmDistanceY;/***刷新界面对象*/privateOnRefreshmOnRefresh;/***控制事件拦截的变量*/privatebooleanmCanIntercept;privateintmTouchSlop;privateintmDistance;privateLayoutParamsHeaderParams;privateViewmHeaderView;privateViewmFootView;privateintmHeaderMaxHeight;privateintmStartY;privateLayoutParamsmFootParams;privateintmFootMaxHeight;privatePullCallBackmCallBack;privateViewmChildView;privateObjectAnimatormAnimator;publicRefreshLayout(Contextcontext){super(context);initData();}publicRefreshLayout(Contextcontext,AttributeSetattrs){super(context,attrs);initData();}publicRefreshLayout(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr)));initData();}/***头尾控件必须实现的接口*/publicinterfaceHeadAndFootCallBack{//设置属性voidsetAttribute();//开始刷新voidstartPull();//停止刷新voidstopPull();}/***被拖控件的子类必须实现*/publicinterfacePullCallBack{booleancanDownPull();booleancanUpPull();}privatevoidinitData(){//不调用这个方法setWillNotDraw(false)就不能画图了;}/***下拉刷新完成后必须使用此方法*/publicvoiddownPullFinish(){mAnimator.setFloatValues(mChildView.getTranslationY(),0);mAnimator.start();((HeadAndFootCallBack)mHeaderView).stopPull();}/***上拉完成后必须调用此方法*/publicvoidupPullFinish(){mAnimator.setFloatValues(mChildView.getTranslationY(),0);mAnimator.start();((HeadAndFootCallBack)mFootView).stopPull();}/***自动下拉刷新*/publicvoidautoDownPullForHead(){postDelayed(newRunnable(){@Overridepublicvoidrun(){mCanDownPull=true;mCanUpPull=false;mAnimator.setFloatValues(10,mHeaderMaxHeight);mAnimator.start();((HeadAndFootCallBack)mHeaderView).startPull();mOnRefresh.onDownPullRefresh();}},500);}/***自动下拉刷新*/publicvoidautoUpPullForHead(){postDelayed(newRunnable(){@Overridepublicvoidrun(){mCanDownPull=false;mCanUpPull=true;mAnimator.setFloatValues(0,mFootMaxHeight);mAnimator.start();((HeadAndFootCallBack)mFootView).startPull();mOnRefresh.onUpPullRefresh();}},500);}@OverridepublicbooleanonInterceptTouchEvent(MotionEventev){returnmCanIntercept;}@OverridepublicbooleanonTouchEvent(MotionEventevent){returntrue;}@OverridepublicbooleandispatchTouchEvent(MotionEventevent){Log.e("shen","mIsRefreshDuring="+mIsRefreshDuring);if(mIsRefreshDuring)/*如果正在执行新将不会获得MotionEvent*/{returnsuper.dispatchTouchEvent(event);}switch(event.getAction()){caseMotionEvent.ACTION_DOWN:mStartY=(int)event.getY();initPull();break;caseMotionEvent.ACTION_MOVE:if(event.getPointerCount()==1){intmoveY=(int)event.getY();mDistanceY=(moveY-mStartY)/V_REFRESH;if(!mIsFirstMove&&mDistanceY!=0&&mDistanceY0;mCanUpPull=!mCanDownPull;mIsFirstMove=true;}if(mCanDownPull&&mCallBack.canDownPull()){upDataForDownPull();//下拉刷新mChildView.setEnabled(false);mCanIntercept=true;}if(mCanUpPull&&mCallBack.canUpPull()){upDataForUpPull();//上拉加载mChildView.setEnabled(false);mCanIntercept=true;}mStartY=moveY;}break;caseMotionEvent.ACTION_UP:mIsRefreshDuring=true;mIsFirstMove=false;if(mHeaderParams.height>=mHeaderMaxHeight)/*可以下拉刷新*/{((HeadAndFootCallBack)mHeaderView).startPull();mOnRefresh.onDownPullRefresh();}elseif(mFootParams.height>=mFootMaxHeight)/*可以上拉刷新*/{((HeadAndFootCallBack)mFootView).startPull();mOnRefresh。onUpPullRefresh();}elseif(mHeaderParams.height>0&&mHeaderParams.height0&&mFootParams.height=mFootMaxHeight){mFootParams.height=mFootMaxHeight;}mChildView.setTranslationY(-mFootParams.height);mFootView.requestLayout();}}/***下拉时处理手势*/privatevoidupDataForDownPull(){if(mDistanceY!=0){mHeaderParams.height+=mDistanceY;if(mHeaderParams.height>=mHeaderMaxHeight){//***mHeaderParams.height=mHeaderMaxHeight;}if(mHeaderParams.height<=0){//最小mHeaderParams.height=0;}mChildView.setTranslationY(mHeaderParams.height);mHeaderView.requestLayout();}}@OverrideprotectedvoidonAttachedToWindow(){super.onAttachedToWindow();}@OverrideprotectedvoidonFinishInflate(){super.onFinishInflate();//加载头mHeaderView=getChildAt(0);if(!(mHeaderViewinstanceofHeadAndFootCallBack)){newIllegalStateException("HeaderView必鎎HeadAndFootCallBack接口");}((HeadAndFootCallBack)mHeaderView).setAttribute();mHeaderParams=(LayoutParams)mHeaderView.getLayoutParams();mHeaderParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);//加载尾mFootView=getChildAt(2);if(!(mfootViewInstanceOfheadAndFootCallback)){newillegalStateException(“footViewheadDheadheadheadheadandfootcallback接口ldView=getChildAt(1);if(!(mChildViewinstanceofHeadAndFootCallBack)){newIllegalStateException("ChildView必须实现PullCallBack接口");}mCallBack=(PullCallBack)getChildAt(1);//设置动画mAnimator=ObjectAnimator.ofFloat(mChildView,"翻译Y",0);mAnimator.setInterpolator(newDecelerateInterpolator());mAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){@OverridepublicvoidonAnimationUpdate(ValueAnimatoranimation){inttranslationY=(int)mChildView.getTranslationY();if(mCanUpPull){//从移动到位置向下滑动mFootParams.height=Math.abs(translationY);mFootView.requestLayout();}elseif(mCanDownPull){mHeaderParams.height=Math.abs(translationY);mHeaderView.requestLayout();}Log.e("shen","translationY="+translationY);Log.e("shen","mHeaderParams.height="+mHeaderParams.height);if(translationY==0){mChildView.setEnabled(true);mDistanceY=0;//重置mIsRefreshDuring=false;//重置mCanIntercept=假;}否则{mIsRefreshDuring=真;}}});}@OverrideprotectedvoidonSizeChanged(intw,inth,intoldw,intoldh){super.onSizeChanged(w,h,oldw,oldh);mTouchSlop=ViewConfiguration.get(getContext()).getScaledTouchSlop();mDistance=mTouchSlop*5;//设置下拉头的初始属性mHeaderMaxHeight=mHeaderParams.height;mHeaderParams.height=0;mHeaderView.requestLayout();//设置上拉尾的初始属性mFootMaxHeight=mFootParams.height;mFootParams.height=0;mFootView.requestLayout();}/***下拉/上拉事件监听*/publicinterfaceOnRefresh{/***下拉刷新*/voidonDownPullRefresh();/***上拉load*/voidonUpPullRefresh();}publicvoidsetOnRefresh(OnRefreshonRefresh){mOnRefresh=onRefresh;}}给他添加三个控件,head和tail是刷新head和tail,二是正常显示控件必须让head和tail实现HeadAndFootCallBack接口设置属性并通知刷新开始和刷新结束难点:现在遇到的难点接下来的开发是由于dispatchTouchEvent中的判断,如果控件和子控件不消费事件,则不会给它发送事件,因为如果不消费DOWN事件,后面的所有事件都不会接收。解决办法是让控件的onTouchEvent方法返回true。当子控件不消费该事件时,返回给控件消费。不会因为没有消费DOWN事件而导致接收不到事件,dispatchTouchEvent也不会消费事件动画,动画是我的痛。最近在学习estimator的控制,觉得写的不错。通过它,可以帮助我们学习事件分发、动画、接口回调,也具有一定的学习意义。