listview下拉刷新

現在QQ空間和新浪微博ListView下拉刷新做的比較炫,正好公司也有這樣的需求,實現起來還是相對簡單的。關鍵是要自定義一個ListView頭部(初始化的時候裏面的控件是不可見的),然後在點擊、拖動、鬆開的時候觸發事件,顯示ListView頭,計算出拖拽的距離,跟ListView頭的高度做比較,以此來顯示對應的ListView頭裏的控件(下拉圖標、提示文字、圓形進度條等)。好了,下面貼出效果圖:



1351581810_3182.png
1351581857_1711.png


好,開始上代碼,先是佈局文件main.xml,沒什麼好說的:

  1. <?xmlversion="1.0"encoding="utf-8"?>

  2. <LinearLayoutxmlns:Android="http://schemas.android.com/apk/res/android"

  3. android:layout_width="fill_parent"

  4. android:layout_height="fill_parent"

  5. android:orientation="vertical"

  6. android:background="@color/mainColor">


  7. <!-- 這裏是自定義的ListView -->

  8. <com.focustech.android.CustomListView

  9. android:id="@+id/list"

  10. android:layout_width="fill_parent"

  11. android:layout_height="wrap_content"

  12. />


  13. </LinearLayout>

ListView頭佈局head.xml:

  1. <?xmlversion="1.0"encoding="utf-8"?>

  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

  3. android:layout_width="match_parent"

  4. android:layout_height="match_parent"

  5. android:orientation="vertical">


  6. <RelativeLayout

  7. android:id="@+id/head_contentLayout"

  8. android:layout_width="fill_parent"

  9. android:layout_height="wrap_content"

  10. android:paddingLeft="30dp">


  11. <FrameLayout

  12. android:layout_width="wrap_content"

  13. android:layout_height="wrap_content"

  14. android:layout_alignParentLeft="true"

  15. android:layout_centerVertical="true">


  16. <ImageView

  17. android:id="@+id/head_arrowImageView"

  18. android:layout_width="wrap_content"

  19. android:layout_height="wrap_content"

  20. android:layout_gravity="center"

  21. android:src="@drawable/ic_pulltorefresh_arrow"/>


  22. <ProgressBar

  23. android:id="@+id/head_progressBar"

  24. style="?android:attr/progressBarStyleSmall"

  25. android:layout_width="wrap_content"

  26. android:layout_height="wrap_content"

  27. android:layout_gravity="center"

  28. android:visibility="gone"/>

  29. </FrameLayout>


  30. <LinearLayout

  31. android:layout_width="wrap_content"

  32. android:layout_height="wrap_content"

  33. android:layout_centerHorizontal="true"

  34. android:gravity="center_horizontal"

  35. android:orientation="vertical">


  36. <TextView

  37. android:id="@+id/head_tipsTextView"

  38. android:layout_width="wrap_content"

  39. android:layout_height="wrap_content"

  40. android:text="下拉可以刷新"

  41. android:textColor="#ffffff"

  42. android:textSize="20sp"/>


  43. <TextView

  44. android:id="@+id/head_lastUpdatedTextView"

  45. android:layout_width="wrap_content"

  46. android:layout_height="wrap_content"

  47. android:text="最近更新"

  48. android:textColor="#cc6600"

  49. android:textSize="10sp"/>


  50. </LinearLayout>

  51. </RelativeLayout>



  52. </LinearLayout>

ListView item項佈局listview_item.xml:

  1. <?xmlversion="1.0"encoding="utf-8"?>

  2. <LinearLayoutxmlns:Android="http://schemas.android.com/apk/res/android"

  3. android:layout_width="match_parent"

  4. android:layout_height="match_parent"

  5. android:orientation="vertical">


  6. <ImageView

  7. android:id="@+id/imageView"

  8. android:layout_width="wrap_content"

  9. android:layout_height="wrap_content"

  10. android:src="@drawable/pretty_girl"

  11. />

  12. <TextView

  13. android:id="@+id/textview"

  14. android:layout_width="wrap_content"

  15. android:layout_height="wrap_content"

  16. />


  17. </LinearLayout>

