android之listivew實現下拉刷新

  1. 新浪微博,和QQ空間裏面,都有那個下拉刷新的效果,另不少人眼前一亮,細細分析,原理原來如此。 動畫

    在原做者的基礎上,寫了一些註釋,和幫助你們更好的閱讀理解,(可能其中有些地方註釋不許,歡迎指正,謝謝) this

    源代碼下載地址:http://download.csdn.net/detail/weidi1989/4588246 spa


  2. /** 
  3.  * 重寫一個ListView,主要是添加一個下拉事件 
  4.  *  
  5.  * @author way 
  6.  *  
  7.  */  
  8. public class MyListView extends ListView implements OnScrollListener {  
  9.   
  10.     private static final String TAG = "listview";  
  11.   
  12.     private final static int RELEASE_To_REFRESH = 0// 鬆開刷新狀態  
  13.     private final static int PULL_To_REFRESH = 1;// 拉動刷新狀態  
  14.     private final static int REFRESHING = 2;// 正在刷新狀態  
  15.     private final static int DONE = 3;// 已經加載完畢狀態  
  16.     private final static int LOADING = 4;// 正在加載數據狀態  
  17.     private final static int RATIO = 3;// 實際的padding的距離與界面上偏移距離的比例  
  18.   
  19.     private LayoutInflater inflater;  
  20.     private LinearLayout headView;// ListView頭部的View  
  21.   
  22.     private TextView tipsTextview;// 提示信息「下拉刷新」的TextView  
  23.     private TextView lastUpdatedTextView;// 上次更新時間的TextView  
  24.     private ImageView arrowImageView;// 箭頭的圖片  
  25.     private ProgressBar progressBar;// 刷新進度  
  26.   
  27.     private RotateAnimation animation;// 箭頭向下動畫  
  28.     private RotateAnimation reverseAnimation;// 逆向箭頭動畫  
  29.   
  30.     private boolean isRecored;// 用於保證startY的值在一個完整的touch事件中只被記錄一次  
  31.   
  32.     private int headContentWidth;// 頭部View內容的寬度  
  33.     private int headContentHeight;// 頭部view內容的高度  
  34.   
  35.     private int startY;// 向下觸屏事件時的手指起始y軸位置  
  36.   
  37.     private int firstItemIndex;// ListView第一項的索引  
  38.   
  39.     private int state;// 刷新狀態  
  40.   
  41.     private boolean isBack;// 是否反彈  
  42.   
  43.     private OnRefreshListener refreshListener;// 給外面預留的刷新的接口  
  44.   
  45.     private boolean isRefreshable;// 是否刷新的標誌位  
  46.   
  47.     /** 
  48.      * 第一個構造器 
  49.      *  
  50.      * @param context 
  51.      *            上下文對象 
  52.      */  
  53.     public MyListView(Context context) {  
  54.         super(context);  
  55.         init(context);  
  56.     }  
  57.   
  58.     /** 
  59.      * 第二個構造器 
  60.      *  
  61.      * @param context 
  62.      *            上下文對象 
  63.      * @param attrs 
  64.      *            屬性 
  65.      */  
  66.     public MyListView(Context context, AttributeSet attrs) {  
  67.         super(context, attrs);  
  68.         init(context);  
  69.     }  
  70.   
  71.     /** 
  72.      * 初始化數據 
  73.      *  
  74.      * @param context 
  75.      *            上下文對象 
  76.      */  
  77.     private void init(Context context) {  
  78.         // setCacheColorHint(context.getResources().getColor(R.color.transparent));  
  79.         inflater = LayoutInflater.from(context);  
  80.         headView = (LinearLayout) inflater.inflate(R.layout.head, null);// 獲取ListView頭部的view  
  81.   
  82.         arrowImageView = (ImageView) headView  
  83.                 .findViewById(R.id.head_arrowImageView);// 從頭部的View獲取箭頭圖片  
  84.         arrowImageView.setMinimumWidth(70);  
  85.         arrowImageView.setMinimumHeight(50);  
  86.         progressBar = (ProgressBar) headView  
  87.                 .findViewById(R.id.head_progressBar);// 獲取刷新進度條  
  88.         tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);// 提示信息的TextView  
  89.         lastUpdatedTextView = (TextView) headView  
  90.                 .findViewById(R.id.head_lastUpdatedTextView);// 最後刷新時間的TextView  
  91.   
  92.         measureView(headView);// 本身寫的一個方法,沒有很理解  
  93.         headContentHeight = headView.getMeasuredHeight();// 獲得headView的原始高度  
  94.         headContentWidth = headView.getMeasuredWidth();  
  95.   
  96.         headView.setPadding(0, -1 * headContentHeight, 00);// 設置內容的內部偏移量  
  97.         headView.invalidate();  
  98.   
  99.         Log.v("size""width:" + headContentWidth + " height:"  
  100.                 + headContentHeight);  
  101.   
  102.         addHeaderView(headView, nullfalse);// 加到ListView的頭部view,ListView組件提供了兩個很實用的功能,那就是能夠在頂部和底部添加自定義的視圖  
  103.         setOnScrollListener(this);  
  104.   
  105.         // 箭頭向下動畫  
  106.         animation = new RotateAnimation(0, -180,  
  107.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  108.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  109.         animation.setInterpolator(new LinearInterpolator());  
  110.         animation.setDuration(250);  
  111.         animation.setFillAfter(true);  
  112.   
  113.         // 逆向箭頭動畫  
  114.         reverseAnimation = new RotateAnimation(-1800,  
  115.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  116.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  117.         reverseAnimation.setInterpolator(new LinearInterpolator());  
  118.         reverseAnimation.setDuration(200);  
  119.         reverseAnimation.setFillAfter(true);  
  120.   
  121.         state = DONE;// 第一次加載默認完成的狀態  
  122.         isRefreshable = false;// 刷新標誌位默認爲false  
  123.     }  
  124.   
  125.     /** 
  126.      * 滑動時被調用 
  127.      */  
  128.     public void onScroll(AbsListView view, int firstVisiableItem,  
  129.             int visibleItemCount, int totalItemCount) {  
  130.         firstItemIndex = firstVisiableItem;// ListView第一個索引值爲ListView數據中第一個可見項  
  131.     }  
  132.   
  133.     /** 
  134.      * 滑動狀態改變時被調用 
  135.      */  
  136.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
  137.     }  
  138.   
  139.     /** 
  140.      * 觸摸事件 
  141.      */  
  142.     public boolean onTouchEvent(MotionEvent event) {  
  143.         if (isRefreshable) {// 若是刷新標誌爲true  
  144.             switch (event.getAction()) {  
  145.             case MotionEvent.ACTION_DOWN:// 向下  
  146.                 if (firstItemIndex == 0 && !isRecored) {  
  147.                     isRecored = true;  
  148.                     startY = (int) event.getY();  
  149.                     Log.v(TAG, "在down時候記錄當前位置");  
  150.                 }  
  151.                 break;  
  152.   
  153.             case MotionEvent.ACTION_UP:// 向上  
  154.                 if (state != REFRESHING && state != LOADING) {  
  155.                     if (state == DONE) {  
  156.                         // 什麼都不作  
  157.                     }  
  158.                     if (state == PULL_To_REFRESH) {// 下拉刷新  
  159.                         state = DONE;  
  160.                         changeHeaderViewByState();// 更新頭部view  
  161.                         Log.v(TAG, "由下拉刷新狀態,到done狀態");  
  162.                     }  
  163.                     if (state == RELEASE_To_REFRESH) {// 釋放刷新  
  164.                         state = REFRESHING;  
  165.                         changeHeaderViewByState();  
  166.                         onRefresh();// 調用接口的方法,通知外面  
  167.   
  168.                         Log.v(TAG, "由鬆開刷新狀態,到done狀態");  
  169.                     }  
  170.                 }  
  171.                 isRecored = false;  
  172.                 isBack = false;  
  173.                 break;  
  174.   
  175.             case MotionEvent.ACTION_MOVE:// 手指移動  
  176.                 int tempY = (int) event.getY();  
  177.                 if (!isRecored && firstItemIndex == 0) {  
  178.                     Log.v(TAG, "在move時候記錄下位置");  
  179.                     isRecored = true;  
  180.                     startY = tempY;  
  181.                 }  
  182.                 if (state != REFRESHING && isRecored && state != LOADING) {  
  183.   
  184.                     // 保證在設置padding的過程當中,當前的位置一直是在head,不然若是當列表超出屏幕的話,當在上推的時候,列表會同時進行滾動  
  185.   
  186.                     // 能夠鬆手去刷新了  
  187.                     if (state == RELEASE_To_REFRESH) {  
  188.                         setSelection(0);  
  189.                         // 往上推了,推到了屏幕足夠掩蓋head的程度,可是尚未推到所有掩蓋的地步  
  190.                         if (((tempY - startY) / RATIO < headContentHeight)  
  191.                                 && (tempY - startY) > 0) {  
  192.                             state = PULL_To_REFRESH;  
  193.                             changeHeaderViewByState();  
  194.                             Log.v(TAG, "由鬆開刷新狀態轉變到下拉刷新狀態");  
  195.                         }  
  196.                         // 一會兒推到頂了  
  197.                         else if (tempY - startY <= 0) {  
  198.                             state = DONE;  
  199.                             changeHeaderViewByState();  
  200.                             Log.v(TAG, "由鬆開刷新狀態轉變到done狀態");  
  201.                         }  
  202.                         // 往下拉了,或者尚未上推到屏幕頂部掩蓋head的地步  
  203.                         else {  
  204.                             // 不用進行特別的操做,只用更新paddingTop的值就好了  
  205.                         }  
  206.                     }  
  207.                     // 尚未到達顯示鬆開刷新的時候,DONE或者是PULL_To_REFRESH狀態  
  208.                     if (state == PULL_To_REFRESH) {  
  209.                         setSelection(0);  
  210.                         // 下拉到能夠進入RELEASE_TO_REFRESH的狀態  
  211.                         if ((tempY - startY) / RATIO >= headContentHeight) {  
  212.                             state = RELEASE_To_REFRESH;  
  213.                             isBack = true;  
  214.                             changeHeaderViewByState();  
  215.                             Log.v(TAG, "由done或者下拉刷新狀態轉變到鬆開刷新");  
  216.                         }  
  217.                         // 上推到頂了  
  218.                         else if (tempY - startY <= 0) {  
  219.                             state = DONE;  
  220.                             changeHeaderViewByState();  
  221.                             Log.v(TAG, "由DOne或者下拉刷新狀態轉變到done狀態");  
  222.                         }  
  223.                     }  
  224.                     // done狀態下  
  225.                     if (state == DONE) {  
  226.                         if (tempY - startY > 0) {  
  227.                             state = PULL_To_REFRESH;  
  228.                             changeHeaderViewByState();  
  229.                         }  
  230.                     }  
  231.                     // 更新headView的size  
  232.                     if (state == PULL_To_REFRESH) {  
  233.                         headView.setPadding(0, -1 * headContentHeight  
  234.                                 + (tempY - startY) / RATIO, 00);  
  235.                     }  
  236.                     // 更新headView的paddingTop  
  237.                     if (state == RELEASE_To_REFRESH) {  
  238.                         headView.setPadding(0, (tempY - startY) / RATIO  
  239.                                 - headContentHeight, 00);  
  240.                     }  
  241.                 }  
  242.                 break;  
  243.             }  
  244.         }  
  245.         return super.onTouchEvent(event);  
  246.     }  
  247.   
  248.     // 當狀態改變時候,調用該方法,以更新界面  
  249.     private void changeHeaderViewByState() {  
  250.         switch (state) {  
  251.         case RELEASE_To_REFRESH:// 鬆開刷新狀態  
  252.             arrowImageView.setVisibility(View.VISIBLE);// 顯示箭頭  
  253.             progressBar.setVisibility(View.GONE);// 移除進度條  
  254.             tipsTextview.setVisibility(View.VISIBLE);// 顯示提示信息  
  255.             lastUpdatedTextView.setVisibility(View.VISIBLE);// 顯示最後刷新時間  
  256.             arrowImageView.clearAnimation();// 先移除全部動畫  
  257.             arrowImageView.startAnimation(animation);// 加載箭頭向下動畫  
  258.   
  259.             tipsTextview.setText("鬆開刷新");  
  260.   
  261.             Log.v(TAG, "當前狀態,鬆開刷新");  
  262.             break;  
  263.         case PULL_To_REFRESH:// 下拉刷新狀態  
  264.             progressBar.setVisibility(View.GONE);// 移除進度條  
  265.             tipsTextview.setVisibility(View.VISIBLE);// 顯示提示信息  
  266.             lastUpdatedTextView.setVisibility(View.VISIBLE);// 顯示最後刷新時間  
  267.             arrowImageView.clearAnimation();// 先移除全部動畫  
  268.             arrowImageView.setVisibility(View.VISIBLE);// 箭頭圖片可見  
  269.             // 若是是由RELEASE_To_REFRESH狀態轉變來的,就加載動畫  
  270.             if (isBack) {  
  271.                 isBack = false;  
  272.                 arrowImageView.clearAnimation();  
  273.                 arrowImageView.startAnimation(reverseAnimation);  
  274.                 tipsTextview.setText("下拉刷新");  
  275.             } else {  
  276.                 tipsTextview.setText("下拉刷新");  
  277.             }  
  278.             Log.v(TAG, "當前狀態,下拉刷新");  
  279.             break;  
  280.   
  281.         case REFRESHING:// 正在刷新狀態  
  282.             headView.setPadding(0000);// 無內部偏移  
  283.             progressBar.setVisibility(View.VISIBLE);// 進度條可見  
  284.             arrowImageView.clearAnimation();// 先清除動畫  
  285.             arrowImageView.setVisibility(View.GONE);// 再移除箭頭動畫  
  286.             tipsTextview.setText("正在刷新...");// 提示信息變成正在刷新...  
  287.             lastUpdatedTextView.setVisibility(View.VISIBLE);// 最後刷新時間可見  
  288.             Log.v(TAG, "當前狀態,正在刷新...");  
  289.             break;  
  290.         case DONE:// 完成狀態  
  291.             headView.setPadding(0, -1 * headContentHeight, 00);  
  292.             progressBar.setVisibility(View.GONE);  
  293.             arrowImageView.clearAnimation();  
  294.             arrowImageView.setImageResource(R.drawable.arrow_down);  
  295.             tipsTextview.setText("下拉刷新");  
  296.             lastUpdatedTextView.setVisibility(View.VISIBLE);  
  297.   
  298.             Log.v(TAG, "當前狀態,done");  
  299.             break;  
  300.         }  
  301.     }  
  302.   
  303.     // 刷新完成  
  304.     public void onRefreshComplete() {  
  305.         state = DONE;  
  306.         SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  HH:mm");  
  307.         String date = format.format(new Date());  
  308.         lastUpdatedTextView.setText("最近更新:" + date);  
  309.         changeHeaderViewByState();  
  310.     }  
  311.   
  312.     /** 
  313.      * 一個下拉刷新的監聽接口 
  314.      *  
  315.      */  
  316.     public interface OnRefreshListener {  
  317.         public void onRefresh();  
  318.     }  
  319.   
  320.     // 監聽方法  
  321.     public void setonRefreshListener(OnRefreshListener refreshListener) {  
  322.         this.refreshListener = refreshListener;  
  323.         isRefreshable = true;  
  324.     }  
  325.   
  326.     // 正在刷新  
  327.     private void onRefresh() {  
  328.         if (refreshListener != null) {  
  329.             refreshListener.onRefresh();  
  330.         }  
  331.     }  
  332.   
  333.     /** 
  334.      * 此方法是「估計」headView的width以及height 
  335.      *  
  336.      * @param child 
  337.      *            傳入進來的headView 
  338.      */  
  339.     private void measureView(View child) {  
  340.         ViewGroup.LayoutParams p = child.getLayoutParams();  
  341.         if (p == null) {  
  342.             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
  343.                     ViewGroup.LayoutParams.WRAP_CONTENT);  
  344.         }  
  345.         int childWidthSpec = ViewGroup.getChildMeasureSpec(00 + 0, p.width);  
  346.         int lpHeight = p.height;  
  347.         int childHeightSpec;  
  348.         if (lpHeight > 0) {  
  349.             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,  
  350.                     MeasureSpec.EXACTLY);  
  351.         } else {  
  352.             childHeightSpec = MeasureSpec.makeMeasureSpec(0,  
  353.                     MeasureSpec.UNSPECIFIED);  
  354.         }  
  355.         child.measure(childWidthSpec, childHeightSpec);  
  356.     }  
  357.   
  358.     // ListView添加Adapter方法  
  359.     public void setAdapter(BaseAdapter adapter) {  
  360.         SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  HH:mm");  
  361.         String date = format.format(new Date());  
  362.         lastUpdatedTextView.setText("最近更新:" + date);  
  363.         super.setAdapter(adapter);  
  364.     }  
  365. }  
相關文章
相關標籤/搜索