Android的ListView是应用最广的一个组件,功能强大,扩展性灵活(不局限于ListView本身一个类),前面的文章有介绍分组,拖拽,3D立体,游标,圆角,而今天我们要介绍的是另外一个扩展ListView:下拉刷新的ListView。
下拉刷新界面最初流行于iphone应用界面,如图:

然后在Android中也逐渐被应用,比如微博,资讯类。
所以,今天要实现的结果应该也是类似的,先贴出最终完成效果,如下图,接下来我们一步一步实现。

1. 流程分析
下拉刷新最主要的流程是:
(1). 下拉,显示提示头部界面(HeaderView),这个过程提示用户"下拉刷新"
(2). 下拉到一定程度,超出了刷新最基本的下拉界限,我们认为达到了刷新的条件,提示用户可以"松手刷新"了,效果上允许用户继续下拉
(3). 用户松手,可能用户下拉远远不止提示头部界面,所以这一步,先反弹回仅显示提示头部界面,然后提示用户"正在加载"。
(4). 加载完成后,隐藏提示头部界面。
示意图如下:

2. 实现分析
当前我们要实现上述流程,是基于ListView的,所以对应ListView本身的功能我们来分析一下实现原理:
(1). 下拉,显示提示头部界面,这个过程提示用户"下拉刷新"
a. 下拉的操作,首先是监听滚动,ListView提供了onScroll()方法
b. 与下拉类似一个动作向下飞滑,所以ListView的scrollState有3种值:SCROLL_STATE_IDLE,SCROLL_STATE_TOUCH_SCROLL,SCROLL_STATE_FLING,意思容易理解,而我们要下拉的触发条件是SCROLL_STATE_TOUCH_SCROLL。判断当前的下拉操作状态,ListView提供了public void onScrollStateChanged(AbsListView view, int scrollState) {}。
c. 下拉的过程中,我们可能还需要下拉到多少的边界值处理,重写onTouchEvent(MotionEvent ev){}方法,可依据ACTION_DOWN,ACTION_MOVE,ACTION_UP实现更精细的判断。
(2). 下拉到一定程度,超出了刷新最基本的下拉界限,我们认为达到了刷新的条件,提示用户可以"松手刷新"了,效果上允许用户继续下拉
a. 达到下拉刷新界限,一般指达到header的高度的,所以有两步,第一,获取header的高度,第二,当header.getBottom()>=header的高度时,我们认为就达到了刷新界限值
b. 继续允许用户下拉,当header完全下拉后,默认无法继续下拉,但是可以增加header的PaddingTop实现这种效果
(3). 用户松手,可能用户下拉远远不止提示头部界面,所以这一步,先反弹回仅显示提示头部界面,然后提示用户"正在加载"。
a. 松手后反弹,这个不能一下***回去,看上去太突然,需要一步一步柔性的弹回去,像弹簧一样,我们可以new一个Thread循环计算减少PaddingTop,直到PaddingTop为0,反弹结束。
b. 正在加载,在子线程里处理后台任务
(4). 加载完成后,隐藏提示头部界面。
a. 后台任务完成后,我们需要隐藏header,setSelection(1)即实现了从第2项开始显示,间接隐藏了header。
上面我们分析了实现过程的轮廓,接下来,通过细节说明和代码具体实现。

3. 初始化
一切状态显示都是用HeaderView显示的,所以我们需要一个HeaderView的layout,使用addHeaderView方法添加到ListView中。
同时,默认状态下,HeaderView是不显示的,只是在下拉后才显示,所以我们需要隐藏HeaderView且不影响后续的下拉显示,用setSelection(1)。
refresh_list_header.xml布局如下:

