该效果是一名国外工程师(johannilsson)的代码,拿来研究了下,自己整合了一下,现在拿出来,跟大家一起分享。

再次感谢这位国外工程师(johannilsson),谢谢!

新浪微博,和QQ空间里面,都有那个下拉刷新的效果,另很多人眼前一亮,细细分析,原理原来如此。

在原作者的基础上,写了一些注释,和帮助大家更好的阅读理解,(可能其中有些地方注释不准,欢迎指正,谢谢)

下面,就亮出关键代码:

 ****  自定义listivew   (关键代码)



  1. public class PullToRefreshListView extends ListView implements OnScrollListener {  
  2.    
  3. private static final int TAP_TO_REFRESH = 1;     // 初始状态  
  4. private static final int PULL_TO_REFRESH = 2;    //拉动刷新  
  5. private static final int RELEASE_TO_REFRESH = 3;  //释放刷新  
  6. private static final int REFRESHING = 4;    //正在刷新  
  7.    
  8. private static final String TAG = "PullToRefreshListView";  
  9. //刷新接口  
  10. private OnRefreshListener mOnRefreshListener;  
  11.    
  12. //箭头图片  
  13. private static  int REFRESHICON = R.drawable.goicon;  
  14.     
  15. /**
  16.      * listview 滚动监听器
  17.      */  
  18. private OnScrollListener mOnScrollListener;  
  19.      
  20. //视图索引器  
  21. private LayoutInflater mInflater;  
  22. /**
  23.      * 头部视图  内容  -- start
  24.      */  
  25. private RelativeLayout mRefreshView;  
  26. private TextView mRefreshViewText;  
  27. private ImageView mRefreshViewImage;  
  28. private ProgressBar mRefreshViewProgress;  
  29. private TextView mRefreshViewLastUpdated;  
  30. /**
  31.      * 头部视图  内容  -- end
  32.      */  
  33. //当前listivew 的滚动状态  
  34. private int mCurrentScrollState;  
  35.      
  36. //当前listview 的刷新状态  
  37. private int mRefreshState;  
  38.    
  39. //动画效果  
  40. //变为向下的箭头  
  41. private RotateAnimation mFlipAnimation;  
  42. //变为逆向的箭头  
  43. private RotateAnimation mReverseFlipAnimation;  
  44. //头视图的高度  
  45. private int mRefreshViewHeight;  
  46. //头视图 原始的top padding 属性值  
  47. private int mRefreshOriginalTopPadding;  
  48. //  
  49. private int mLastMotionY;  
  50. //是否反弹  
  51. private boolean mBounceHack;  
  52.    
  53. public PullToRefreshListView(Context context) {  
  54. super(context);  
  55.         init(context);  
  56.     }  
  57.    
  58. public PullToRefreshListView(Context context, AttributeSet attrs) {  
  59. super(context, attrs);  
  60.         init(context);  
  61.     }  
  62.    
  63. public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {  
  64. super(context, attrs, defStyle);  
  65.         init(context);  
  66.     }  
  67.    
  68. private void init(Context context) {  
  69. // Load all of the animations we need in code rather than through XML  
  70. //初始化动画  
  71. //  
  72. new RotateAnimation(0, -180,  
  73. 0.5f,  
  74. 0.5f);  
  75. new LinearInterpolator());  
  76. 250);  
  77. true);  
  78.   
  79. new RotateAnimation(-180, 0,  
  80. 0.5f,  
  81. 0.5f);  
  82. new LinearInterpolator());  
  83. 250);  
  84. true);  
  85.    
  86.         mInflater = (LayoutInflater) context.getSystemService(  
  87.                 Context.LAYOUT_INFLATER_SERVICE);  
  88.    
  89. this, false);//(R.layout.pull_to_refresh_header, null);  
  90.     mRefreshViewText =  
  91.             (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text);  
  92.         mRefreshViewImage =  
  93.             (ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image);  
  94.         mRefreshViewProgress =  
  95.             (ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress);  
  96.         mRefreshViewLastUpdated =  
  97.             (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);  
  98.    
  99. 50);  
  100. new OnClickRefreshListener());  
  101.         mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();  
  102.    
  103.         mRefreshState = TAP_TO_REFRESH;  
  104.    
  105.         addHeaderView(mRefreshView);  
  106.    
  107. super.setOnScrollListener(this);  
  108.    
  109.         measureView(mRefreshView);  
  110. //获取头文件的测量高度  
  111.     }  
  112.    
  113. @Override  
  114. protected void onAttachedToWindow() {  
  115. 1);  
  116.     }  
  117.    
  118. @Override  
  119. public void setAdapter(ListAdapter adapter) {  
  120. super.setAdapter(adapter);  
  121. 1);  
  122.     }  
  123.    
  124. /**
  125.      * Set the listener that will receive notifications every time the list
  126.      * scrolls.
  127.      *
  128.      * @param l The scroll listener.
  129.      */  
  130. @Override  
  131. public void setOnScrollListener(AbsListView.OnScrollListener l) {  
  132.         mOnScrollListener = l;  
  133.     }  
  134.    
  135. /**
  136.      * Register a callback to be invoked when this list should be refreshed.
  137.      *
  138.      * @param onRefreshListener The callback to run.
  139.      */  
  140. public void setOnRefreshListener(OnRefreshListener onRefreshListener) {  
  141.         mOnRefreshListener = onRefreshListener;  
  142.     }  
  143.    
  144. /**
  145.      * Set a text to represent when the list was last updated.
  146.      * @param lastUpdated Last updated at.
  147.      */  
  148. public void setLastUpdated(CharSequence lastUpdated) {  
  149. if (lastUpdated != null) {  
  150.             mRefreshViewLastUpdated.setVisibility(View.VISIBLE);  
  151.             mRefreshViewLastUpdated.setText(lastUpdated);  
  152. else {  
  153.             mRefreshViewLastUpdated.setVisibility(View.GONE);  
  154.         }  
  155.     }  
  156.    
  157. @Override  
  158. public boolean onTouchEvent(MotionEvent event) {  
  159. //当前手指的Y值  
  160. final int y = (int) event.getY();  
  161.          
  162. //Log.i(TAG, "触屏的Y值"+y);  
  163. false;  //不反弹  
  164.          
  165. switch (event.getAction()) {  
  166. case MotionEvent.ACTION_UP:  
  167. //将垂直滚动条设置为可用状态  
  168. if (!isVerticalScrollBarEnabled()) {  
  169. true);  
  170.                 }  
  171.                  
  172. //如果头部刷新条出现,并且不是正在刷新状态  
  173. if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {  
  174. if ((mRefreshView.getBottom() >= mRefreshViewHeight  
  175. 0)  
  176. //如果头部视图处于拉离顶部的情况  
  177. // Initiate the refresh  
  178. //将标量设置为,正在刷新  
  179. //准备刷新  
  180. //刷新  
  181. else if (mRefreshView.getBottom() < mRefreshViewHeight  
  182. 0) {  
  183. // Abort refresh and scroll down below the refresh view  
  184. // 停止刷新,并且滚动到头部刷新视图的下一个视图  
  185.                         resetHeader();  
  186. 1);  //定位在第二个列表项  
  187.                     }  
  188.                 }  
  189. break;  
  190. case MotionEvent.ACTION_DOWN:  
  191. //跟踪手指的Y值  
  192. break;  
  193.              
  194. case MotionEvent.ACTION_MOVE:  
  195. //更行头视图的toppadding 属性  
  196.                 applyHeaderPadding(event);  
  197. break;  
  198.         }  
  199. return super.onTouchEvent(event);  
  200.     }  
  201.    
  202. /****
  203.      * 不断的头部的top padding 属性
  204.      * @param ev
  205.      */  
  206. private void applyHeaderPadding(MotionEvent ev) {  
  207. //获取累积的动作数  
  208. int pointerCount = ev.getHistorySize();  
  209. // Log.i(TAG, "获取累积的动作数"+pointerCount);  
  210. for (int p = 0; p < pointerCount; p++) {  
  211. if (mRefreshState == RELEASE_TO_REFRESH) {    //如果是释放将要刷新状态  
  212. if (isVerticalFadingEdgeEnabled()) {    
  213. false);  
  214.                 }  
  215. //历史累积的高度  
  216. int historicalY = (int) ev.getHistoricalY(p);  
  217. //Log.i(TAG, "单个动作getHistoricalY值:"+historicalY);  
  218. // Calculate the padding to apply, we divide by 1.7 to  
  219. // simulate a more resistant effect during pull.  
  220. int topPadding = (int) (((historicalY - mLastMotionY)  
  221. 1.7);  
  222.                  
  223.                 mRefreshView.setPadding(  
  224.                         mRefreshView.getPaddingLeft(),  
  225.                         topPadding,  
  226.                         mRefreshView.getPaddingRight(),  
  227.                         mRefreshView.getPaddingBottom());  
  228.             }  
  229.         }  
  230.     }  
  231.    
  232. /**
  233.      * Sets the header padding back to original size.
  234.      * 使头部视图的toppadding 恢复到初始值
  235.      */  
  236. private void resetHeaderPadding() {  
  237.         mRefreshView.setPadding(  
  238.                 mRefreshView.getPaddingLeft(),  
  239.                 mRefreshOriginalTopPadding,  
  240.                 mRefreshView.getPaddingRight(),  
  241.                 mRefreshView.getPaddingBottom());  
  242.     }  
  243.    
  244. /**
  245.      * Resets the header to the original state. 
  246.      *  初始化头部视图 状态
  247.      */  
  248. private void resetHeader() {  
  249. if (mRefreshState != TAP_TO_REFRESH) {  
  250. //初始刷新状态  
  251. //使头部视图的toppadding 恢复到初始值  
  252.             resetHeaderPadding();  
  253. // Set refresh view text to the pull label  
  254. //将文字初始化  
  255.             mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);  
  256. // Replace refresh drawable with arrow drawable  
  257. //设置初始图片  
  258.             mRefreshViewImage.setImageResource(REFRESHICON);  
  259. // Clear the full rotation animation  
  260. // 清除动画  
  261.             mRefreshViewImage.clearAnimation();  
  262. // Hide progress bar and arrow.  
  263. //隐藏头视图  
  264.             mRefreshViewImage.setVisibility(View.GONE);  
  265. //隐藏进度条  
  266.             mRefreshViewProgress.setVisibility(View.GONE);  
  267.         }  
  268.     }  