清單文件也貼出來吧,其實就是自動生成的,什麼都不用改:

  1. <?xmlversion="1.0"encoding="utf-8"?>

  2. <manifestxmlns:android="http://schemas.android.com/apk/res/android"

  3. package="com.focustech.android"

  4. android:versionCode="1"

  5. android:versionName="1.0">


  6. <uses-sdkandroid:minSdkVersion="14"/>


  7. <application

  8. android:icon="@drawable/ic_launcher"

  9. android:label="@string/app_name">

  10. <activity

  11. android:label="@string/app_name"

  12. android:name="com.focustech.android.MainActivity">

  13. <intent-filter>

  14. <actionandroid:name="android.intent.action.MAIN"/>


  15. <categoryandroid:name="android.intent.category.LAUNCHER"/>

  16. </intent-filter>

  17. </activity>

  18. </application>


  19. </manifest>

此Demo的Activity:

  1. package com.focustech.android;  


  2. import android.app.Activity;  

  3. import android.database.DataSetObserver;  

  4. import android.graphics.Color;  

  5. import android.os.Bundle;  

  6. import android.view.LayoutInflater;  

  7. import android.view.View;  

  8. import android.view.ViewGroup;  

  9. import android.widget.ListAdapter;  

  10. import android.widget.TextView;  


  11. import com.focustech.android.CustomListView.OnRefreshListener;  

  12. import com.focustech.android.R;  


  13. publicclass MainActivity extends Activity {  


  14. private ListAdapter adapter;  

  15. int size = 10;  


  16. publicvoid onCreate(Bundle savedInstanceState) {  

  17. super.onCreate(savedInstanceState);  

  18.        setContentView(R.layout.main);  


  19. final CustomListView list = (CustomListView) findViewById(R.id.list);  


  20.        adapter = new ListAdapter() {  

  21. publicvoid unregisterDataSetObserver(DataSetObserver arg0) {  


  22.            }  


  23. publicvoid registerDataSetObserver(DataSetObserver arg0) {  


  24.            }  


  25. publicboolean isEmpty() {  

  26. returnfalse;  

  27.            }  


  28. publicboolean hasStableIds() {  

  29. returnfalse;  

  30.            }  


  31. publicint getViewTypeCount() {  

  32. return1;  

  33.            }  


  34. public View getView(int position, View convertView, ViewGroup parent) {  

  35.                convertView = LayoutInflater.from(getApplicationContext())  

  36.                        .inflate(R.layout.listview_item, null);  


  37.                TextView mTextView = (TextView) convertView  

  38.                        .findViewById(R.id.textview);  

  39.                mTextView.setText("pretty girl" + position);  

  40.                mTextView.setTextColor(Color.RED);  

  41. return convertView;  

  42.            }  


  43. publicint getItemViewType(int arg0) {  

  44. return arg0;  

  45.            }  


  46. publiclong getItemId(int arg0) {  

  47. return arg0;  

  48.            }  


  49. public Object getItem(int arg0) {  

  50. returnnull;  

  51.            }  


  52. publicint getCount() {  

  53. return size;  

  54.            }  


  55. @Override

  56. publicboolean isEnabled(int position) {  

  57. returnfalse;  

  58.            }  


  59. @Override

  60. publicboolean areAllItemsEnabled() {  

  61. returnfalse;  

  62.            }  

  63.        };  


  64.        list.setAdapter(adapter);  

  65. // 在list上註冊自定義的監聽器

  66.        list.setonRefreshListener(new OnRefreshListener() {  

  67. publicvoid onRefresh() {  

  68. new Thread(new Runnable() {  

  69. publicvoid run() {  

  70. try {  

  71.                            Thread.sleep(1000);  

  72.                        } catch (Exception e) {  

  73.                        }  


  74.                        runOnUiThread(new Runnable() {  

  75. publicvoid run() {  

  76.                                list.onRefreshComplete();  

  77.                            }  

  78.                        });  

  79.                        size++;  


  80.                    }  

  81.                }).start();  

  82.            }  

  83.        });  

  84.    }  


  85. }  

