android中实现ListView的刷新Demo

后面的很多例子应该都是仿照这个写的,下面的这个例子就是对这个例子的修改,先看下一个点击的效果,我看到其他的分析博客里面没有谈到这一点,在这个代码中,我们一直看到是listview的第二项,而listview的第一项被遮挡了起来,滑动至第一项:

       点击头条,头条会变成以下:

然后,过一段时间,刷新完成以后,listviewsetSelection(1),增加一条数据,同时,把顶部给遮挡住:

这是点击刷新,然后是下拉刷新:

       最后结果和点击刷新相同。那现在开始看下代码:

       首先看下所用到的控件和变量:

[java] view plaincopy

1.     // 状态  

2.         private static final int TAP_TO_REFRESH = 1;//点击刷新  

3.         private static final int PULL_TO_REFRESH = 2;  //拉动刷新   

4.         private static final int RELEASE_TO_REFRESH = 3//释放刷新  

5.         private static final int REFRESHING = 4;  //正在刷新  

6.         // 当前滑动状态  

7.         private int mCurrentScrollState;  

8.         // 当前刷新状态   

9.         private int mRefreshState;  

10.      //头视图的高度  

11.      private int mRefreshViewHeight;  

12.      //头视图 原始的top padding 属性值  

13.      private int mRefreshOriginalTopPadding;  

14.      private int mLastMotionY;  

15.      // 监听对listview的滑动动作  

16.      private OnRefreshListener mOnRefreshListener;  

17.      //箭头图片  

18.      private static  int REFRESHICON = R.drawable.goicon;      

19.      //listview 滚动监听器  

20.      private OnScrollListener mOnScrollListener;  

21.      private LayoutInflater mInflater;  

22.      private RelativeLayout mRefreshView;  

23.      //顶部刷新时出现的控件  

24.      private TextView mRefreshViewText;  

25.      private ImageView mRefreshViewImage;  

26.      private ProgressBar mRefreshViewProgress;  

27.      private TextView mRefreshViewLastUpdated;  

28.      // 箭头动画效果  

29.      //变为向下的箭头  

30.      private RotateAnimation mFlipAnimation;  

31.      //变为逆向的箭头  

32.      private RotateAnimation mReverseFlipAnimation;  

33.      //是否反弹  

34.      private boolean mBounceHack;  

看下点击刷新的代码过程:

init()方法中初始化各个控件及设置监听:

[java] view plaincopy

1.     private void init(Context context) {  

2.             // Load all of the animations we need in code rather than through XML     

3.             mFlipAnimation = new RotateAnimation(0, -180,RotateAnimation.RELATIVE_TO_SELF,   

4.                     0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);  

5.             mFlipAnimation.setInterpolator(new LinearInterpolator());  

6.             mFlipAnimation.setDuration(250);  

7.             mFlipAnimation.setFillAfter(true);  

8.                            

9.             mReverseFlipAnimation = new RotateAnimation(-1800,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);  

10.          mReverseFlipAnimation.setInterpolator(new LinearInterpolator());  

11.          mReverseFlipAnimation.setDuration(250);  

12.          mReverseFlipAnimation.setFillAfter(true);  

13.    

14.          mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  

15.          mRefreshView = (RelativeLayout) mInflater.inflate(R.layout.pull_to_refresh_header, thisfalse);  

16.          mRefreshViewText =(TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text);  

17.          mRefreshViewImage =(ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image);  

18.          mRefreshViewProgress =(ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress);  

19.          mRefreshViewLastUpdated =(TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);  

20.    

21.          mRefreshViewImage.setMinimumHeight(50);  

22.          mRefreshView.setOnClickListener(new OnClickRefreshListener());  

23.          mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();  

24.          mRefreshState = TAP_TO_REFRESH;  

25.          //listview头部增加一个view    

26.          addHeaderView(mRefreshView);  

27.          super.setOnScrollListener(this);  

28.          measureView(mRefreshView);  

29.          mRefreshViewHeight = mRefreshView.getMeasuredHeight();    

30.      }  

 

