Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android下拉刷新完全解析

Android下拉刷新完全解析

編輯:關於Android編程

首先講一下實現原理。這裡我們將采取的方案是使用組合View的方式,先自定義一個布局繼承自LinearLayout,然後在這個布局中加入下拉頭和ListView這兩個子元素,並讓這兩個子元素縱向排列。初始化的時候,讓下拉頭向上偏移出屏幕,這樣我們看到的就只有ListView了。然後對ListView的touch事件進行監聽,如果當前ListView已經滾動到頂部並且手指還在向下拉的話,那就將下拉頭顯示出來,松手後進行刷新操作,並將下拉頭隱藏。原理示意圖如下:

\

那我們現在就來動手實現一下,新建一個項目起名叫PullToRefreshTest,先在項目中定義一個下拉頭的布局文件pull_to_refresh.xml,代碼如下所示:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KCgoKPHN0cm9uZz5baHRtbF08L3N0cm9uZz4gdmlldwogcGxhaW5jb3B5CgoKCgo8b2wgc3RhcnQ9"1" class="dp-xml">

  • xmlns:tools="http://schemas.android.com/tools"
  • android:id="@+id/pull_to_refresh_head"
  • android:layout_width="fill_parent"
  • android:layout_height="60dip" >
  • android:layout_width="200dip"
  • android:layout_height="60dip"
  • android:layout_centerInParent="true"
  • android:orientation="horizontal" >
  • android:layout_width="0dip"
  • android:layout_height="60dip"
  • android:layout_weight="3"
  • >
  • android:id="@+id/arrow"
  • android:layout_width="wrap_content"
  • android:layout_height="wrap_content"
  • android:layout_centerInParent="true"
  • android:src="@drawable/arrow"
  • />
  • android:id="@+id/progress_bar"
  • android:layout_width="30dip"
  • android:layout_height="30dip"
  • android:layout_centerInParent="true"
  • android:visibility="gone"
  • />
  • android:layout_width="0dip"
  • android:layout_height="60dip"
  • android:layout_weight="12"
  • android:orientation="vertical" >
  • android:id="@+id/description"
  • android:layout_width="fill_parent"
  • android:layout_height="0dip"
  • android:layout_weight="1"
  • android:gravity="center_horizontal|bottom"
  • android:text="@string/pull_to_refresh" />
  • android:id="@+id/updated_at"
  • android:layout_width="fill_parent"
  • android:layout_height="0dip"
  • android:layout_weight="1"
  • android:gravity="center_horizontal|top"
  • android:text="@string/updated_at" />
  • 在這個布局中,我們包含了一個下拉指示箭頭,一個下拉狀態文字提示,和一個上次更新的時間。當然,還有一個隱藏的旋轉進度條,只有正在刷新的時候我們才會將它顯示出來。
    布局中所有引用的字符串我們都放在strings.xml中,如下所示: [html] view plaincopy
    1. PullToRefreshTest
    2. 下拉可以刷新
    3. 釋放立即刷新
    4. 正在刷新…
    5. 暫未更新過
    6. 上次更新於%1$s前
    7. 剛剛更新
    8. 時間有問題
    9. 然後新建一個RefreshableView繼承自LinearLayout,代碼如下所示: [java] view plaincopy
      1. public class RefreshableView extends LinearLayout implements OnTouchListener {
      2. /**
      3. * 下拉狀態
      4. */
      5. public static final int STATUS_PULL_TO_REFRESH = 0;
      6. /**
      7. * 釋放立即刷新狀態
      8. */
      9. public static final int STATUS_RELEASE_TO_REFRESH = 1;
      10. /**
      11. * 正在刷新狀態
      12. */
      13. public static final int STATUS_REFRESHING = 2;
      14. /**
      15. * 刷新完成或未刷新狀態
      16. */
      17. public static final int STATUS_REFRESH_FINISHED = 3;
      18. /**
      19. * 下拉頭部回滾的速度
      20. */
      21. public static final int SCROLL_SPEED = -20;
      22. /**
      23. * 一分鐘的毫秒值,用於判斷上次的更新時間
      24. */
      25. public static final long ONE_MINUTE = 60 * 1000;
      26. /**
      27. * 一小時的毫秒值,用於判斷上次的更新時間
      28. */
      29. public static final long ONE_HOUR = 60 * ONE_MINUTE;
      30. /**
      31. * 一天的毫秒值,用於判斷上次的更新時間
      32. */
      33. public static final long ONE_DAY = 24 * ONE_HOUR;
      34. /**
      35. * 一月的毫秒值,用於判斷上次的更新時間
      36. */
      37. public static final long ONE_MONTH = 30 * ONE_DAY;
      38. /**
      39. * 一年的毫秒值,用於判斷上次的更新時間
      40. */
      41. public static final long ONE_YEAR = 12 * ONE_MONTH;
      42. /**
      43. * 上次更新時間的字符串常量,用於作為SharedPreferences的鍵值
      44. */
      45. private static final String UPDATED_AT = "updated_at";
      46. /**
      47. * 下拉刷新的回調接口
      48. */
      49. private PullToRefreshListener mListener;
      50. /**
      51. * 用於存儲上次更新時間
      52. */
      53. private SharedPreferences preferences;
      54. /**
      55. * 下拉頭的View
      56. */
      57. private View header;
      58. /**
      59. * 需要去下拉刷新的ListView
      60. */
      61. private ListView listView;
      62. /**
      63. * 刷新時顯示的進度條
      64. */
      65. private ProgressBar progressBar;
      66. /**
      67. * 指示下拉和釋放的箭頭
      68. */
      69. private ImageView arrow;
      70. /**
      71. * 指示下拉和釋放的文字描述
      72. */
      73. private TextView description;
      74. /**
      75. * 上次更新時間的文字描述
      76. */
      77. private TextView updateAt;
      78. /**
      79. * 下拉頭的布局參數
      80. */
      81. private MarginLayoutParams headerLayoutParams;
      82. /**
      83. * 上次更新時間的毫秒值
      84. */
      85. private long lastUpdateTime;
      86. /**
      87. * 為了防止不同界面的下拉刷新在上次更新時間上互相有沖突,使用id來做區分
      88. */
      89. private int mId = -1;
      90. /**
      91. * 下拉頭的高度
      92. */
      93. private int hideHeaderHeight;
      94. /**
      95. * 當前處理什麼狀態,可選值有STATUS_PULL_TO_REFRESH, STATUS_RELEASE_TO_REFRESH,
      96. * STATUS_REFRESHING 和 STATUS_REFRESH_FINISHED
      97. */
      98. private int currentStatus = STATUS_REFRESH_FINISHED;;
      99. /**
      100. * 記錄上一次的狀態是什麼,避免進行重復操作
      101. */
      102. private int lastStatus = currentStatus;
      103. /**
      104. * 手指按下時的屏幕縱坐標
      105. */
      106. private float yDown;
      107. /**
      108. * 在被判定為滾動之前用戶手指可以移動的最大值。
      109. */
      110. private int touchSlop;
      111. /**
      112. * 是否已加載過一次layout,這裡onLayout中的初始化只需加載一次
      113. */
      114. private boolean loadOnce;
      115. /**
      116. * 當前是否可以下拉,只有ListView滾動到頭的時候才允許下拉
      117. */
      118. private boolean ableToPull;
      119. /**
      120. * 下拉刷新控件的構造函數,會在運行時動態添加一個下拉頭的布局。
      121. *
      122. * @param context
      123. * @param attrs
      124. */
      125. public RefreshableView(Context context, AttributeSet attrs) {
      126. super(context, attrs);
      127. preferences = PreferenceManager.getDefaultSharedPreferences(context);
      128. header = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh, null, true);
      129. progressBar = (ProgressBar) header.findViewById(R.id.progress_bar);
      130. arrow = (ImageView) header.findViewById(R.id.arrow);
      131. description = (TextView) header.findViewById(R.id.description);
      132. updateAt = (TextView) header.findViewById(R.id.updated_at);
      133. touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
      134. refreshUpdatedAtValue();
      135. setOrientation(VERTICAL);
      136. addView(header, 0);
      137. }
      138. /**
      139. * 進行一些關鍵性的初始化操作,比如:將下拉頭向上偏移進行隱藏,給ListView注冊touch事件。
      140. */
      141. @Override
      142. protected void onLayout(boolean changed, int l, int t, int r, int b) {
      143. super.onLayout(changed, l, t, r, b);
      144. if (changed && !loadOnce) {
      145. hideHeaderHeight = -header.getHeight();
      146. headerLayoutParams = (MarginLayoutParams) header.getLayoutParams();
      147. headerLayoutParams.topMargin = hideHeaderHeight;
      148. header.setLayoutParams(headerLayoutParams);
      149. listView = (ListView) getChildAt(1);
      150. listView.setOnTouchListener(this);
      151. loadOnce = true;
      152. }
      153. }
      154. /**
      155. * 當ListView被觸摸時調用,其中處理了各種下拉刷新的具體邏輯。
      156. */
      157. @Override
      158. public boolean onTouch(View v, MotionEvent event) {
      159. setIsAbleToPull(event);
      160. if (ableToPull) {
      161. switch (event.getAction()) {
      162. case MotionEvent.ACTION_DOWN:
      163. yDown = event.getRawY();
      164. break;
      165. case MotionEvent.ACTION_MOVE:
      166. float yMove = event.getRawY();
      167. int distance = (int) (yMove - yDown);
      168. // 如果手指是下滑狀態,並且下拉頭是完全隱藏的,就屏蔽下拉事件
      169. if (distance <= 0 && headerLayoutParams.topMargin <= hideHeaderHeight) {
      170. return false;
      171. }
      172. if (distance < touchSlop) {
      173. return false;
      174. }
      175. if (currentStatus != STATUS_REFRESHING) {
      176. if (headerLayoutParams.topMargin > 0) {
      177. currentStatus = STATUS_RELEASE_TO_REFRESH;
      178. } else {
      179. currentStatus = STATUS_PULL_TO_REFRESH;
      180. }
      181. // 通過偏移下拉頭的topMargin值,來實現下拉效果
      182. headerLayoutParams.topMargin = (distance / 2) + hideHeaderHeight;
      183. header.setLayoutParams(headerLayoutParams);
      184. }
      185. break;
      186. case MotionEvent.ACTION_UP:
      187. default:
      188. if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
      189. // 松手時如果是釋放立即刷新狀態,就去調用正在刷新的任務
      190. new RefreshingTask().execute();
      191. } else if (currentStatus == STATUS_PULL_TO_REFRESH) {
      192. // 松手時如果是下拉狀態,就去調用隱藏下拉頭的任務
      193. new HideHeaderTask().execute();
      194. }
      195. break;
      196. }
      197. // 時刻記得更新下拉頭中的信息
      198. if (currentStatus == STATUS_PULL_TO_REFRESH
      199. || currentStatus == STATUS_RELEASE_TO_REFRESH) {
      200. updateHeaderView();
      201. // 當前正處於下拉或釋放狀態,要讓ListView失去焦點,否則被點擊的那一項會一直處於選中狀態
      202. listView.setPressed(false);
      203. listView.setFocusable(false);
      204. listView.setFocusableInTouchMode(false);
      205. lastStatus = currentStatus;
      206. // 當前正處於下拉或釋放狀態,通過返回true屏蔽掉ListView的滾動事件
      207. return true;
      208. }
      209. }
      210. return false;
      211. }
      212. /**
      213. * 給下拉刷新控件注冊一個監聽器。
      214. *
      215. * @param listener
      216. * 監聽器的實現。
      217. * @param id
      218. * 為了防止不同界面的下拉刷新在上次更新時間上互相有沖突, 請不同界面在注冊下拉刷新監聽器時一定要傳入不同的id。
      219. */
      220. public void setOnRefreshListener(PullToRefreshListener listener, int id) {
      221. mListener = listener;
      222. mId = id;
      223. }
      224. /**
      225. * 當所有的刷新邏輯完成後,記錄調用一下,否則你的ListView將一直處於正在刷新狀態。
      226. */
      227. public void finishRefreshing() {
      228. currentStatus = STATUS_REFRESH_FINISHED;
      229. preferences.edit().putLong(UPDATED_AT + mId, System.currentTimeMillis()).commit();
      230. new HideHeaderTask().execute();
      231. }
      232. /**
      233. * 根據當前ListView的滾動狀態來設定 {@link #ableToPull}
      234. * 的值,每次都需要在onTouch中第一個執行,這樣可以判斷出當前應該是滾動ListView,還是應該進行下拉。
      235. *
      236. * @param event
      237. */
      238. private void setIsAbleToPull(MotionEvent event) {
      239. View firstChild = listView.getChildAt(0);
      240. if (firstChild != null) {
      241. int firstVisiblePos = listView.getFirstVisiblePosition();
      242. if (firstVisiblePos == 0 && firstChild.getTop() == 0) {
      243. if (!ableToPull) {
      244. yDown = event.getRawY();
      245. }
      246. // 如果首個元素的上邊緣,距離父布局值為0,就說明ListView滾動到了最頂部,此時應該允許下拉刷新
      247. ableToPull = true;
      248. } else {
      249. if (headerLayoutParams.topMargin != hideHeaderHeight) {
      250. headerLayoutParams.topMargin = hideHeaderHeight;
      251. header.setLayoutParams(headerLayoutParams);
      252. }
      253. ableToPull = false;
      254. }
      255. } else {
      256. // 如果ListView中沒有元素,也應該允許下拉刷新
      257. ableToPull = true;
      258. }
      259. }
      260. /**
      261. * 更新下拉頭中的信息。
      262. */
      263. private void updateHeaderView() {
      264. if (lastStatus != currentStatus) {
      265. if (currentStatus == STATUS_PULL_TO_REFRESH) {
      266. description.setText(getResources().getString(R.string.pull_to_refresh));
      267. arrow.setVisibility(View.VISIBLE);
      268. progressBar.setVisibility(View.GONE);
      269. rotateArrow();
      270. } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
      271. description.setText(getResources().getString(R.string.release_to_refresh));
      272. arrow.setVisibility(View.VISIBLE);
      273. progressBar.setVisibility(View.GONE);
      274. rotateArrow();
      275. } else if (currentStatus == STATUS_REFRESHING) {
      276. description.setText(getResources().getString(R.string.refreshing));
      277. progressBar.setVisibility(View.VISIBLE);
      278. arrow.clearAnimation();
      279. arrow.setVisibility(View.GONE);
      280. }
      281. refreshUpdatedAtValue();
      282. }
      283. }
      284. /**
      285. * 根據當前的狀態來旋轉箭頭。
      286. */
      287. private void rotateArrow() {
      288. float pivotX = arrow.getWidth() / 2f;
      289. float pivotY = arrow.getHeight() / 2f;
      290. float fromDegrees = 0f;
      291. float toDegrees = 0f;
      292. if (currentStatus == STATUS_PULL_TO_REFRESH) {
      293. fromDegrees = 180f;
      294. toDegrees = 360f;
      295. } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
      296. fromDegrees = 0f;
      297. toDegrees = 180f;
      298. }
      299. RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
      300. animation.setDuration(100);
      301. animation.setFillAfter(true);
      302. arrow.startAnimation(animation);
      303. }
      304. /**
      305. * 刷新下拉頭中上次更新時間的文字描述。
      306. */
      307. private void refreshUpdatedAtValue() {
      308. lastUpdateTime = preferences.getLong(UPDATED_AT + mId, -1);
      309. long currentTime = System.currentTimeMillis();
      310. long timePassed = currentTime - lastUpdateTime;
      311. long timeIntoFormat;
      312. String updateAtValue;
      313. if (lastUpdateTime == -1) {
      314. updateAtValue = getResources().getString(R.string.not_updated_yet);
      315. } else if (timePassed < 0) {
      316. updateAtValue = getResources().getString(R.string.time_error);
      317. } else if (timePassed < ONE_MINUTE) {
      318. updateAtValue = getResources().getString(R.string.updated_just_now);
      319. } else if (timePassed < ONE_HOUR) {
      320. timeIntoFormat = timePassed / ONE_MINUTE;
      321. String value = timeIntoFormat + "分鐘";
      322. updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
      323. } else if (timePassed < ONE_DAY) {
      324. timeIntoFormat = timePassed / ONE_HOUR;
      325. String value = timeIntoFormat + "小時";
      326. updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
      327. } else if (timePassed < ONE_MONTH) {
      328. timeIntoFormat = timePassed / ONE_DAY;
      329. String value = timeIntoFormat + "天";
      330. updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
      331. } else if (timePassed < ONE_YEAR) {
      332. timeIntoFormat = timePassed / ONE_MONTH;
      333. String value = timeIntoFormat + "個月";
      334. updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
      335. } else {
      336. timeIntoFormat = timePassed / ONE_YEAR;
      337. String value = timeIntoFormat + "年";
      338. updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
      339. }
      340. updateAt.setText(updateAtValue);
      341. }
      342. /**
      343. * 正在刷新的任務,在此任務中會去回調注冊進來的下拉刷新監聽器。
      344. *
      345. * @author guolin
      346. */
      347. class RefreshingTask extends AsyncTask {
      348. @Override
      349. protected Void doInBackground(Void... params) {
      350. int topMargin = headerLayoutParams.topMargin;
      351. while (true) {
      352. topMargin = topMargin + SCROLL_SPEED;
      353. if (topMargin <= 0) {
      354. topMargin = 0;
      355. break;
      356. }
      357. publishProgress(topMargin);
      358. sleep(10);
      359. }
      360. currentStatus = STATUS_REFRESHING;
      361. publishProgress(0);
      362. if (mListener != null) {
      363. mListener.onRefresh();
      364. }
      365. return null;
      366. }
      367. @Override
      368. protected void onProgressUpdate(Integer... topMargin) {
      369. updateHeaderView();
      370. headerLayoutParams.topMargin = topMargin[0];
      371. header.setLayoutParams(headerLayoutParams);
      372. }
      373. }
      374. /**
      375. * 隱藏下拉頭的任務,當未進行下拉刷新或下拉刷新完成後,此任務將會使下拉頭重新隱藏。
      376. *
      377. * @author guolin
      378. */
      379. class HideHeaderTask extends AsyncTask {
      380. @Override
      381. protected Integer doInBackground(Void... params) {
      382. int topMargin = headerLayoutParams.topMargin;
      383. while (true) {
      384. topMargin = topMargin + SCROLL_SPEED;
      385. if (topMargin <= hideHeaderHeight) {
      386. topMargin = hideHeaderHeight;
      387. break;
      388. }
      389. publishProgress(topMargin);
      390. sleep(10);
      391. }
      392. return topMargin;
      393. }
      394. @Override
      395. protected void onProgressUpdate(Integer... topMargin) {
      396. headerLayoutParams.topMargin = topMargin[0];
      397. header.setLayoutParams(headerLayoutParams);
      398. }
      399. @Override
      400. protected void onPostExecute(Integer topMargin) {
      401. headerLayoutParams.topMargin = topMargin;
      402. header.setLayoutParams(headerLayoutParams);
      403. currentStatus = STATUS_REFRESH_FINISHED;
      404. }
      405. }
      406. /**
      407. * 使當前線程睡眠指定的毫秒數。
      408. *
      409. * @param time
      410. * 指定當前線程睡眠多久,以毫秒為單位
      411. */
      412. private void sleep(int time) {
      413. try {
      414. Thread.sleep(time);
      415. } catch (InterruptedException e) {
      416. e.printStackTrace();
      417. }
      418. }
      419. /**
      420. * 下拉刷新的監聽器,使用下拉刷新的地方應該注冊此監聽器來獲取刷新回調。
      421. *
      422. * @author guolin
      423. */
      424. public interface PullToRefreshListener {
      425. /**
      426. * 刷新時會去回調此方法,在方法內編寫具體的刷新邏輯。注意此方法是在子線程中調用的, 你可以不必另開線程來進行耗時操作。
      427. */
      428. void onRefresh();
      429. }
      430. }

        這個類是整個下拉刷新功能中最重要的一個類,注釋已經寫得比較詳細了,我再簡單解釋一下。首先在RefreshableView的構造函數中動態添加了剛剛定義的pull_to_refresh這個布局作為下拉頭,然後在onLayout方法中將下拉頭向上偏移出了屏幕,再給ListView注冊了touch事件。之後每當手指在ListView上滑動時,onTouch方法就會執行。在onTouch方法中的第一行就調用了setIsAbleToPull方法來判斷ListView是否滾動到了最頂部,只有滾動到了最頂部才會執行後面的代碼,否則就視為正常的ListView滾動,不做任何處理。當ListView滾動到了最頂部時,如果手指還在向下拖動,就會改變下拉頭的偏移值,讓下拉頭顯示出來,下拉的距離設定為手指移動距離的1/2,這樣才會有拉力的感覺。如果下拉的距離足夠大,在松手的時候就會執行刷新操作,如果距離不夠大,就僅僅重新隱藏下拉頭。

        具體的刷新操作會在RefreshingTask中進行,其中在doInBackground方法中回調了PullToRefreshListener接口的onRefresh方法,這也是大家在使用RefreshableView時必須要去實現的一個接口,因為具體刷新的邏輯就應該寫在onRefresh方法中,後面會演示使用的方法。

        另外每次在下拉的時候都還會調用updateHeaderView方法來改變下拉頭中的數據,比如箭頭方向的旋轉,下拉文字描述的改變等。更加深入的理解請大家仔細去閱讀RefreshableView中的代碼。
        現在我們已經把下拉刷新的所有功能都完成了,接下來就要看一看如何在項目中引入下拉刷新了。打開或新建activity_main.xml作為程序主界面的布局,加入如下代碼: [html] view plaincopy
        1. xmlns:tools="http://schemas.android.com/tools"
        2. android:layout_width="match_parent"
        3. android:layout_height="match_parent"
        4. tools:context=".MainActivity" >
        5. android:id="@+id/refreshable_view"
        6. android:layout_width="fill_parent"
        7. android:layout_height="fill_parent" >
        8. android:id="@+id/list_view"
        9. android:layout_width="fill_parent"
        10. android:layout_height="fill_parent" >
        11. 可以看到,我們在自定義的RefreshableView中加入了一個ListView,這就意味著給這個ListView加入了下拉刷新的功能,就是這麼簡單!
          然後我們再來看一下程序的主Activity,打開或新建MainActivity,加入如下代碼: [java] view plaincopy
          1. public class MainActivity extends Activity {
          2. RefreshableView refreshableView;
          3. ListView listView;
          4. ArrayAdapter adapter;
          5. String[] items = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" };
          6. @Override
          7. protected void onCreate(Bundle savedInstanceState) {
          8. super.onCreate(savedInstanceState);
          9. requestWindowFeature(Window.FEATURE_NO_TITLE);
          10. setContentView(R.layout.activity_main);
          11. refreshableView = (RefreshableView) findViewById(R.id.refreshable_view);
          12. listView = (ListView) findViewById(R.id.list_view);
          13. adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, items);
          14. listView.setAdapter(adapter);
          15. refreshableView.setOnRefreshListener(new PullToRefreshListener() {
          16. @Override
          17. public void onRefresh() {
          18. try {
          19. Thread.sleep(3000);
          20. } catch (InterruptedException e) {
          21. e.printStackTrace();
          22. }
          23. refreshableView.finishRefreshing();
          24. }
          25. }, 0);
          26. }
          27. }

            可以看到,我們通過調用RefreshableView的setOnRefreshListener方法注冊了一個監聽器,當ListView正在刷新時就會回調監聽器的onRefresh方法,刷新的具體邏輯就在這裡處理。而且這個方法已經自動開啟了線程,可以直接在onRefresh方法中進行耗時操作,比如向服務器請求最新數據等,在這裡我就簡單讓線程睡眠3秒鐘。另外在onRefresh方法的最後,一定要調用RefreshableView中的finishRefreshing方法,這個方法是用來通知RefreshableView刷新結束了,不然我們的ListView將一直處於正在刷新的狀態。
            不知道大家有沒有注意到,setOnRefreshListener這個方法其實是有兩個參數的,我們剛剛也是傳入了一個不起眼的0。那這第二個參數是用來做什麼的呢?由於RefreshableView比較智能,它會自動幫我們記錄上次刷新完成的時間,然後下拉的時候會在下拉頭中顯示距上次刷新已過了多久。這是一個非常好用的功能,讓我們不用再自己手動去記錄和計算時間了,但是卻存在一個問題。如果當前我們的項目中有三個地方都使用到了下拉刷新的功能,現在在一處進行了刷新,其它兩處的時間也都會跟著改變!因為刷新完成的時間是記錄在配置文件中的,由於在一處刷新更改了配置文件,導致在其它兩處讀取到的配置文件時間已經是更改過的了。那解決方案是什麼?就是每個用到下拉刷新的地方,給setOnRefreshListener方法的第二個參數中傳入不同的id就行了。這樣各處的上次刷新完成時間都是單獨記錄的,相互之間就不會再有影響。
            好了,全部的代碼都在這裡了,讓我們來運行一下,看看效果吧。


            效果看起來還是非常不錯的。我們最後再來總結一下,在項目中引入ListView下拉刷新功能只需三步:

            1. 在Activity的布局文件中加入自定義的RefreshableView,並讓ListView包含在其中。

            2. 在Activity中調用RefreshableView的setOnRefreshListener方法注冊回調接口。

            3. 在onRefresh方法的最後,記得調用RefreshableView的finishRefreshing方法,通知刷新結束。

            從此以後,在項目的任何地方,一分鐘引入下拉刷新功能妥妥的。

            好了,今天的講解到此結束,有疑問的朋友請在下面留言。


    1. 上一頁:
    2. 下一頁:
    熱門文章
    閱讀排行版
    Copyright © Android教程網 All Rights Reserved