實現了OnScrollListener的自定義ListView:

  1. package com.focustech.Android;  


  2. import java.util.Date;  


  3. import android.content.Context;  

  4. import android.util.AttributeSet;  

  5. import android.util.Log;  

  6. import android.view.LayoutInflater;  

  7. import android.view.MotionEvent;  

  8. import android.view.View;  

  9. import android.view.ViewGroup;  

  10. import android.view.animation.LinearInterpolator;  

  11. import android.view.animation.RotateAnimation;  

  12. import android.widget.AbsListView;  

  13. import android.widget.AbsListView.OnScrollListener;  

  14. import android.widget.ImageView;  

  15. import android.widget.LinearLayout;  

  16. import android.widget.ListView;  

  17. import android.widget.ProgressBar;  

  18. import android.widget.TextView;  


  19. import com.focustech.android.R;  


  20. publicclass CustomListView extends ListView implements OnScrollListener {  


  21. // 鬆開刷新標誌

  22. privatefinalstaticint RELEASE_To_REFRESH = 0;  

  23. // 下拉刷新標誌

  24. privatefinalstaticint PULL_To_REFRESH = 1;  

  25. // 正在刷新標誌

  26. privatefinalstaticint REFRESHING = 2;  

  27. // 刷新完成標誌

  28. privatefinalstaticint DONE = 3;  


  29. private LayoutInflater inflater;  


  30. private LinearLayout headView;  

  31. private TextView tipsTextview;  

  32. private TextView lastUpdatedTextView;  

  33. private ImageView arrowImageView;  

  34. private ProgressBar progressBar;  

  35. // 用來設置箭頭圖標動畫效果

  36. private RotateAnimation animation;  

  37. private RotateAnimation reverseAnimation;  


  38. // 用於保證startY的值在一個完整的touch事件中只被記錄一次

  39. privateboolean isRecored;  


  40. privateint headContentWidth;  

  41. privateint headContentHeight;  


  42. privateint startY;  

  43. privateint firstItemIndex;  


  44. privateint state;  


  45. privateboolean isBack;  


  46. public OnRefreshListener refreshListener;  


  47. privatefinalstatic String TAG = "Monitor Current Infomation";  


  48. public CustomListView(Context context, AttributeSet attrs) {  

  49. super(context, attrs);  

  50.        init(context);  

  51.    }  


  52. privatevoid init(Context context) {  


  53.        inflater = LayoutInflater.from(context);  

  54.        headView = (LinearLayout) inflater.inflate(R.layout.head, null);  


  55.        arrowImageView = (ImageView) headView  

  56.                .findViewById(R.id.head_arrowImageView);  

  57.        arrowImageView.setMinimumWidth(50);  

  58.        arrowImageView.setMinimumHeight(50);  

  59.        progressBar = (ProgressBar) headView  

  60.                .findViewById(R.id.head_progressBar);  

  61.        tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);  

  62.        lastUpdatedTextView = (TextView) headView  

  63.                .findViewById(R.id.head_lastUpdatedTextView);  


  64.        measureView(headView);  


  65.        headContentHeight = headView.getMeasuredHeight();  

  66.        headContentWidth = headView.getMeasuredWidth();  


  67.        headView.setPadding(0, -1 * headContentHeight, 0, 0);  

  68.        headView.invalidate();  


  69.        Log.v("size", "width:" + headContentWidth + " height:"

  70.                + headContentHeight);  


  71.        addHeaderView(headView);  

  72.        setOnScrollListener(this);  


  73.        animation = new RotateAnimation(0, -180,  

  74.                RotateAnimation.RELATIVE_TO_SELF, 0.5f,  

  75.                RotateAnimation.RELATIVE_TO_SELF, 0.5f);  

  76.        animation.setInterpolator(new LinearInterpolator());  

  77.        animation.setDuration(500);  

  78.        animation.setFillAfter(true);  


  79.        reverseAnimation = new RotateAnimation(-180, 0,  

  80.                RotateAnimation.RELATIVE_TO_SELF, 0.5f,  

  81.                RotateAnimation.RELATIVE_TO_SELF, 0.5f);  

  82.        reverseAnimation.setInterpolator(new LinearInterpolator());  

  83.        reverseAnimation.setDuration(500);  

  84.        reverseAnimation.setFillAfter(true);  

  85.    }  


  86. publicvoid onScroll(AbsListView arg0, int firstVisiableItem, int arg2,  

  87. int arg3) {  

  88.        firstItemIndex = firstVisiableItem;  

  89.    }  


  90. publicvoid onScrollStateChanged(AbsListView arg0, int arg1) {  

  91.    }  


  92. publicboolean onTouchEvent(MotionEvent event) {  

  93. switch (event.getAction()) {  

  94. case MotionEvent.ACTION_DOWN:  

  95. if (firstItemIndex == 0 && !isRecored) {  

  96.                startY = (int) event.getY();  

  97.                isRecored = true;  


  98.                Log.v(TAG, "記錄按下時的位置");  

  99.            }  

  100. break;  


  101. case MotionEvent.ACTION_UP:  


  102. if (state != REFRESHING) {  

  103. if (state == DONE) {  

  104.                    Log.v(TAG, "什麼都不做");  

  105.                }  

  106. if (state == PULL_To_REFRESH) {  

  107.                    state = DONE;  

  108.                    changeHeaderViewByState();  


  109.                    Log.v(TAG, "由下拉刷新狀態到刷新完成狀態");  

  110.                }  

  111. if (state == RELEASE_To_REFRESH) {  

  112.                    state = REFRESHING;  

  113.                    changeHeaderViewByState();  

  114.                    onRefresh();  


  115.                    Log.v(TAG, "由鬆開刷新狀態,到刷新完成狀態");  

  116.                }  

  117.            }  


  118.            isRecored = false;  

  119.            isBack = false;  


  120. break;  


  121. case MotionEvent.ACTION_MOVE:  

  122. int tempY = (int) event.getY();  

  123. if (!isRecored && firstItemIndex == 0) {  

  124.                Log.v(TAG, "記錄拖拽時的位置");  

  125.                isRecored = true;  

  126.                startY = tempY;  

  127.            }  

  128. if (state != REFRESHING && isRecored) {  

  129. // 可以鬆開刷新了

  130. if (state == RELEASE_To_REFRESH) {  

  131. // 往上推,推到屏幕足夠掩蓋head的程度,但還沒有全部掩蓋

  132. if ((tempY - startY < headContentHeight)  

  133.                            && (tempY - startY) > 0) {  

  134.                        state = PULL_To_REFRESH;  

  135.                        changeHeaderViewByState();  


  136.                        Log.v(TAG, "由鬆開刷新狀態轉變到下拉刷新狀態");  

  137.                    }  

  138. // 一下子推到頂

  139. elseif (tempY - startY <= 0) {  

  140.                        state = DONE;  

  141.                        changeHeaderViewByState();  


  142.                        Log.v(TAG, "由鬆開刷新狀態轉變到done狀態");  

  143.                    }  

  144. // 往下拉,或者還沒有上推到屏幕頂部掩蓋head

  145. else {  

  146. // 不用進行特別的操作,只用更新paddingTop的值就行了

  147.                    }  

  148.                }  

  149. // 還沒有到達顯示鬆開刷新的時候,DONE或者是PULL_To_REFRESH狀態

  150. if (state == PULL_To_REFRESH) {  

  151. // 下拉到可以進入RELEASE_TO_REFRESH的狀態

  152. if (tempY - startY >= headContentHeight) {  

  153.                        state = RELEASE_To_REFRESH;  

  154.                        isBack = true;  

  155.                        changeHeaderViewByState();  


  156.                        Log.v(TAG, "由done或者下拉刷新狀態轉變到鬆開刷新");  

  157.                    }  

  158. // 上推到頂了

  159. elseif (tempY - startY <= 0) {  

  160.                        state = DONE;  

  161.                        changeHeaderViewByState();  


  162.                        Log.v(TAG, "由Done或者下拉刷新狀態轉變到done狀態");  

  163.                    }  

  164.                }  


  165. // done狀態下

  166. if (state == DONE) {  

  167. if (tempY - startY > 0) {  

  168.                        state = PULL_To_REFRESH;  

  169.                        changeHeaderViewByState();  

  170.                    }  

  171.                }  


  172. // 更新headView的size

  173. if (state == PULL_To_REFRESH) {  

  174.                    headView.setPadding(0, -1 * headContentHeight  

  175.                            + (tempY - startY), 0, 0);  

  176.                    headView.invalidate();  

  177.                }  


  178. // 更新headView的paddingTop

  179. if (state == RELEASE_To_REFRESH) {  

  180.                    headView.setPadding(0, tempY - startY - headContentHeight,  

  181. 0, 0);  

  182.                    headView.invalidate();  

  183.                }  

  184.            }  

  185. break;  

  186.        }  

  187. returnsuper.onTouchEvent(event);  

  188.    }  


  189. // 當狀態改變時候,調用該方法,以更新界面

  190. privatevoid changeHeaderViewByState() {  

  191. switch (state) {  

  192. case RELEASE_To_REFRESH:  


  193.            arrowImageView.setVisibility(View.VISIBLE);  

  194.            progressBar.setVisibility(View.GONE);  

  195.            tipsTextview.setVisibility(View.VISIBLE);  

  196.            lastUpdatedTextView.setVisibility(View.VISIBLE);  


  197.            arrowImageView.clearAnimation();  

  198.            arrowImageView.startAnimation(animation);  


  199.            tipsTextview.setText("鬆開可以刷新");  


  200.            Log.v(TAG, "當前狀態,鬆開刷新");  

  201. break;  

  202. case PULL_To_REFRESH:  


  203.            progressBar.setVisibility(View.GONE);  

  204.            tipsTextview.setVisibility(View.VISIBLE);  

  205.            lastUpdatedTextView.setVisibility(View.VISIBLE);  

  206.            arrowImageView.clearAnimation();  

  207.            arrowImageView.setVisibility(View.VISIBLE);  

  208. if (isBack) {  

  209.                isBack = false;  

  210.                arrowImageView.clearAnimation();  

  211.                arrowImageView.startAnimation(reverseAnimation);  


  212.                tipsTextview.setText("下拉可以刷新");  

  213.            } else {  

  214.                tipsTextview.setText("下拉可以刷新");  

  215.            }  

  216.            Log.v(TAG, "當前狀態,下拉刷新");  

  217. break;  


  218. case REFRESHING:  


  219.            headView.setPadding(0, 0, 0, 0);  

  220.            headView.invalidate();  


  221.            progressBar.setVisibility(View.VISIBLE);  

  222.            arrowImageView.clearAnimation();  

  223.            arrowImageView.setVisibility(View.GONE);  

  224.            tipsTextview.setText("正在刷新,請稍後...");  

  225.            lastUpdatedTextView.setVisibility(View.GONE);  


  226.            Log.v(TAG, "當前狀態,正在刷新...");  

  227. break;  

  228. case DONE:  

  229.            headView.setPadding(0, -1 * headContentHeight, 0, 0);  

  230.            headView.invalidate();  


  231.            progressBar.setVisibility(View.GONE);  

  232.            arrowImageView.clearAnimation();  

  233. // 此處更換圖標

  234.            arrowImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);  


  235.            tipsTextview.setText("下拉可以刷新");  

  236.            lastUpdatedTextView.setVisibility(View.VISIBLE);  


  237.            Log.v(TAG, "當前狀態,done");  

  238. break;  

  239.        }  

  240.    }  


  241. publicvoid setonRefreshListener(OnRefreshListener refreshListener) {  

  242. this.refreshListener = refreshListener;  

  243.    }  


  244. publicinterface OnRefreshListener {  

  245. publicvoid onRefresh();  

  246.    }  


  247. publicvoid onRefreshComplete() {  

  248.        state = DONE;  

  249.        lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());  

  250.        changeHeaderViewByState();  

  251.    }  


  252. privatevoid onRefresh() {  

  253. if (refreshListener != null) {  

  254.            refreshListener.onRefresh();  

  255.        }  

  256.    }  


  257. // 此處是“估計”headView的width以及height

  258. privatevoid measureView(View child) {  

  259.        ViewGroup.LayoutParams p = child.getLayoutParams();  

  260. if (p == null) {  

  261.            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  

  262.                    ViewGroup.LayoutParams.WRAP_CONTENT);  

  263.        }  

  264. int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);  

  265. int lpHeight = p.height;  

  266. int childHeightSpec;  

  267. if (lpHeight > 0) {  

  268.            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,  

  269.                    MeasureSpec.EXACTLY);  

  270.        } else {  

  271.            childHeightSpec = MeasureSpec.makeMeasureSpec(0,  

  272.                    MeasureSpec.UNSPECIFIED);  

  273.        }  

  274.        child.measure(childWidthSpec, childHeightSpec);  

  275.    }  


  276. }  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章