我们看到,mRefreshView控件既是listview用于刷新的头控件,这里它设置了监听事件:

[java] view plaincopy

1.     mRefreshView.setOnClickListener(new OnClickRefreshListener());  


我们再来看下监听事件的定义:

[java] view plaincopy

1.     private class OnClickRefreshListener implements OnClickListener {  

2.             @Override  

3.             public void onClick(View v) {  

4.                 if (mRefreshState != REFRESHING) {  

5.                     prepareForRefresh();    

6.                     onRefresh();   

7.                 }  

8.             }  

9.         }  


调用了preparForRefresh()(准备刷新)和onRefresh()(刷新)两个方法,然后在查看这两个方法的定义:

[java] view plaincopy

1.     public void prepareForRefresh() {  

2.             resetHeaderPadding();   // 恢复header的边距   

3.             mRefreshViewImage.setVisibility(View.GONE);  

4.             // We need this hack, otherwise it will keep the previous drawable.  

5.             // 注意加上,否则仍然显示之前的图片    

6.             mRefreshViewImage.setImageDrawable(null);  

7.             mRefreshViewProgress.setVisibility(View.VISIBLE);  

8.             // Set refresh view text to the refreshing label  

9.            mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);  

10.          mRefreshState = REFRESHING;  

11.      }  

12.      public void onRefresh() {  

13.          if (mOnRefreshListener != null) {  

14.              mOnRefreshListener.onRefresh();  

15.          }  

16.      }  


其中,后者还是回调方法。

我们看下preparForRefresh()方法中,引用了resetHeadPadding()方法:

[java] view plaincopy

1.     /**  

2.          * Sets the header padding back to original size. 

3.          * head的边距重置为初始的数值  

4.          */   

5.         private void resetHeaderPadding() {  

6.             mRefreshView.setPadding(  

7.                     mRefreshView.getPaddingLeft(),  

8.                     mRefreshOriginalTopPadding,  

9.                     mRefreshView.getPaddingRight(),  

10.                  mRefreshView.getPaddingBottom());  

11.      }      


从新设置下header距上下左右的距离。

最重要的方法应该是:onScroll()onTouchEvent()方法,先看下onTouchEvent()方法:

[java] view plaincopy

1.     @Override  

2.         public boolean onTouchEvent(MotionEvent event) {  

3.             //当前手指的Y  

4.             final int y = (int) event.getY();  

5.             mBounceHack = false;     

6.             switch (event.getAction()) {  

7.                 case MotionEvent.ACTION_UP:  

8.                     //将垂直滚动条设置为可用状态  

9.                     if (!isVerticalScrollBarEnabled()) {  

10.                      setVerticalScrollBarEnabled(true);  

11.                  }  

12.                  if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {  

13.                      // 拖动距离达到刷新需要  

14.                      if ((mRefreshView.getBottom() >= mRefreshViewHeight  

15.                              || mRefreshView.getTop() >= 0)  

16.                              && mRefreshState == RELEASE_TO_REFRESH) {    

17.                          // 把状态设置为正在刷新  

18.                          // Initiate the refresh  

19.                          mRefreshState = REFRESHING; //将标量设置为,正在刷新  

20.                          // 准备刷新  

21.                          prepareForRefresh();    

22.                          // 刷新    

23.                          onRefresh();    

24.                      } else if (mRefreshView.getBottom() < mRefreshViewHeight  

25.                              || mRefreshView.getTop() <= 0) {  

26.                          // Abort refresh and scroll down below the refresh view  

27.                          //停止刷新,并且滚动到头部刷新视图的下一个视图  

28.                          resetHeader();  

29.                          setSelection(1); //定位在第二个列表项  

30.                      }  

31.                  }  

32.                  break;  

33.              case MotionEvent.ACTION_DOWN:  

34.                  // 获得按下y轴位置   

35.                  mLastMotionY = y;    

36.                  break;              

37.              case MotionEvent.ACTION_MOVE:  

38.                  //更行头视图的toppadding 属性  

39.                  applyHeaderPadding(event);  

40.                  break;  

41.          }  

42.          return super.onTouchEvent(event);  

43.      }  