<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center"><ProgressBarandroid:id="@+id/refresh_list_header_progressbar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"style="?android:attr/progressBarStyleSmall"android:visibility="gone"></ProgressBar><ImageViewandroid:id="@+id/refresh_list_header_pull_down"android:layout_width="9dip"android:layout_height="25dip"android:layout_gravity="center"android:src="@drawable/refresh_list_pull_down"/><ImageViewandroid:id="@+id/refresh_list_header_release_up"android:layout_width="9dip"android:layout_height="25dip"android:layout_gravity="center"android:src="@drawable/refresh_list_release_up"android:visibility="gone"/><RelativeLayoutandroid:layout_width="180dip"android:layout_height="wrap_content"><TextViewandroid:id="@+id/refresh_list_header_text"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center"android:layout_alignParentTop="true"android:textSize="12dip"android:textColor="#192F06"android:paddingTop="8dip"android:text="@string/app_list_header_refresh_down"/><TextViewandroid:id="@+id/refresh_list_header_last_update"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center"android:layout_below="@id/refresh_list_header_text"android:textSize="12dip"android:textColor="#192F06"android:paddingBottom="8dip"android:text="@string/app_list_header_refresh_last_update"/></RelativeLayout></LinearLayout>

代码中在构造函数中添加init()方法加载如下:

privateLinearLayoutmHeaderLinearLayout=null;privateTextViewmHeaderTextView=null;privateTextViewmHeaderUpdateText=null;privateImageViewmHeaderPullDownImageView=null;privateImageViewmHeaderReleaseDownImageView=null;privateProgressBarmHeaderProgressBar=null;publicRefreshListView(Contextcontext){this(context,null);}publicRefreshListView(Contextcontext,AttributeSetattrs){super(context,attrs);init(context);}voidinit(finalContextcontext){mHeaderLinearLayout=(LinearLayout)LayoutInflater.from(context).inflate(R.layout.refresh_list_header,null);addHeaderView(mHeaderLinearLayout);mHeaderTextView=(TextView)findViewById(R.id.refresh_list_header_text);mHeaderUpdateText=(TextView)findViewById(R.id.refresh_list_header_last_update);mHeaderPullDownImageView=(ImageView)findViewById(R.id.refresh_list_header_pull_down);mHeaderReleaseDownImageView=(ImageView)findViewById(R.id.refresh_list_header_release_up);mHeaderProgressBar=(ProgressBar)findViewById(R.id.refresh_list_header_progressbar);setSelection(1);}

默认就显示完成了。

4. HeaderView的默认高度测量
因为下拉到HeaderView全部显示出来,就由提示"下拉刷新"变为"松手刷新",全部显示的出来的测量标准就是header.getBottom()>=header的高度。
所以,首先我们需要测量HeaderView的默认高度。

//因为是在构造函数里测量高度,应该先measure一下privatevoidmeasureView(Viewchild){ViewGroup.LayoutParamsp=child.getLayoutParams();if(p==null){p=newViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);}intchildWidthSpec=ViewGroup.getChildMeasureSpec(0,0+0,p.width);intlpHeight=p.height;intchildHeightSpec;if(lpHeight>0){childHeightSpec=MeasureSpec.makeMeasureSpec(lpHeight,MeasureSpec.EXACTLY);}else{childHeightSpec=MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);}child.measure(childWidthSpec,childHeightSpec);}

然后在init的上述代码后面加上调用measureView后,使用getMeasureHeight()方法获取header的高度:

privateintmHeaderHeight;voidinit(finalContextcontext){......measureView(mHeaderLinearLayout);mHeaderHeight=mHeaderLinearLayout.getMeasuredHeight();}

  后面我们就会用到这个mHeaderHeight.

5. scrollState监听记录
scrollState有3种,使用onScrollStateChanged()方法监听记录。

privateintmCurrentScrollState;@OverridepublicvoidonScrollStateChanged(AbsListViewview,intscrollState){mCurrentScrollState=scrollState;}

然后即可使用mCurrentScrollState作为后面判断的条件了。

6. 刷新状态分析
因为一些地方需要知道我们处在正常状态下还是进入下拉刷新状态还是松手反弹状态,比如,
(1). 在非正常的状态下,我们不小心飞滑了一下(松手的瞬间容易出现这种情况),我们不能setSelection(1)的,否则总是松手后header跳的一下消失掉了。
(2). 下拉后要做一个下拉效果的特殊处理,需要用到OVER_PULL_REFRESH(松手刷新状态下)
(3). 松手反弹后要做一个反弹效果的特殊处理,需要用到OVER_PULL_REFRESH和ENTER_PULL_REFRESH。