[java]  ​​view plain​​ ​​copy​​



  1. //测量视图的高度  
  2. private void measureView(View child) {  
  3. //获取头部视图属性  
  4.     ViewGroup.LayoutParams p = child.getLayoutParams();  
  5. if (p == null) {  
  6. new ViewGroup.LayoutParams(  
  7.                 ViewGroup.LayoutParams.FILL_PARENT,  
  8.                 ViewGroup.LayoutParams.WRAP_CONTENT);  
  9.     }  
  10.      
  11. int childWidthSpec = ViewGroup.getChildMeasureSpec(0,  
  12. 0 + 0, p.width);  
  13. int lpHeight = p.height;  
  14. int childHeightSpec;  
  15.      
  16. //不懂MeasureSpec------------------------------------------------------------------------------------------  
  17. if (lpHeight > 0) {  //如果视图的高度大于0  
  18.         childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);   
  19. else {  
  20. 0, MeasureSpec.UNSPECIFIED);  
  21.     }  
  22.     child.measure(childWidthSpec, childHeightSpec);  
  23. //不懂MeasureSpec------------------------------------------------------------------------------------------  
  24. }  
  25.   
  26. /****
  27.  * 滑动事件
  28.  */  
  29. @Override  
  30. public void onScroll(AbsListView view, int firstVisibleItem,  
  31. int visibleItemCount, int totalItemCount) {  
  32. // When the refresh view is completely visible, change the text to say  
  33. // "Release to refresh..." and flip the arrow drawable.  
  34. if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL   //如果是接触滚动状态,并且不是正在刷新的状态  
  35.             && mRefreshState != REFRESHING) {  
  36. if (firstVisibleItem == 0) {    //如果显示出来了第一个列表项  
  37. //显示刷新图片  
  38.             mRefreshViewImage.setVisibility(View.VISIBLE);  
  39. if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20  
  40. 0)  
  41. //如果下拉了listiview,则显示上拉刷新动画  
  42.                 mRefreshViewText.setText(R.string.pull_to_refresh_release_label);  
  43.                 mRefreshViewImage.clearAnimation();  
  44.                 mRefreshViewImage.startAnimation(mFlipAnimation);  
  45.                 mRefreshState = RELEASE_TO_REFRESH;  
  46.                  
  47. "现在处于下拉状态");  
  48.                  
  49. else if (mRefreshView.getBottom() < mRefreshViewHeight + 20  
  50. //如果没有到达,下拉刷新距离,则回归原来的状态  
  51.                 mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);  
  52. if (mRefreshState != TAP_TO_REFRESH) {  
  53.                     mRefreshViewImage.clearAnimation();  
  54.                     mRefreshViewImage.startAnimation(mReverseFlipAnimation);  
  55.                      
  56. "现在处于回弹状态");  
  57.                      
  58.                 }  
  59.                 mRefreshState = PULL_TO_REFRESH;  
  60.             }  
  61. else {    
  62. //隐藏刷新图片  
  63. //初始化,头部  
  64.         }  
  65. else if (mCurrentScrollState == SCROLL_STATE_FLING  //如果是自己滚动状态+ 第一个视图已经显示+ 不是刷新状态  
  66. 0  
  67.             && mRefreshState != REFRESHING) {  
  68. 1);  
  69. true;   //状态为回弹  
  70. "现在处于自由滚动到顶部的状态");  
  71. else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {  
  72. 1);  
  73. "现在处于自由滚动到顶部的状态");  
  74.     }  
  75.   
  76. if (mOnScrollListener != null) {  
  77.         mOnScrollListener.onScroll(view, firstVisibleItem,  
  78.                 visibleItemCount, totalItemCount);  
  79.     }  
  80. }  
  81.   
  82.   
  83. //滚动状态改变  
  84. @Override  
  85. public void onScrollStateChanged(AbsListView view, int scrollState) {  
  86.     mCurrentScrollState = scrollState;  
  87.   
  88. if (mCurrentScrollState == SCROLL_STATE_IDLE) {   //如果滚动停顿  
  89. false;  
  90.     }  
  91.   
  92. if (mOnScrollListener != null) {  
  93.         mOnScrollListener.onScrollStateChanged(view, scrollState);  
  94.     }  
  95. }  
  96.   
  97.   
  98.   
  99. //准备刷新  
  100. public void prepareForRefresh() {  
  101. //初始化,头部文件  
  102.   
  103.     mRefreshViewImage.setVisibility(View.GONE);  
  104. // We need this hack, otherwise it will keep the previous drawable.  
  105. null);  
  106.     mRefreshViewProgress.setVisibility(View.VISIBLE);  
  107.   
  108. // Set refresh view text to the refreshing label  
  109.    mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);  
  110.   
  111.     mRefreshState = REFRESHING;  
  112. }  
  113.   
  114. //刷新  
  115. public void onRefresh() {  
  116. "执行刷新");  
  117.   
  118. if (mOnRefreshListener != null) {  
  119.         mOnRefreshListener.onRefresh();  
  120.     }  
  121. }  
  122.   
  123. /**
  124.  * 刷新完成 的回调函数
  125.  * Resets the list to a normal state after a refresh.
  126.  * @param lastUpdated Last updated at.
  127.  */  
  128. public void onRefreshComplete(CharSequence lastUpdated) {  
  129.     setLastUpdated(lastUpdated);  
  130.     onRefreshComplete();  
  131. }  
  132.   
  133. /**
  134.  *  刷新完成回调函数
  135.  * Resets the list to a normal state after a refresh.
  136.  */  
  137. public void onRefreshComplete() {         
  138. "onRefreshComplete");  
  139.   
  140.     resetHeader();  
  141.   
  142. // If refresh view is visible when loading completes, scroll down to  
  143. // the next item.  
  144. if (mRefreshView.getBottom() > 0) {  
  145. //重绘视图  
  146. 1);  
  147.     }  
  148. }  
  149.   
  150. /**
  151.  * Invoked when the refresh view is clicked on. This is mainly used when
  152.  * there's only a few items in the list and it's not possible to drag the
  153.  * list.
  154.  */  
  155. private class OnClickRefreshListener implements OnClickListener {  
  156.   
  157. @Override  
  158. public void onClick(View v) {  
  159. if (mRefreshState != REFRESHING) {  
  160. //准备刷新  
  161.             prepareForRefresh();   
  162. //刷新    
  163.             onRefresh();  
  164.         }  
  165.     }  
  166.   
  167. }  
  168.   
  169. /**
  170.  * 刷新方法接口
  171.  */  
  172. public interface OnRefreshListener {  
  173.      
  174. public void onRefresh();  
  175. }  



  

---------


------


 

* 如果你还是没有弄明白的话,那就点击下面的链接,来​​下载​​整个demo项目: 


johannilsson