当按下的时候,记录按下y轴的位置,然后在move中调用了applyHeaderPadding()方法,我们再看下这个方法:

[java] view plaincopy

1.     // 获得header距离   

2.         private void applyHeaderPadding(MotionEvent ev) {  

3.             //获取累积的动作数  

4.             int pointerCount = ev.getHistorySize();  

5.             for (int p = 0; p < pointerCount; p++) {  

6.                 //如果是释放将要刷新状态  

7.                 if (mRefreshState == RELEASE_TO_REFRESH) {     

8.                     if (isVerticalFadingEdgeEnabled()) {     

9.                         setVerticalScrollBarEnabled(false);  

10.                  }  

11.                  //历史累积的高度  

12.                  int historicalY = (int) ev.getHistoricalY(p);  

13.                  // Calculate the padding to apply, we divide by 1.7 to  

14.                  // simulate a more resistant effect during pull.  

15.                  // 计算申请的边距,除以1.7使得拉动效果更好  

16.                  int topPadding = (int) (((historicalY - mLastMotionY)- mRefreshViewHeight) / 1.7);  

17.                  mRefreshView.setPadding(  

18.                          mRefreshView.getPaddingLeft(),  

19.                          topPadding,  

20.                          mRefreshView.getPaddingRight(),  

21.                          mRefreshView.getPaddingBottom());  

22.              }  

23.          }  

24.      }  


通过记录滑动距离,实时变化头部mRefreshView的上下左右的距离。

最后,看下手指松开的ACTION_UP

[java] view plaincopy

1.     case MotionEvent.ACTION_UP:  

2.                     //将垂直滚动条设置为可用状态  

3.                     if (!isVerticalScrollBarEnabled()) {  

4.                         setVerticalScrollBarEnabled(true);  

5.                     }  

6.                     if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {  

7.                         // 拖动距离达到刷新需要  

8.                         if ((mRefreshView.getBottom() >= mRefreshViewHeight  

9.                                 || mRefreshView.getTop() >= 0)  

10.                              && mRefreshState == RELEASE_TO_REFRESH) {    

11.                          // 把状态设置为正在刷新  

12.                          // Initiate the refresh  

13.                          mRefreshState = REFRESHING; //将标量设置为:正在刷新  

14.                          // 准备刷新  

15.                          prepareForRefresh();    

16.                          // 刷新    

17.                          onRefresh();    

18.                      } else if (mRefreshView.getBottom() < mRefreshViewHeight  

19.                              || mRefreshView.getTop() <= 0) {  

20.                          // Abort refresh and scroll down below the refresh view  

21.                          //停止刷新,并且滚动到头部刷新视图的下一个视图  

22.                          resetHeader();  

23.                          setSelection(1); //定位在第二个列表项  

24.                      }  

25.                  }  

26.                  break;  


当滑动距离大于一个item的距离时,添加一个item,否则,弹回。

看完onTouchEvent(),然后再看一下onScroll()方法:

[java] view plaincopy

1.     @Override  

2.         public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {  

3.             // When the refresh view is completely visible, change the text to say  

4.             // "Release to refresh..." and flip the arrow drawable.  

5.             // refreshview完全可见时,设置文字为松开刷新,同时翻转箭头   

6.             //如果是接触滚动状态,并且不是正在刷新的状态  

7.             if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL&& mRefreshState != REFRESHING) {  

8.                 if (firstVisibleItem == 0) {    

9.                     //如果显示出来了第一个列表项,显示刷新图片  

10.                  mRefreshViewImage.setVisibility(View.VISIBLE);  

11.                  //如果下拉了listiview,则显示上拉刷新动画  

12.                  if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20|| mRefreshView.getTop() >= 0)  

13.                          && mRefreshState != RELEASE_TO_REFRESH) {   

14.                      mRefreshViewText.setText(R.string.pull_to_refresh_release_label);  

15.                      mRefreshViewImage.clearAnimation();  

16.                      mRefreshViewImage.startAnimation(mFlipAnimation);  

17.                      mRefreshState = RELEASE_TO_REFRESH;     

18.                    //如果下拉距离不够,则回归原来的状态  

19.                  } else if (mRefreshView.getBottom() < mRefreshViewHeight + 20  

20.                          && mRefreshState != PULL_TO_REFRESH) {      

21.                      mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);  