privatefinalstaticintNONE_PULL_REFRESH=0;//正常状态privatefinalstaticintENTER_PULL_REFRESH=1;//进入下拉刷新状态privatefinalstaticintOVER_PULL_REFRESH=2;//进入松手刷新状态privatefinalstaticintEXIT_PULL_REFRESH=3;//松手后反弹后加载状态privateintmPullRefreshState=0;//记录刷新状态@OverridepublicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){if(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&firstVisibleItem==0&&(mHeaderLinearLayout.getBottom()>=0&&mHeaderLinearLayout.getBottom()<mHeaderHeight)){//进入且仅进入下拉刷新状态if(mPullRefreshState==NONE_PULL_REFRESH){mPullRefreshState=ENTER_PULL_REFRESH;}}elseif(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&firstVisibleItem==0&&(mHeaderLinearLayout.getBottom()>=mHeaderHeight)){//下拉达到界限,进入松手刷新状态if(mPullRefreshState==ENTER_PULL_REFRESH||mPullRefreshState==NONE_PULL_REFRESH){mPullRefreshState=OVER_PULL_REFRESH;//下面是进入松手刷新状态需要做的一个显示改变mDownY=mMoveY;//用于后面的下拉特殊效果mHeaderTextView.setText("松手刷新");mHeaderPullDownImageView.setVisibility(View.GONE);mHeaderReleaseDownImageView.setVisibility(View.VISIBLE);}}elseif(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&firstVisibleItem!=0){//不刷新了if(mPullRefreshState==ENTER_PULL_REFRESH){mPullRefreshState=NONE_PULL_REFRESH;}}elseif(mCurrentScrollState==SCROLL_STATE_FLING&&firstVisibleItem==0){//飞滑状态,不能显示出header,也不能影响正常的飞滑//只在正常情况下才纠正位置if(mPullRefreshState==NONE_PULL_REFRESH){setSelection(1);}}}

  mPullRefreshState将是后面我们处理边界的重要变量。

6. 下拉效果的特殊处理
所谓的特殊处理,当header完全显示后,下拉只按下拉1/3的距离下拉,给用户一种艰难下拉,该松手的弹簧感觉。
这个在onTouchEvent里处理比较方便:

