这个下拉效果在网上最早的例子恐怕就是Johan Nilsson的实现,http://johannilsson.com/2011/03/13/android-pull-to-refresh-update.html。
如果这篇文章对您有用,劳烦几秒钟帮忙投下票:http://vote.blog.csdn.net/item/blogstar/aomandeshangxiao,Csdn
2012博客之星投票,谢谢!!!
后面的很多例子应该都是仿照这个写的,下面的这个例子就是对这个例子的修改,先看下一个点击的效果,我看到其他的分析博客里面没有谈到这一点,在这个代码中,我们一直看到是listview的第二项,而listview的第一项被遮挡了起来,滑动至第一项:
点击头条,头条会变成以下:
然后,过一段时间,刷新完成以后,listview又setSelection(1),增加一条数据,同时,把顶部给遮挡住:
这是点击刷新,然后是下拉刷新:
最后结果和点击刷新相同。那现在开始看下代码:
首先看下所用到的控件和变量:
-
-
privatestaticfinalintTAP_TO_REFRESH=1;
-
privatestaticfinalintPULL_TO_REFRESH=2;
-
privatestaticfinalintRELEASE_TO_REFRESH=3;
-
privatestaticfinalintREFRESHING=4;
-
-
privateintmCurrentScrollState;
-
-
privateintmRefreshState;
-
-
privateintmRefreshViewHeight;
-
-
privateintmRefreshOriginalTopPadding;
-
privateintmLastMotionY;
-
-
privateOnRefreshListenermOnRefreshListener;
-
-
privatestaticintREFRESHICON=R.drawable.goicon;
-
-
privateOnScrollListenermOnScrollListener;
-
privateLayoutInflatermInflater;
-
privateRelativeLayoutmRefreshView;
-
-
privateTextViewmRefreshViewText;
-
privateImageViewmRefreshViewImage;
-
privateProgressBarmRefreshViewProgress;
-
privateTextViewmRefreshViewLastUpdated;
-
-
-
privateRotateAnimationmFlipAnimation;
-
-
privateRotateAnimationmReverseFlipAnimation;
-
-
privatebooleanmBounceHack;
看下点击刷新的代码过程:
在init()方法中初始化各个控件及设置监听:
-
privatevoidinit(Contextcontext){
-
-
mFlipAnimation=newRotateAnimation(0,-180,RotateAnimation.RELATIVE_TO_SELF,
-
0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f);
-
mFlipAnimation.setInterpolator(newLinearInterpolator());
-
mFlipAnimation.setDuration(250);
-
mFlipAnimation.setFillAfter(true);
-
-
mReverseFlipAnimation=newRotateAnimation(-180,0,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f);
-
mReverseFlipAnimation.setInterpolator(newLinearInterpolator());
-
mReverseFlipAnimation.setDuration(250);
-
mReverseFlipAnimation.setFillAfter(true);
-
-
mInflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
mRefreshView=(RelativeLayout)mInflater.inflate(R.layout.pull_to_refresh_header,this,false);
-
mRefreshViewText=(TextView)mRefreshView.findViewById(R.id.pull_to_refresh_text);
-
mRefreshViewImage=(ImageView)mRefreshView.findViewById(R.id.pull_to_refresh_image);
-
mRefreshViewProgress=(ProgressBar)mRefreshView.findViewById(R.id.pull_to_refresh_progress);
-
mRefreshViewLastUpdated=(TextView)mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);
-
-
mRefreshViewImage.setMinimumHeight(50);
-
mRefreshView.setOnClickListener(newOnClickRefreshListener());
-
mRefreshOriginalTopPadding=mRefreshView.getPaddingTop();
-
mRefreshState=TAP_TO_REFRESH;
-
-
addHeaderView(mRefreshView);
-
super.setOnScrollListener(this);
-
measureView(mRefreshView);
-
mRefreshViewHeight=mRefreshView.getMeasuredHeight();
-
}
我们看到,mRefreshView控件既是listview用于刷新的头控件,这里它设置了监听事件:
-
mRefreshView.setOnClickListener(newOnClickRefreshListener());
我们再来看下监听事件的定义:
-
privateclassOnClickRefreshListenerimplementsOnClickListener{
-
@Override
-
publicvoidonClick(Viewv){
-
if(mRefreshState!=REFRESHING){
-
prepareForRefresh();
-
onRefresh();
-
}
-
}
-
}
调用了preparForRefresh()(准备刷新)和onRefresh()(刷新)两个方法,然后在查看这两个方法的定义:
-
publicvoidprepareForRefresh(){
-
resetHeaderPadding();
-
mRefreshViewImage.setVisibility(View.GONE);
-
-
-
mRefreshViewImage.setImageDrawable(null);
-
mRefreshViewProgress.setVisibility(View.VISIBLE);
-
-
mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);
-
mRefreshState=REFRESHING;
-
}
-
publicvoidonRefresh(){
-
if(mOnRefreshListener!=null){
-
mOnRefreshListener.onRefresh();
-
}
-
}
其中,后者还是回调方法。
我们看下preparForRefresh()方法中,引用了resetHeadPadding()方法:
-
-
-
-
-
privatevoidresetHeaderPadding(){
-
mRefreshView.setPadding(
-
mRefreshView.getPaddingLeft(),
-
mRefreshOriginalTopPadding,
-
mRefreshView.getPaddingRight(),
-
mRefreshView.getPaddingBottom());
-
}
从新设置下header距上下左右的距离。
最重要的方法应该是:onScroll()和onTouchEvent()方法,先看下onTouchEvent()方法:
-
@Override
-
publicbooleanonTouchEvent(MotionEventevent){
-
-
finalinty=(int)event.getY();
-
mBounceHack=false;
-
switch(event.getAction()){
-
caseMotionEvent.ACTION_UP:
-
-
if(!isVerticalScrollBarEnabled()){
-
setVerticalScrollBarEnabled(true);
-
}
-
if(getFirstVisiblePosition()==0&&mRefreshState!=REFRESHING){
-
-
if((mRefreshView.getBottom()>=mRefreshViewHeight
-
||mRefreshView.getTop()>=0)
-
&&mRefreshState==RELEASE_TO_REFRESH){
-
-
-
mRefreshState=REFRESHING;
-
-
prepareForRefresh();
-
-
onRefresh();
-
}elseif(mRefreshView.getBottom()<mRefreshViewHeight
-
||mRefreshView.getTop()<=0){
-
-
-
resetHeader();
-
setSelection(1);
-
}
-
}
-
break;
-
caseMotionEvent.ACTION_DOWN:
-
-
mLastMotionY=y;
-
break;
-
caseMotionEvent.ACTION_MOVE:
-
-
applyHeaderPadding(event);
-
break;
-
}
-
returnsuper.onTouchEvent(event);
-
}
当按下的时候,记录按下y轴的位置,然后在move中调用了applyHeaderPadding()方法,我们再看下这个方法:
-
-
privatevoidapplyHeaderPadding(MotionEventev){
-
-
intpointerCount=ev.getHistorySize();
-
for(intp=0;p<pointerCount;p++){
-
-
if(mRefreshState==RELEASE_TO_REFRESH){
-
if(isVerticalFadingEdgeEnabled()){
-
setVerticalScrollBarEnabled(false);
-
}
-
-
inthistoricalY=(int)ev.getHistoricalY(p);
-
-
-
-
inttopPadding=(int)(((historicalY-mLastMotionY)-mRefreshViewHeight)/1.7);
-
mRefreshView.setPadding(
-
mRefreshView.getPaddingLeft(),
-
topPadding,
-
mRefreshView.getPaddingRight(),
-
mRefreshView.getPaddingBottom());
-
}
-
}
-
}
通过记录滑动距离,实时变化头部mRefreshView的上下左右的距离。
最后,看下手指松开的ACTION_UP:
-
caseMotionEvent.ACTION_UP:
-
-
if(!isVerticalScrollBarEnabled()){
-
setVerticalScrollBarEnabled(true);
-
}
-
if(getFirstVisiblePosition()==0&&mRefreshState!=REFRESHING){
-
-
if((mRefreshView.getBottom()>=mRefreshViewHeight
-
||mRefreshView.getTop()>=0)
-
&&mRefreshState==RELEASE_TO_REFRESH){
-
-
-
mRefreshState=REFRESHING;
-
-
prepareForRefresh();
-
-
onRefresh();
-
}elseif(mRefreshView.getBottom()<mRefreshViewHeight
-
||mRefreshView.getTop()<=0){
-
-
-
resetHeader();
-
setSelection(1);
-
}
-
}
-
break;
当滑动距离大于一个item的距离时,添加一个item,否则,弹回。
看完onTouchEvent(),然后再看一下onScroll()方法:
-
@Override
-
publicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){
-
-
-
-
-
if(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&mRefreshState!=REFRESHING){
-
if(firstVisibleItem==0){
-
-
mRefreshViewImage.setVisibility(View.VISIBLE);
-
-
if((mRefreshView.getBottom()>=mRefreshViewHeight+20||mRefreshView.getTop()>=0)
-
&&mRefreshState!=RELEASE_TO_REFRESH){
-
mRefreshViewText.setText(R.string.pull_to_refresh_release_label);
-
mRefreshViewImage.clearAnimation();
-
mRefreshViewImage.startAnimation(mFlipAnimation);
-
mRefreshState=RELEASE_TO_REFRESH;
-
-
}elseif(mRefreshView.getBottom()<mRefreshViewHeight+20
-
&&mRefreshState!=PULL_TO_REFRESH){
-
mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);
-
if(mRefreshState!=TAP_TO_REFRESH){
-
mRefreshViewImage.clearAnimation();
-
mRefreshViewImage.startAnimation(mReverseFlipAnimation);
-
}
-
mRefreshState=PULL_TO_REFRESH;
-
}
-
}else{
-
mRefreshViewImage.setVisibility(View.GONE);
-
resetHeader();
-
}
-
-
}elseif(mCurrentScrollState==SCROLL_STATE_FLING&&firstVisibleItem==0
-
&&mRefreshState!=REFRESHING){
-
setSelection(1);
-
mBounceHack=true;
-
}elseif(mBounceHack&&mCurrentScrollState==SCROLL_STATE_FLING){
-
setSelection(1);
-
}
-
if(mOnScrollListener!=null){
-
mOnScrollListener.onScroll(view,firstVisibleItem,visibleItemCount,totalItemCount);
-
}
-
}
该方法是在滑动过程中,各种状况的处理。
onScroll()方法和onTouchEvent()方法的执行过程应该是,先onTouchEvent()的ACTION_DOWN,然后是ACTION_MOVE和onScroll()方法同时进行,最后是onTouchEvent()的ACTION_UP。也可以自己打log看一下。这样在onTouchEvent()处理header,就是mRefreshView的外部的各个熟悉,onScroll()里面处理header(mRefreshView)里面内部的控件变化,从逻辑上来说比较清晰。
在onScroll()中,引用方法resetHeader()方法:
-
-
-
-
-
privatevoidresetHeader(){
-
if(mRefreshState!=TAP_TO_REFRESH){
-
mRefreshState=TAP_TO_REFRESH;
-
resetHeaderPadding();
-
-
-
mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);
-
-
-
mRefreshViewImage.setImageResource(REFRESHICON);
-
-
mRefreshViewImage.clearAnimation();
-
-
-
mRefreshViewImage.setVisibility(View.GONE);
-
mRefreshViewProgress.setVisibility(View.GONE);
-
}
-
}
resetHead就是header(mRefreshView)的内部的具体操作。
当一切都完成以后,就可以调用onRefreshComplete()方法:
-
-
-
-
-
-
-
-
publicvoidonRefreshComplete(CharSequencelastUpdated){
-
setLastUpdated(lastUpdated);
-
onRefreshComplete();
-
}
-
-
-
-
-
publicvoidonRefreshComplete(){
-
resetHeader();
-
-
-
if(mRefreshView.getBottom()>0){
-
invalidateViews();
-
setSelection(1);
-
}
-
}
重新绘制listivew,然后setSelection(1)。完成!
最后是源代码的下载地址:http://download.csdn.net/detail/aomandeshangxiao/4117390
还有其他两篇相关:listView下拉刷新2,listView滑动刷新代码(分页功能)。
分享到:
相关推荐
android activity选项卡 listview 下拉刷新 仿新浪微博 提供现成demo
android listview 下拉刷新 上拉翻页 仿新浪微博客户端 完美的demo
ListView下拉刷新 ListView下拉刷新ListView下拉刷新ListView下拉刷新
仿照新浪微博Android客户端个人中心的ScrollView.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
仿新浪微博Android客户端主界面的源码。
Android ListView下拉刷新 Demo.rar
android 实现listview动态下拉刷新,动态加载数据 可以避免一次数据加载过多
Android_仿新浪微博的ListView下拉更新功能
android Listview下拉刷新 上拉(滑动分页)加载更多 高仿新浪微博下拉刷新,同时实现了滑动加载下一页
android listview 下拉回弹刷新效果, 现在,QQ空间和新浪微博上的效果,是一个比较炫的效果。 你想让你的APP也有这样炫的效果吗? 赶快下载体验吧!
Android listview 下拉 刷新
免责声明:资料部分来源于合法的互联网渠道收集和整理,部分自己学习积累成果,供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者或出版方,资料版权归原作者或出版方所有,...
仿新浪微博Android客户端主界面的源码。
NULL 博文链接:https://geningaixin.iteye.com/blog/2034379
ListView ListGridScroll下拉刷新 下拉加载 代码可直接运行
仿照手机微博刷新列表,下拉listview松手后刷新数据
基于Android的新浪微博系统开发01-开发概述 基于Android的新浪微博系统开发02-下载微博sdk ...基于Android的新浪微博系统开发07-不断扩展的ListView 基于Android的新浪微博系统开发08-动态获取Icon和局部TextVieww
Android 顶部可以伸缩+头部固定+listview下拉刷新、上拉请求更多
解决Scrollview嵌套listview下拉刷新的一些问题
整理了下以前写的小项目,ListView的下拉刷新,虽然小但还是想纪念下。。适合新手看,大神略过。。。