22.                      if (mRefreshState != TAP_TO_REFRESH) {  

23.                          mRefreshViewImage.clearAnimation();  

24.                          mRefreshViewImage.startAnimation(mReverseFlipAnimation);  

25.                      }  

26.                      mRefreshState = PULL_TO_REFRESH;  

27.                  }  

28.              } else {     

29.                  mRefreshViewImage.setVisibility(View.GONE);    

30.                  resetHeader();     

31.              }  

32.            //如果是滚动状态第一个视图已经显示不是刷新状态  

33.          } else if (mCurrentScrollState == SCROLL_STATE_FLING  && firstVisibleItem == 0  

34.                  && mRefreshState != REFRESHING) {  

35.              setSelection(1);  

36.              mBounceHack = true;     

37.          } else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {  

38.              setSelection(1);         

39.          }  

40.          if (mOnScrollListener != null) {  

41.              mOnScrollListener.onScroll(view, firstVisibleItem,visibleItemCount, totalItemCount);  

42.          }  

43.      }  


该方法是在滑动过程中,各种状况的处理。

       onScroll()方法和onTouchEvent()方法的执行过程应该是,先onTouchEvent()ACTION_DOWN,然后是ACTION_MOVEonScroll()方法同时进行,最后是onTouchEvent()ACTION_UP。也可以自己打log看一下。这样在onTouchEvent()处理header,就是mRefreshView的外部的各个熟悉,onScroll()里面处理headermRefreshView)里面内部的控件变化,从逻辑上来说比较清晰。

       onScroll()中,引用方法resetHeader()方法:

[java] view plaincopy

1.     /**  

2.          * Resets the header to the original state. 

3.          * 重置header为之前的状态  

4.          */   

5.         private void resetHeader() {  

6.             if (mRefreshState != TAP_TO_REFRESH) {  

7.                 mRefreshState = TAP_TO_REFRESH;   

8.                 resetHeaderPadding();  

9.                 // 将刷新图标换成箭头   

10.              // Set refresh view text to the pull label  

11.              mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);  

12.              // Replace refresh drawable with arrow drawable  

13.              // 清除动画   

14.              mRefreshViewImage.setImageResource(REFRESHICON);  

15.              // Clear the full rotation animation  

16.              mRefreshViewImage.clearAnimation();  

17.              // Hide progress bar and arrow.  

18.              // 隐藏图标和进度条   

19.              mRefreshViewImage.setVisibility(View.GONE);  

20.              mRefreshViewProgress.setVisibility(View.GONE);  

21.          }  

22.      }  


resetHead就是headermRefreshView)的内部的具体操作。

       当一切都完成以后,就可以调用onRefreshComplete()方法:

[java] view plaincopy

1.     /**  

2.          * Resets the list to a normal state after a refresh. 

3.          * 重置listview为普通的listview 

4.          * @param lastUpdated  

5.          * Last updated at.  

6.          */    

7.           

8.         public void onRefreshComplete(CharSequence lastUpdated) {  

9.             setLastUpdated(lastUpdated);  

10.          onRefreshComplete();   

11.      }  

12.      /**  

13.       * Resets the list to a normal state after a refresh. 

14.       * 重置listview为普通的listview 

15.       */  

16.      public void onRefreshComplete() {          

17.          resetHeader();  

18.          // If refresh view is visible when loading completes, scroll down to  

19.          // the next item.  

20.          if (mRefreshView.getBottom() > 0) {  

21.              invalidateViews();  //重绘视图  

22.              setSelection(1);  

23.          }  

24.      } 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值