privatefloatmDownY;privatefloatmMoveY;@OverridepublicbooleanonTouchEvent(MotionEventev){switch(ev.getAction()){caseMotionEvent.ACTION_DOWN://记下按下位置//改变mDownY=ev.getY();break;caseMotionEvent.ACTION_MOVE://移动时手指的位置mMoveY=ev.getY();if(mPullRefreshState==OVER_PULL_REFRESH){//注意下面的mDownY在onScroll的第二个else中被改变了mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),(int)((mMoveY-mDownY)/3),//1/3距离折扣mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());}break;caseMotionEvent.ACTION_UP:......break;}returnsuper.onTouchEvent(ev);}//重复贴出下面这段需要注意的代码@OverridepublicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){......elseif(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&firstVisibleItem==0&&(mHeaderLinearLayout.getBottom()>=mHeaderHeight)){//下拉达到界限,进入松手刷新状态if(mPullRefreshState==ENTER_PULL_REFRESH||mPullRefreshState==NONE_PULL_REFRESH){mPullRefreshState=OVER_PULL_REFRESH;mDownY=mMoveY;//为下拉1/3折扣效果记录开始位置mHeaderTextView.setText("松手刷新");//显示松手刷新mHeaderPullDownImageView.setVisibility(View.GONE);//隐藏"下拉刷新"mHeaderReleaseDownImageView.setVisibility(View.VISIBLE);//显示向上的箭头}}......}

  onScroll里监听到了进入松手刷新状态,onTouchEvent就开始在ACTION_MOVE中处理1/3折扣问题。

7. 反弹效果的特殊处理
松手后我们需要一个柔性的反弹效果,意味着我们弹回去的过程需要分一步步走,我的解决方案是:
在子线程里计算PaddingTop,并减少到原来的3/4,循环通知主线程,直到PaddingTop小于1(这个值取一个小值,合适即可)。
松手后,当然是在onTouchEvent的ACTION_UP条件下处理比较方便:

//因为涉及到handler数据处理,为方便我们定义如下常量privatefinalstaticintREFRESH_BACKING=0;//反弹中privatefinalstaticintREFRESH_BACED=1;//达到刷新界限,反弹结束后privatefinalstaticintREFRESH_RETURN=2;//没有达到刷新界限,返回privatefinalstaticintREFRESH_DONE=3;//加载数据结束@OverridepublicbooleanonTouchEvent(MotionEventev){switch(ev.getAction()){......caseMotionEvent.ACTION_UP://whenyouactionup,itwilldothese://1.rollbackutilheadertopPaddingis0//2.hidetheheaderbysetSelection(1)if(mPullRefreshState==OVER_PULL_REFRESH||mPullRefreshState==ENTER_PULL_REFRESH){newThread(){publicvoidrun(){Messagemsg;while(mHeaderLinearLayout.getPaddingTop()>1){msg=mHandler.obtainMessage();msg.what=REFRESH_BACKING;mHandler.sendMessage(msg);try{sleep(5);//慢一点反弹,别一下子就弹回去了}catch(InterruptedExceptione){e.printStackTrace();}}msg=mHandler.obtainMessage();if(mPullRefreshState==OVER_PULL_REFRESH){msg.what=REFRESH_BACED;//加载数据完成,结束返回}else{msg.what=REFRESH_RETURN;//未达到刷新界限,直接返回}mHandler.sendMessage(msg);};}.start();}break;}returnsuper.onTouchEvent(ev);}privateHandlermHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){switch(msg.what){caseREFRESH_BACKING:mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),(int)(mHeaderLinearLayout.getPaddingTop()*0.75f),mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());break;caseREFRESH_BACED:mHeaderTextView.setText("正在加载...");mHeaderProgressBar.setVisibility(View.VISIBLE);mHeaderPullDownImageView.setVisibility(View.GONE);mHeaderReleaseDownImageView.setVisibility(View.GONE);mPullRefreshState=EXIT_PULL_REFRESH;newThread(){publicvoidrun(){sleep(2000);//处理后台加载数据Messagemsg=mHandler.obtainMessage();msg.what=REFRESH_DONE;//通知主线程加载数据完成mHandler.sendMessage(msg);};}.start();break;caseREFRESH_RETURN://未达到刷新界限,返回mHeaderTextView.setText("下拉刷新");mHeaderProgressBar.setVisibility(View.INVISIBLE);mHeaderPullDownImageView.setVisibility(View.VISIBLE);mHeaderReleaseDownImageView.setVisibility(View.GONE);mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),0,mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());mPullRefreshState=NONE_PULL_REFRESH;setSelection(1);break;caseREFRESH_DONE://刷新结束后,恢复原始默认状态mHeaderTextView.setText("下拉刷新");mHeaderProgressBar.setVisibility(View.INVISIBLE);mHeaderPullDownImageView.setVisibility(View.VISIBLE);mHeaderReleaseDownImageView.setVisibility(View.GONE);mHeaderUpdateText.setText(getContext().getString(R.string.app_list_header_refresh_last_update,mSimpleDateFormat.format(newDate())));mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),0,mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());mPullRefreshState=NONE_PULL_REFRESH;setSelection(1);break;default:break;}}};

为了一下子看的明确,我把效果中的数据处理代码也贴出来了。

8. 切入数据加载过程
上面数据后台处理我们用sleep(2000)来处理,实际处理中,作为公共组件,我们也不好把具体代码直接写在这里,我们需要一个更灵活的分离:
(1). 定义接口
(2). 注入接口

//定义接口publicinterfaceRefreshListener{Objectrefreshing();//加载数据voidrefreshed(Objectobj);//外部可扩展加载完成后的操作}//注入接口privateObjectmRefreshObject=null;//传值privateRefreshListenermRefreshListener=null;publicvoidsetOnRefreshListener(RefreshListenerrefreshListener){this.mRefreshListener=refreshListener;}//我们需要重写上面的mHandler如下代码caseREFRESH_BACED:......newThread(){publicvoidrun(){if(mRefreshListener!=null){mRefreshObject=mRefreshListener.refreshing();}Messagemsg=mHandler.obtainMessage();msg.what=REFRESH_DONE;mHandler.sendMessage(msg);};}.start();break;caseREFRESH_DONE:......mPullRefreshState=NONE_PULL_REFRESH;setSelection(1);if(mRefreshListener!=null){mRefreshListener.refreshed(mRefreshObject);}break;

在其他地方我们就可以不修改这个listview组件的代码,使用如下:

publicxxximplementsRefreshListener{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);//类似如下((RefreshListView)listView).setOnRefreshListener(this);}@OverridepublicObjectrefreshing(){Stringresult=null;//result=FileUtils.readTextFile(file);returnresult;}@Overridepublicvoidrefreshed(Objectobj){if(obj!=null){//扩展操作}};}

  很方便了。

9. 扩展"更多"功能
下拉刷新之外,我们也可以通过相同方法使用FooterView切入底部"更多"过程,这里我就不详细说明了

10. 源码
上面的每段代码都看做是"零部件",需要组合一下。
因为我们上面实现了下拉刷新,还增加了"更多"功能,我们直接命名这个类为RefreshListView吧:

packagecom.tianxia.lib.baseworld.widget;importjava.text.SimpleDateFormat;importjava.util.Date;importandroid.content.Context;importandroid.os.Handler;importandroid.os.Message;importandroid.util.AttributeSet;importandroid.view.LayoutInflater;importandroid.view.MotionEvent;importandroid.view.View;importandroid.view.ViewGroup;importandroid.widget.AbsListView;importandroid.widget.AbsListView.OnScrollListener;importandroid.widget.ImageView;importandroid.widget.LinearLayout;importandroid.widget.ListAdapter;importandroid.widget.ListView;importandroid.widget.ProgressBar;importandroid.widget.TextView;importcom.tianxia.lib.baseworld.R;/***下拉刷新,底部更多**/publicclassRefreshListViewextendsListViewimplementsOnScrollListener{privatefloatmDownY;privatefloatmMoveY;privateintmHeaderHeight;privateintmCurrentScrollState;privatefinalstaticintNONE_PULL_REFRESH=0;//正常状态privatefinalstaticintENTER_PULL_REFRESH=1;//进入下拉刷新状态privatefinalstaticintOVER_PULL_REFRESH=2;//进入松手刷新状态privatefinalstaticintEXIT_PULL_REFRESH=3;//松手后反弹和加载状态privateintmPullRefreshState=0;//记录刷新状态privatefinalstaticintREFRESH_BACKING=0;//反弹中privatefinalstaticintREFRESH_BACED=1;//达到刷新界限,反弹结束后privatefinalstaticintREFRESH_RETURN=2;//没有达到刷新界限,返回privatefinalstaticintREFRESH_DONE=3;//加载数据结束privateLinearLayoutmHeaderLinearLayout=null;privateLinearLayoutmFooterLinearLayout=null;privateTextViewmHeaderTextView=null;privateTextViewmHeaderUpdateText=null;privateImageViewmHeaderPullDownImageView=null;privateImageViewmHeaderReleaseDownImageView=null;privateProgressBarmHeaderProgressBar=null;privateTextViewmFooterTextView=null;privateProgressBarmFooterProgressBar=null;privateSimpleDateFormatmSimpleDateFormat;privateObjectmRefreshObject=null;privateRefreshListenermRefreshListener=null;publicvoidsetOnRefreshListener(RefreshListenerrefreshListener){this.mRefreshListener=refreshListener;}publicRefreshListView(Contextcontext){this(context,null);}publicRefreshListView(Contextcontext,AttributeSetattrs){super(context,attrs);init(context);}voidinit(finalContextcontext){mHeaderLinearLayout=(LinearLayout)LayoutInflater.from(context).inflate(R.layout.refresh_list_header,null);addHeaderView(mHeaderLinearLayout);mHeaderTextView=(TextView)findViewById(R.id.refresh_list_header_text);mHeaderUpdateText=(TextView)findViewById(R.id.refresh_list_header_last_update);mHeaderPullDownImageView=(ImageView)findViewById(R.id.refresh_list_header_pull_down);mHeaderReleaseDownImageView=(ImageView)findViewById(R.id.refresh_list_header_release_up);mHeaderProgressBar=(ProgressBar)findViewById(R.id.refresh_list_header_progressbar);mFooterLinearLayout=(LinearLayout)LayoutInflater.from(context).inflate(R.layout.refresh_list_footer,null);addFooterView(mFooterLinearLayout);mFooterProgressBar=(ProgressBar)findViewById(R.id.refresh_list_footer_progressbar);mFooterTextView=(TextView)mFooterLinearLayout.findViewById(R.id.refresh_list_footer_text);mFooterLinearLayout.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){if(context.getString(R.string.app_list_footer_more).equals(mFooterTextView.getText())){mFooterTextView.setText(R.string.app_list_footer_loading);mFooterProgressBar.setVisibility(View.VISIBLE);if(mRefreshListener!=null){mRefreshListener.more();}}}});setSelection(1);setOnScrollListener(this);measureView(mHeaderLinearLayout);mHeaderHeight=mHeaderLinearLayout.getMeasuredHeight();mSimpleDateFormat=newSimpleDateFormat("yyyy-MM-ddhh:mm");mHeaderUpdateText.setText(context.getString(R.string.app_list_header_refresh_last_update,mSimpleDateFormat.format(newDate())));}@OverridepublicbooleanonTouchEvent(MotionEventev){switch(ev.getAction()){caseMotionEvent.ACTION_DOWN:mDownY=ev.getY();break;caseMotionEvent.ACTION_MOVE:mMoveY=ev.getY();if(mPullRefreshState==OVER_PULL_REFRESH){mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),(int)((mMoveY-mDownY)/3),mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());}break;caseMotionEvent.ACTION_UP://whenyouactionup,itwilldothese://1.rollbackutilheadertopPaddingis0//2.hidetheheaderbysetSelection(1)if(mPullRefreshState==OVER_PULL_REFRESH||mPullRefreshState==ENTER_PULL_REFRESH){newThread(){publicvoidrun(){Messagemsg;while(mHeaderLinearLayout.getPaddingTop()>1){msg=mHandler.obtainMessage();msg.what=REFRESH_BACKING;mHandler.sendMessage(msg);try{sleep(5);}catch(InterruptedExceptione){e.printStackTrace();}}msg=mHandler.obtainMessage();if(mPullRefreshState==OVER_PULL_REFRESH){msg.what=REFRESH_BACED;}else{msg.what=REFRESH_RETURN;}mHandler.sendMessage(msg);};}.start();}break;}returnsuper.onTouchEvent(ev);}@OverridepublicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){if(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&firstVisibleItem==0&&(mHeaderLinearLayout.getBottom()>=0&&mHeaderLinearLayout.getBottom()<mHeaderHeight)){//进入且仅进入下拉刷新状态if(mPullRefreshState==NONE_PULL_REFRESH){mPullRefreshState=ENTER_PULL_REFRESH;}}elseif(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&firstVisibleItem==0&&(mHeaderLinearLayout.getBottom()>=mHeaderHeight)){//下拉达到界限,进入松手刷新状态if(mPullRefreshState==ENTER_PULL_REFRESH||mPullRefreshState==NONE_PULL_REFRESH){mPullRefreshState=OVER_PULL_REFRESH;mDownY=mMoveY;//为下拉1/3折扣效果记录开始位置mHeaderTextView.setText("松手刷新");//显示松手刷新mHeaderPullDownImageView.setVisibility(View.GONE);//隐藏"下拉刷新"mHeaderReleaseDownImageView.setVisibility(View.VISIBLE);//显示向上的箭头}}elseif(mCurrentScrollState==SCROLL_STATE_TOUCH_SCROLL&&firstVisibleItem!=0){//不刷新了if(mPullRefreshState==ENTER_PULL_REFRESH){mPullRefreshState=NONE_PULL_REFRESH;}}elseif(mCurrentScrollState==SCROLL_STATE_FLING&&firstVisibleItem==0){//飞滑状态,不能显示出header,也不能影响正常的飞滑//只在正常情况下才纠正位置if(mPullRefreshState==NONE_PULL_REFRESH){setSelection(1);}}}@OverridepublicvoidonScrollStateChanged(AbsListViewview,intscrollState){mCurrentScrollState=scrollState;}@OverridepublicvoidsetAdapter(ListAdapteradapter){super.setAdapter(adapter);setSelection(1);}privatevoidmeasureView(Viewchild){ViewGroup.LayoutParamsp=child.getLayoutParams();if(p==null){p=newViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);}intchildWidthSpec=ViewGroup.getChildMeasureSpec(0,0+0,p.width);intlpHeight=p.height;intchildHeightSpec;if(lpHeight>0){childHeightSpec=MeasureSpec.makeMeasureSpec(lpHeight,MeasureSpec.EXACTLY);}else{childHeightSpec=MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);}child.measure(childWidthSpec,childHeightSpec);}privateHandlermHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){switch(msg.what){caseREFRESH_BACKING:mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),(int)(mHeaderLinearLayout.getPaddingTop()*0.75f),mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());break;caseREFRESH_BACED:mHeaderTextView.setText("正在加载...");mHeaderProgressBar.setVisibility(View.VISIBLE);mHeaderPullDownImageView.setVisibility(View.GONE);mHeaderReleaseDownImageView.setVisibility(View.GONE);mPullRefreshState=EXIT_PULL_REFRESH;newThread(){publicvoidrun(){if(mRefreshListener!=null){mRefreshObject=mRefreshListener.refreshing();}Messagemsg=mHandler.obtainMessage();msg.what=REFRESH_DONE;mHandler.sendMessage(msg);};}.start();break;caseREFRESH_RETURN:mHeaderTextView.setText("下拉刷新");mHeaderProgressBar.setVisibility(View.INVISIBLE);mHeaderPullDownImageView.setVisibility(View.VISIBLE);mHeaderReleaseDownImageView.setVisibility(View.GONE);mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),0,mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());mPullRefreshState=NONE_PULL_REFRESH;setSelection(1);break;caseREFRESH_DONE:mHeaderTextView.setText("下拉刷新");mHeaderProgressBar.setVisibility(View.INVISIBLE);mHeaderPullDownImageView.setVisibility(View.VISIBLE);mHeaderReleaseDownImageView.setVisibility(View.GONE);mHeaderUpdateText.setText(getContext().getString(R.string.app_list_header_refresh_last_update,mSimpleDateFormat.format(newDate())));mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),0,mHeaderLinearLayout.getPaddingRight(),mHeaderLinearLayout.getPaddingBottom());mPullRefreshState=NONE_PULL_REFRESH;setSelection(1);if(mRefreshListener!=null){mRefreshListener.refreshed(mRefreshObject);}break;default:break;}}};publicinterfaceRefreshListener{Objectrefreshing();voidrefreshed(Objectobj);voidmore();}publicvoidfinishFootView(){mFooterProgressBar.setVisibility(View.GONE);mFooterTextView.setText(R.string.app_list_footer_more);}publicvoidaddFootView(){if(getFooterViewsCount()==0){addFooterView(mFooterLinearLayout);}}publicvoidremoveFootView(){removeFooterView(mFooterLinearLayout);}}


更多相关文章

  1. Android免费获取短信验证码
  2. Android入门教程(八)之-----简单的Button事件响应综合提示控件To
  3. android横竖屏和隐藏标题栏、状态栏总结
  4. 大神之路:重学Android——Android多媒体MediaPlayer
  5. Android如何一步步实现状态栏一体化效果
  6. Android(安卓)进阶学习:Android视图状态及重绘流程分析,带你一步步
  7. Genymotion下载vbox提示connection timeout的办法
  8. android -- 测试器 AVD 虚拟设备
  9. Android(安卓)沉寖状态栏与透明状态栏

随机推荐

  1. Activity 与 ActivityManagerService 的
  2. Android(安卓)设置图片 Bitmap任意透明度
  3. HasMap 与 SparseArray
  4. Android中的AOP
  5. 最简单的android底部导航栏 + Fragment的
  6. [Android分享] 如何解决Android 5.0中出
  7. android native c 的so调试
  8. android中对sim卡联系人的增删改查以及监
  9. Android 关闭/打开多点触控 (Android中设
  10. android 文字尺寸(宽高)的测量