好久沒有寫博客了,感受都懶惰了,今天說一下一個自定義的空間,就是ListView下拉列表能夠刷新的功能,相信不少同窗都看到過這種功能,最典型的就是新浪微博的下拉刷新列表了。java
廢話很少說,首先呢,下拉刷新的那個帶有progressBar的是ListView的headView,因此首先咱們須要自定義一個HeadView,以下所示:android
1 <?xml version="1.0" encoding="utf-8"?> 2 3 <!-- ListView的頭部 --> 4 5 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 6 android:layout_width="match_parent" 7 android:layout_height="wrap_content" 8 android:orientation="horizontal" > 9 10 <!-- 內容 --> 11 <RelativeLayout 12 android:layout_width="match_parent" 13 android:layout_height="wrap_content" 14 android:id="@+id/head_contentLayout" > 15 16 <!-- 箭頭頭像、進度條 --> 17 <FrameLayout 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:layout_alignParentLeft="true" 21 android:layout_centerVertical="true"> 22 23 <!-- 箭頭 --> 24 <ImageView 25 android:layout_width="wrap_content" 26 android:layout_height="wrap_content" 27 android:layout_gravity="center" 28 android:id="@+id/head_arrowImageView" 29 android:src="@drawable/arrow"/> 30 31 <!-- 進度條 --> 32 <ProgressBar 33 android:layout_width="wrap_content" 34 android:layout_height="wrap_content" 35 style="?android:attr/progressBarStyleSmall" 36 android:layout_gravity="center" 37 android:visibility="gone" 38 android:id="@+id/head_progressBar"/> 39 </FrameLayout> 40 41 <!-- 提示、最近更新 --> 42 <LinearLayout 43 android:layout_width="wrap_content" 44 android:layout_height="wrap_content" 45 android:layout_centerHorizontal="true" 46 android:orientation="vertical" 47 android:gravity="center_horizontal"> 48 49 <!-- 提示 --> 50 <TextView 51 android:layout_width="wrap_content" 52 android:layout_height="wrap_content" 53 android:textColor="@android:color/white" 54 android:id="@+id/head_tipsTextView" 55 android:textSize="@dimen/head_tips_size" 56 android:text="@string/head_tips_text"/> 57 58 <!-- 最近更新 --> 59 <TextView 60 android:layout_width="wrap_content" 61 android:layout_height="wrap_content" 62 android:id="@+id/head_lastUpdatedTextView" 63 android:textColor="@color/gold" 64 android:text="@string/lash_updated_text" 65 android:textSize="@dimen/last_updated_size"/> 66 </LinearLayout> 67 68 </RelativeLayout> 69 70 </LinearLayout>
FrameLayout裏面是兩個圖標,一個是下拉的箭頭,另外一個是刷新的ProgressBar,由於這兩個不會同時出現,因此咱們把他們房子一個FrameLayout裏面。app
右邊是兩個TextView,一個顯示提示的信息,一個顯示上次更新的詳情。ide
headView完成了,咱們要繼續ListView,並添加HeadView,在ListView中的onTouchEvent事件裏面對滑動作監聽之類的。佈局
以下是代碼:ui
1 package com.alex.helloworld; 2 3 import java.util.Date; 4 5 import android.content.Context; 6 import android.util.AttributeSet; 7 import android.view.LayoutInflater; 8 import android.view.MotionEvent; 9 import android.view.View; 10 import android.view.ViewGroup; 11 import android.view.animation.LinearInterpolator; 12 import android.view.animation.RotateAnimation; 13 import android.widget.AbsListView; 14 import android.widget.AbsListView.OnScrollListener; 15 import android.widget.ImageView; 16 import android.widget.LinearLayout; 17 import android.widget.ListAdapter; 18 import android.widget.ListView; 19 import android.widget.ProgressBar; 20 import android.widget.TextView; 21 22 public class CustomListView extends ListView implements OnScrollListener { 23 24 private final static int RELEASE_TO_REFRESH = 0; 25 private final static int PULL_TO_REFRESH = 1; 26 //正在刷新 27 private final static int REFRESHING = 2; 28 //刷新完成 29 private final static int DONE = 3; 30 private final static int LOADING = 4; 31 32 private final static int RADIO = 3; 33 34 private LayoutInflater mInflater; 35 private LinearLayout mHeadView; 36 private TextView mTipsTextView; 37 private TextView mLastUpdatedTextView; 38 private ImageView mArrowImageView; 39 private ProgressBar mProgressBar; 40 41 private RotateAnimation mAnimation; 42 private RotateAnimation mReverseAnimation; 43 private boolean mIsRecored; 44 private int mHeadContentWidth; 45 private int mHeadContentHeight; 46 private int mStartY; 47 private int mFirstItemIndex; 48 private int mState; 49 private boolean mIsBack; 50 private boolean mISRefreshable; 51 private OnRefreshListener mRefreshListener; 52 53 public CustomListView(Context context, AttributeSet attrs) { 54 super(context, attrs); 55 init(context); 56 } 57 58 private void init(Context context) { 59 // setCacheColorHint(android.R.color.black); 60 mInflater = LayoutInflater.from(context); 61 mHeadView = (LinearLayout) mInflater.inflate(R.layout.head, null); 62 mArrowImageView = (ImageView) mHeadView.findViewById(R.id.head_arrowImageView); 63 // mArrowImageView.setMinimumWidth(70); 64 // mArrowImageView.setMinimumHeight(50); 65 mProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressBar); 66 mTipsTextView = (TextView) mHeadView.findViewById(R.id.head_tipsTextView); 67 mLastUpdatedTextView = (TextView) mHeadView.findViewById(R.id.head_lastUpdatedTextView); 68 69 measureView(mHeadView); 70 mHeadContentHeight = mHeadView.getMeasuredHeight(); 71 System.out.println("mHeadContentHeight = " + mHeadContentHeight); 72 mHeadContentWidth = mHeadView.getMeasuredWidth(); 73 System.out.println("mHeadContentWidth = " + mHeadContentWidth); 74 mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0); 75 mHeadView.invalidate(); 76 addHeaderView(mHeadView, null, false); 77 setOnScrollListener(this); 78 79 mAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); 80 mAnimation.setInterpolator(new LinearInterpolator()); 81 mAnimation.setDuration(250); 82 mAnimation.setFillAfter(true); 83 84 mReverseAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); 85 mReverseAnimation.setInterpolator(new LinearInterpolator()); 86 mReverseAnimation.setDuration(250); 87 mReverseAnimation.setFillAfter(true); 88 89 mState = DONE; 90 mISRefreshable = false; 91 } 92 93 private void measureView(View child) { 94 android.view.ViewGroup.LayoutParams params = child.getLayoutParams(); 95 System.out.println("params = " + params); 96 if(params == null) { 97 params = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 98 } 99 System.out.println("lpWidth = " + params.width); 100 int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0+0, params.width); 101 System.out.println("childWidthSpec = " + childWidthSpec); 102 int lpHeight = params.height; 103 System.out.println("lpHeight = " + lpHeight); 104 int childHeightSpec; 105 if(lpHeight > 0) { 106 childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); 107 } else { 108 childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.UNSPECIFIED); 109 } 110 System.out.println("childHeightSpec = " + childHeightSpec); 111 child.measure(childWidthSpec, childHeightSpec); 112 } 113 114 @Override 115 public void onScrollStateChanged(AbsListView view, int scrollState) { 116 117 } 118 119 @Override 120 public void onScroll(AbsListView view, int firstVisibleItem, 121 int visibleItemCount, int totalItemCount) { 122 mFirstItemIndex = firstVisibleItem; 123 } 124 125 public interface OnRefreshListener { 126 public void onRefresh(); 127 } 128 129 private void onRefresh() { 130 if(mRefreshListener != null) { 131 mRefreshListener.onRefresh(); 132 } 133 } 134 135 public void onRefreshComplete() { 136 mState = DONE; 137 mLastUpdatedTextView.setText("已加載完成:" + new Date().toLocaleString()); 138 changeHeaderViewByState(); 139 } 140 141 public void setonRefreshListener(OnRefreshListener onRefreshListener) { 142 this.mRefreshListener = onRefreshListener; 143 mISRefreshable = true; 144 } 145 146 @Override 147 public boolean onTouchEvent(MotionEvent ev) { 148 if(mISRefreshable) { 149 switch (ev.getAction()) { 150 case MotionEvent.ACTION_DOWN: 151 if(mFirstItemIndex == 0 && !mIsRecored) { 152 mIsRecored = true; 153 mStartY = (int) ev.getY(); 154 } 155 break; 156 157 case MotionEvent.ACTION_UP: 158 if(mState != REFRESHING && mState != LOADING) { 159 if(mState == DONE) { 160 161 } 162 if(mState == PULL_TO_REFRESH) { 163 mState = DONE; 164 changeHeaderViewByState(); 165 } 166 if(mState == RELEASE_TO_REFRESH) { 167 mState = REFRESHING; 168 changeHeaderViewByState(); 169 onRefresh(); 170 } 171 } 172 mIsBack = false; 173 mIsRecored = false; 174 break; 175 176 case MotionEvent.ACTION_MOVE: 177 int tempY = (int) ev.getY(); 178 if(!mIsRecored && mFirstItemIndex == 0) { 179 mIsRecored = true; 180 mStartY = tempY; 181 } 182 if(mState != REFRESHING && mIsRecored && mState != LOADING) { 183 if(mState == RELEASE_TO_REFRESH) { 184 setSelection(0); 185 if((tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0) { 186 mState = PULL_TO_REFRESH; 187 changeHeaderViewByState(); 188 } else if(tempY - mStartY <= 0) { 189 mState = DONE; 190 changeHeaderViewByState(); 191 } 192 } 193 194 if(mState == PULL_TO_REFRESH) { 195 setSelection(0); 196 if((tempY - mStartY)/RADIO >= mHeadContentHeight) { 197 mState = RELEASE_TO_REFRESH; 198 mIsBack = true; 199 changeHeaderViewByState(); 200 } 201 } else if(tempY - mStartY <= 0) { 202 mState = DONE; 203 changeHeaderViewByState(); 204 } 205 206 if(mState == DONE) { 207 if(tempY - mStartY > 0) { 208 mState = PULL_TO_REFRESH; 209 changeHeaderViewByState(); 210 } 211 } 212 213 if(mState == PULL_TO_REFRESH) { 214 mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0); 215 } 216 217 if(mState == RELEASE_TO_REFRESH) { 218 mHeadView.setPadding(0, (tempY - mStartY)/RADIO - mHeadContentHeight, 0, 0); 219 } 220 } 221 break; 222 223 default: 224 break; 225 } 226 } 227 return super.onTouchEvent(ev); 228 } 229 230 private void changeHeaderViewByState() { 231 switch (mState) { 232 case PULL_TO_REFRESH: 233 mProgressBar.setVisibility(GONE); 234 mTipsTextView.setVisibility(VISIBLE); 235 mLastUpdatedTextView.setVisibility(VISIBLE); 236 mArrowImageView.clearAnimation(); 237 mArrowImageView.setVisibility(VISIBLE); 238 if(mIsBack) { 239 mIsBack = false; 240 mArrowImageView.clearAnimation(); 241 mArrowImageView.startAnimation(mReverseAnimation); 242 mTipsTextView.setText("isBack is true!!!"); 243 } else { 244 mTipsTextView.setText("isBack is false!!!"); 245 } 246 break; 247 248 case DONE: 249 mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0); 250 mProgressBar.setVisibility(GONE); 251 mArrowImageView.clearAnimation(); 252 mArrowImageView.setImageResource(R.drawable.arrow); 253 mTipsTextView.setText("已經加載完畢-DONE"); 254 mLastUpdatedTextView.setVisibility(VISIBLE); 255 break; 256 257 case REFRESHING: 258 mHeadView.setPadding(0, 0, 0, 0); 259 mProgressBar.setVisibility(VISIBLE); 260 mArrowImageView.clearAnimation(); 261 mArrowImageView.setVisibility(GONE); 262 mTipsTextView.setText("正在加載中……REFRESHING"); 263 break; 264 265 case RELEASE_TO_REFRESH: 266 mArrowImageView.setVisibility(VISIBLE); 267 mProgressBar.setVisibility(GONE); 268 mTipsTextView.setVisibility(VISIBLE); 269 mLastUpdatedTextView.setVisibility(VISIBLE); 270 mArrowImageView.clearAnimation(); 271 mArrowImageView.startAnimation(mAnimation); 272 mTipsTextView.setText("請釋放刷新"); 273 break; 274 default: 275 break; 276 } 277 } 278 279 @Override 280 public void setAdapter(ListAdapter adapter) { 281 mLastUpdatedTextView.setText("this is in MyListView:" + new Date().toLocaleString()); 282 super.setAdapter(adapter); 283 } 284 }
代碼有點多,須要耐心的看,這裏我主要說一下大致的思路,細節就不在說了this
仍是先把截圖看一下吧,對照着比較好說:spa
代碼裏面寫了一個接口,用做刷新用的。code
手指按下屏幕的時候會走Action down,這是有一堆判斷條件,若是知足判斷條件的話,就記錄下Y軸的座標server
若是手機繼續在屏幕上滑動,會走action move,若是手指不鬆開,一直滑動,會一直走action move,這時也能夠拿到Y軸的座標,判斷如今的狀態,沒有刷新,沒有加載,剛開始的默認狀態是done的,若是向下滑動,致使座標差大於0,將狀態改成PULL_TO_REFRESH,同時改變控件的一些狀態,好比可見性等。
同時要改變headView的padding狀態,致使headview漸漸的變爲可見:if(mState == PULL_TO_REFRESH) {
mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0);
}
若是把padding設置爲-1*高度,就會致使headView看不見了。
這時手指繼續下滑,若是終點和起點的差值/3 還要大於headView的高度的話,這時改變狀態爲RELEASE_TO_REFRESH,mState = RELEASE_TO_REFRESH;
mIsBack = true;這就意味着鬆手就能夠刷新了,固然是要知足終點和起點的差值/3 還要大於headView的高度,若是tempY - mStartY <= 0,說明在向回滑動,狀態改成mState = DONE;同時改變控件的狀態。
如今只能向回滑動,由於向下滑動不會有任何效果,(tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0,繼續下滑,改變狀態不作操做,同時改變控件狀態,向回滑動,改變狀態,mState = DONE;同時改變控件狀態。
當狀態爲mState == PULL_TO_REFRESH時,鬆開手指,就是action up,不作任何改變,當狀態爲mState == RELEASE_TO_REFRESH時,鬆手,開始刷新操做。
佈局文件:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns: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 7 <com.alex.helloworld.CustomListView 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" 10 android:id="@+id/listView"/> 11 12 </LinearLayout>
Activity的文件:
1 package com.alex.helloworld; 2 3 import java.util.ArrayList; 4 5 import com.alex.helloworld.CustomListView.OnRefreshListener; 6 7 import android.app.Activity; 8 import android.app.AlertDialog.Builder; 9 import android.content.DialogInterface; 10 import android.content.pm.PackageManager.NameNotFoundException; 11 import android.os.AsyncTask; 12 import android.os.Bundle; 13 import android.view.Menu; 14 import android.widget.ArrayAdapter; 15 import android.widget.ListView; 16 17 public class HelloWord extends Activity { 18 19 // private TextView mTextView; 20 private SlidingLayout slidingLayout; 21 private ListView contentList; 22 private ArrayAdapter<String> contentListAdapter; 23 private String[] contentItems = { 24 "Content Item 1", "Content Item 2", "Content Item 3", 25 "Content Item 4", "Content Item 5", "Content Item 6", "Content Item 7", 26 "Content Item 8", "Content Item 9", "Content Item 10", "Content Item 11", 27 "Content Item 12", "Content Item 13", "Content Item 14", "Content Item 15", 28 "Content Item 16" 29 }; 30 31 private ArrayList<String> mArrays = new ArrayList<String>(); 32 33 @Override 34 protected void onCreate(Bundle savedInstanceState) { 35 super.onCreate(savedInstanceState); 36 setContentView(R.layout.test); 37 for(int i=0; i<contentItems.length; i++) { 38 mArrays.add(contentItems[i]); 39 } 40 // slidingLayout = (SlidingLayout) findViewById(R.id.sliding_layout); 41 // Button showLeftButton = (Button) findViewById(R.id.show_left_menu); 42 // Button showRightButton = (Button) findViewById(R.id.show_right_menu); 43 // contentList = (ListView) findViewById(R.id.contentList); 44 // contentListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contentItems); 45 // contentList.setAdapter(contentListAdapter); 46 // mTextView = (TextView) findViewById(R.id.tv); 47 // mTextView.setText(""); 48 // String product = Build.PRODUCT; 49 // mTextView.append(product + "\n"); 50 // mTextView.append(Build.MANUFACTURER + "\n"); 51 // mTextView.append(Build.DISPLAY + "\n"); 52 // mTextView.append(Build.VERSION.SDK); 53 // checkUpdate(); 54 55 final CustomListView listView = (CustomListView) findViewById(R.id.listView); 56 final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mArrays); 57 listView.setAdapter(adapter); 58 listView.setonRefreshListener(new OnRefreshListener() { 59 60 @Override 61 public void onRefresh() { 62 new AsyncTask<Void, Void, Void> () { 63 64 @Override 65 protected Void doInBackground(Void... params) { 66 try { 67 Thread.sleep(1000); 68 } catch (InterruptedException e) { 69 e.printStackTrace(); 70 } 71 mArrays.add("add a new one"); 72 return null; 73 } 74 75 protected void onPostExecute(Void result) { 76 adapter.notifyDataSetChanged(); 77 listView.onRefreshComplete(); 78 }; 79 80 }.execute(); 81 } 82 }); 83 } 84 85 86 // @Override 87 // public boolean onCreateOptionsMenu(Menu menu) { 88 // getMenuInflater().inflate(R.menu.hello_word, menu); 89 // return true; 90 // } 91 92 // @Override 93 // protected void onDestroy() { 94 // super.onDestroy(); 95 // } 96 97 // private void checkUpdate() { 98 // System.out.println("checkUpdate"); 99 // int localVersion = 0; 100 // int serverVersion = 0; 101 // try { 102 // localVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; 103 // serverVersion = 2; 104 // } catch (NameNotFoundException e) { 105 // e.printStackTrace(); 106 // } 107 // 108 // if(serverVersion > localVersion) { 109 // Builder builder = new Builder(this); 110 // builder.setTitle("更新軟件"); 111 // builder.setMessage("更新"); 112 // builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 113 // 114 // @Override 115 // public void onClick(DialogInterface dialog, int which) { 116 // 117 // } 118 // }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 119 // 120 // @Override 121 // public void onClick(DialogInterface dialog, int which) { 122 // dialog.dismiss(); 123 // } 124 // }); 125 // builder.show(); 126 // } 127 // } 128 }
就這麼多吧。