日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)

發(fā)布時間:2023/12/1 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載請注明出處:http://blog.csdn.net/guolin_blog/article/details/9255575

最 近項目中需要用到ListView下拉刷新的功能,一開始想圖省事,在網上直接找一個現成的,可是嘗試了網上多個版本的下拉刷新之后發(fā)現效果都不怎么理 想。有些是因為功能不完整或有Bug,有些是因為使用起來太復雜,十全十美的還真沒找到。因此我也是放棄了在網上找現成代碼的想法,自己花功夫編寫了一種 非常簡單的下拉刷新實現方案,現在拿出來和大家分享一下。相信在閱讀完本篇文章之后,大家都可以在自己的項目中一分鐘引入下拉刷新功能。

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

? ? ? ? ? ? ? ? ? ? ? ? ? ??

那我們現在就來動手實現一下,新建一個項目起名叫PullToRefreshTest,先在項目中定義一個下拉頭的布局文件pull_to_refresh.xml,代碼如下所示:

[html] view plaincopy
  • <RelativeLayout?xmlns:android="http://schemas.android.com/apk/res/android"??
  • ????xmlns:tools="http://schemas.android.com/tools"??
  • ????android:id="@+id/pull_to_refresh_head"??
  • ????android:layout_width="fill_parent"??
  • ????android:layout_height="60dip"?>??
  • ??
  • ????<LinearLayout??
  • ????????android:layout_width="200dip"??
  • ????????android:layout_height="60dip"??
  • ????????android:layout_centerInParent="true"??
  • ????????android:orientation="horizontal"?>??
  • ??
  • ????????<RelativeLayout??
  • ????????????android:layout_width="0dip"??
  • ????????????android:layout_height="60dip"??
  • ????????????android:layout_weight="3"??
  • ????????????>??
  • ????????????<ImageView???
  • ????????????????android:id="@+id/arrow"??
  • ????????????????android:layout_width="wrap_content"??
  • ????????????????android:layout_height="wrap_content"??
  • ????????????????android:layout_centerInParent="true"??
  • ????????????????android:src="@drawable/arrow"??
  • ????????????????/>??
  • ????????????<ProgressBar???
  • ????????????????android:id="@+id/progress_bar"??
  • ????????????????android:layout_width="30dip"??
  • ????????????????android:layout_height="30dip"??
  • ????????????????android:layout_centerInParent="true"??
  • ????????????????android:visibility="gone"??
  • ????????????????/>??
  • ????????</RelativeLayout>??
  • ??
  • ????????<LinearLayout??
  • ????????????android:layout_width="0dip"??
  • ????????????android:layout_height="60dip"??
  • ????????????android:layout_weight="12"??
  • ????????????android:orientation="vertical"?>??
  • ??
  • ????????????<TextView??
  • ????????????????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"?/>??
  • ??
  • ????????????<TextView??
  • ????????????????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"?/>??
  • ????????</LinearLayout>??
  • ????</LinearLayout>??
  • ??
  • </RelativeLayout>??
  • 在這個布局中,我們包含了一個下拉指示箭頭,一個下拉狀態(tài)文字提示,和一個上次更新的時間。當然,還有一個隱藏的旋轉進度條,只有正在刷新的時候我們才會將它顯示出來。
    布局中所有引用的字符串我們都放在strings.xml中,如下所示:

    [html] view plaincopy
  • <?xml?version="1.0"?encoding="utf-8"?>??
  • <resources>??
  • ??
  • ????<string?name="app_name">PullToRefreshTest</string>??
  • ????<string?name="pull_to_refresh">下拉可以刷新</string>??
  • ????<string?name="release_to_refresh">釋放立即刷新</string>??
  • ????<string?name="refreshing">正在刷新…</string>??
  • ????<string?name="not_updated_yet">暫未更新過</string>??
  • ????<string?name="updated_at">上次更新于%1$s前</string>??
  • ????<string?name="updated_just_now">剛剛更新</string>??
  • ????<string?name="time_error">時間有問題</string>??
  • ??????
  • </resources>??
  • 然后新建一個RefreshableView繼承自LinearLayout,代碼如下所示:

    [java] view plaincopy
  • public?class?RefreshableView?extends?LinearLayout?implements?OnTouchListener?{??
  • ??
  • ????/**?
  • ?????*?下拉狀態(tài)?
  • ?????*/??
  • ????public?static?final?int?STATUS_PULL_TO_REFRESH?=?0;??
  • ??
  • ????/**?
  • ?????*?釋放立即刷新狀態(tài)?
  • ?????*/??
  • ????public?static?final?int?STATUS_RELEASE_TO_REFRESH?=?1;??
  • ??
  • ????/**?
  • ?????*?正在刷新狀態(tài)?
  • ?????*/??
  • ????public?static?final?int?STATUS_REFRESHING?=?2;??
  • ??
  • ????/**?
  • ?????*?刷新完成或未刷新狀態(tài)?
  • ?????*/??
  • ????public?static?final?int?STATUS_REFRESH_FINISHED?=?3;??
  • ??
  • ????/**?
  • ?????*?下拉頭部回滾的速度?
  • ?????*/??
  • ????public?static?final?int?SCROLL_SPEED?=?-20;??
  • ??
  • ????/**?
  • ?????*?一分鐘的毫秒值,用于判斷上次的更新時間?
  • ?????*/??
  • ????public?static?final?long?ONE_MINUTE?=?60?*?1000;??
  • ??
  • ????/**?
  • ?????*?一小時的毫秒值,用于判斷上次的更新時間?
  • ?????*/??
  • ????public?static?final?long?ONE_HOUR?=?60?*?ONE_MINUTE;??
  • ??
  • ????/**?
  • ?????*?一天的毫秒值,用于判斷上次的更新時間?
  • ?????*/??
  • ????public?static?final?long?ONE_DAY?=?24?*?ONE_HOUR;??
  • ??
  • ????/**?
  • ?????*?一月的毫秒值,用于判斷上次的更新時間?
  • ?????*/??
  • ????public?static?final?long?ONE_MONTH?=?30?*?ONE_DAY;??
  • ??
  • ????/**?
  • ?????*?一年的毫秒值,用于判斷上次的更新時間?
  • ?????*/??
  • ????public?static?final?long?ONE_YEAR?=?12?*?ONE_MONTH;??
  • ??
  • ????/**?
  • ?????*?上次更新時間的字符串常量,用于作為SharedPreferences的鍵值?
  • ?????*/??
  • ????private?static?final?String?UPDATED_AT?=?"updated_at";??
  • ??
  • ????/**?
  • ?????*?下拉刷新的回調接口?
  • ?????*/??
  • ????private?PullToRefreshListener?mListener;??
  • ??
  • ????/**?
  • ?????*?用于存儲上次更新時間?
  • ?????*/??
  • ????private?SharedPreferences?preferences;??
  • ??
  • ????/**?
  • ?????*?下拉頭的View?
  • ?????*/??
  • ????private?View?header;??
  • ??
  • ????/**?
  • ?????*?需要去下拉刷新的ListView?
  • ?????*/??
  • ????private?ListView?listView;??
  • ??
  • ????/**?
  • ?????*?刷新時顯示的進度條?
  • ?????*/??
  • ????private?ProgressBar?progressBar;??
  • ??
  • ????/**?
  • ?????*?指示下拉和釋放的箭頭?
  • ?????*/??
  • ????private?ImageView?arrow;??
  • ??
  • ????/**?
  • ?????*?指示下拉和釋放的文字描述?
  • ?????*/??
  • ????private?TextView?description;??
  • ??
  • ????/**?
  • ?????*?上次更新時間的文字描述?
  • ?????*/??
  • ????private?TextView?updateAt;??
  • ??
  • ????/**?
  • ?????*?下拉頭的布局參數?
  • ?????*/??
  • ????private?MarginLayoutParams?headerLayoutParams;??
  • ??
  • ????/**?
  • ?????*?上次更新時間的毫秒值?
  • ?????*/??
  • ????private?long?lastUpdateTime;??
  • ??
  • ????/**?
  • ?????*?為了防止不同界面的下拉刷新在上次更新時間上互相有沖突,使用id來做區(qū)分?
  • ?????*/??
  • ????private?int?mId?=?-1;??
  • ??
  • ????/**?
  • ?????*?下拉頭的高度?
  • ?????*/??
  • ????private?int?hideHeaderHeight;??
  • ??
  • ????/**?
  • ?????*?當前處理什么狀態(tài),可選值有STATUS_PULL_TO_REFRESH,?STATUS_RELEASE_TO_REFRESH,?
  • ?????*?STATUS_REFRESHING?和?STATUS_REFRESH_FINISHED?
  • ?????*/??
  • ????private?int?currentStatus?=?STATUS_REFRESH_FINISHED;;??
  • ??
  • ????/**?
  • ?????*?記錄上一次的狀態(tài)是什么,避免進行重復操作?
  • ?????*/??
  • ????private?int?lastStatus?=?currentStatus;??
  • ??
  • ????/**?
  • ?????*?手指按下時的屏幕縱坐標?
  • ?????*/??
  • ????private?float?yDown;??
  • ??
  • ????/**?
  • ?????*?在被判定為滾動之前用戶手指可以移動的最大值。?
  • ?????*/??
  • ????private?int?touchSlop;??
  • ??
  • ????/**?
  • ?????*?是否已加載過一次layout,這里onLayout中的初始化只需加載一次?
  • ?????*/??
  • ????private?boolean?loadOnce;??
  • ??
  • ????/**?
  • ?????*?當前是否可以下拉,只有ListView滾動到頭的時候才允許下拉?
  • ?????*/??
  • ????private?boolean?ableToPull;??
  • ??
  • ????/**?
  • ?????*?下拉刷新控件的構造函數,會在運行時動態(tài)添加一個下拉頭的布局。?
  • ?????*??
  • ?????*?@param?context?
  • ?????*?@param?attrs?
  • ?????*/??
  • ????public?RefreshableView(Context?context,?AttributeSet?attrs)?{??
  • ????????super(context,?attrs);??
  • ????????preferences?=?PreferenceManager.getDefaultSharedPreferences(context);??
  • ????????header?=?LayoutInflater.from(context).inflate(R.layout.pull_to_refresh,?null,?true);??
  • ????????progressBar?=?(ProgressBar)?header.findViewById(R.id.progress_bar);??
  • ????????arrow?=?(ImageView)?header.findViewById(R.id.arrow);??
  • ????????description?=?(TextView)?header.findViewById(R.id.description);??
  • ????????updateAt?=?(TextView)?header.findViewById(R.id.updated_at);??
  • ????????touchSlop?=?ViewConfiguration.get(context).getScaledTouchSlop();??
  • ????????refreshUpdatedAtValue();??
  • ????????setOrientation(VERTICAL);??
  • ????????addView(header,?0);??
  • ????}??
  • ??
  • ????/**?
  • ?????*?進行一些關鍵性的初始化操作,比如:將下拉頭向上偏移進行隱藏,給ListView注冊touch事件。?
  • ?????*/??
  • ????@Override??
  • ????protected?void?onLayout(boolean?changed,?int?l,?int?t,?int?r,?int?b)?{??
  • ????????super.onLayout(changed,?l,?t,?r,?b);??
  • ????????if?(changed?&&?!loadOnce)?{??
  • ????????????hideHeaderHeight?=?-header.getHeight();??
  • ????????????headerLayoutParams?=?(MarginLayoutParams)?header.getLayoutParams();??
  • ????????????headerLayoutParams.topMargin?=?hideHeaderHeight;??
  • ????????????listView?=?(ListView)?getChildAt(1);??
  • ????????????listView.setOnTouchListener(this);??
  • ????????????loadOnce?=?true;??
  • ????????}??
  • ????}??
  • ??
  • ????/**?
  • ?????*?當ListView被觸摸時調用,其中處理了各種下拉刷新的具體邏輯。?
  • ?????*/??
  • ????@Override??
  • ????public?boolean?onTouch(View?v,?MotionEvent?event)?{??
  • ????????setIsAbleToPull(event);??
  • ????????if?(ableToPull)?{??
  • ????????????switch?(event.getAction())?{??
  • ????????????case?MotionEvent.ACTION_DOWN:??
  • ????????????????yDown?=?event.getRawY();??
  • ????????????????break;??
  • ????????????case?MotionEvent.ACTION_MOVE:??
  • ????????????????float?yMove?=?event.getRawY();??
  • ????????????????int?distance?=?(int)?(yMove?-?yDown);??
  • ????????????????//?如果手指是下滑狀態(tài),并且下拉頭是完全隱藏的,就屏蔽下拉事件??
  • ????????????????if?(distance?<=?0?&&?headerLayoutParams.topMargin?<=?hideHeaderHeight)?{??
  • ????????????????????return?false;??
  • ????????????????}??
  • ????????????????if?(distance?<?touchSlop)?{??
  • ????????????????????return?false;??
  • ????????????????}??
  • ????????????????if?(currentStatus?!=?STATUS_REFRESHING)?{??
  • ????????????????????if?(headerLayoutParams.topMargin?>?0)?{??
  • ????????????????????????currentStatus?=?STATUS_RELEASE_TO_REFRESH;??
  • ????????????????????}?else?{??
  • ????????????????????????currentStatus?=?STATUS_PULL_TO_REFRESH;??
  • ????????????????????}??
  • ????????????????????//?通過偏移下拉頭的topMargin值,來實現下拉效果??
  • ????????????????????headerLayoutParams.topMargin?=?(distance?/?2)?+?hideHeaderHeight;??
  • ????????????????????header.setLayoutParams(headerLayoutParams);??
  • ????????????????}??
  • ????????????????break;??
  • ????????????case?MotionEvent.ACTION_UP:??
  • ????????????default:??
  • ????????????????if?(currentStatus?==?STATUS_RELEASE_TO_REFRESH)?{??
  • ????????????????????//?松手時如果是釋放立即刷新狀態(tài),就去調用正在刷新的任務??
  • ????????????????????new?RefreshingTask().execute();??
  • ????????????????}?else?if?(currentStatus?==?STATUS_PULL_TO_REFRESH)?{??
  • ????????????????????//?松手時如果是下拉狀態(tài),就去調用隱藏下拉頭的任務??
  • ????????????????????new?HideHeaderTask().execute();??
  • ????????????????}??
  • ????????????????break;??
  • ????????????}??
  • ????????????//?時刻記得更新下拉頭中的信息??
  • ????????????if?(currentStatus?==?STATUS_PULL_TO_REFRESH??
  • ????????????????????||?currentStatus?==?STATUS_RELEASE_TO_REFRESH)?{??
  • ????????????????updateHeaderView();??
  • ????????????????//?當前正處于下拉或釋放狀態(tài),要讓ListView失去焦點,否則被點擊的那一項會一直處于選中狀態(tài)??
  • ????????????????listView.setPressed(false);??
  • ????????????????listView.setFocusable(false);??
  • ????????????????listView.setFocusableInTouchMode(false);??
  • ????????????????lastStatus?=?currentStatus;??
  • ????????????????//?當前正處于下拉或釋放狀態(tài),通過返回true屏蔽掉ListView的滾動事件??
  • ????????????????return?true;??
  • ????????????}??
  • ????????}??
  • ????????return?false;??
  • ????}??
  • ??
  • ????/**?
  • ?????*?給下拉刷新控件注冊一個監(jiān)聽器。?
  • ?????*??
  • ?????*?@param?listener?
  • ?????*????????????監(jiān)聽器的實現。?
  • ?????*?@param?id?
  • ?????*????????????為了防止不同界面的下拉刷新在上次更新時間上互相有沖突,?請不同界面在注冊下拉刷新監(jiān)聽器時一定要傳入不同的id。?
  • ?????*/??
  • ????public?void?setOnRefreshListener(PullToRefreshListener?listener,?int?id)?{??
  • ????????mListener?=?listener;??
  • ????????mId?=?id;??
  • ????}??
  • ??
  • ????/**?
  • ?????*?當所有的刷新邏輯完成后,記錄調用一下,否則你的ListView將一直處于正在刷新狀態(tài)。?
  • ?????*/??
  • ????public?void?finishRefreshing()?{??
  • ????????currentStatus?=?STATUS_REFRESH_FINISHED;??
  • ????????preferences.edit().putLong(UPDATED_AT?+?mId,?System.currentTimeMillis()).commit();??
  • ????????new?HideHeaderTask().execute();??
  • ????}??
  • ??
  • ????/**?
  • ?????*?根據當前ListView的滾動狀態(tài)來設定?{@link?#ableToPull}?
  • ?????*?的值,每次都需要在onTouch中第一個執(zhí)行,這樣可以判斷出當前應該是滾動ListView,還是應該進行下拉。?
  • ?????*??
  • ?????*?@param?event?
  • ?????*/??
  • ????private?void?setIsAbleToPull(MotionEvent?event)?{??
  • ????????View?firstChild?=?listView.getChildAt(0);??
  • ????????if?(firstChild?!=?null)?{??
  • ????????????int?firstVisiblePos?=?listView.getFirstVisiblePosition();??
  • ????????????if?(firstVisiblePos?==?0?&&?firstChild.getTop()?==?0)?{??
  • ????????????????if?(!ableToPull)?{??
  • ????????????????????yDown?=?event.getRawY();??
  • ????????????????}??
  • ????????????????//?如果首個元素的上邊緣,距離父布局值為0,就說明ListView滾動到了最頂部,此時應該允許下拉刷新??
  • ????????????????ableToPull?=?true;??
  • ????????????}?else?{??
  • ????????????????if?(headerLayoutParams.topMargin?!=?hideHeaderHeight)?{??
  • ????????????????????headerLayoutParams.topMargin?=?hideHeaderHeight;??
  • ????????????????????header.setLayoutParams(headerLayoutParams);??
  • ????????????????}??
  • ????????????????ableToPull?=?false;??
  • ????????????}??
  • ????????}?else?{??
  • ????????????//?如果ListView中沒有元素,也應該允許下拉刷新??
  • ????????????ableToPull?=?true;??
  • ????????}??
  • ????}??
  • ??
  • ????/**?
  • ?????*?更新下拉頭中的信息。?
  • ?????*/??
  • ????private?void?updateHeaderView()?{??
  • ????????if?(lastStatus?!=?currentStatus)?{??
  • ????????????if?(currentStatus?==?STATUS_PULL_TO_REFRESH)?{??
  • ????????????????description.setText(getResources().getString(R.string.pull_to_refresh));??
  • ????????????????arrow.setVisibility(View.VISIBLE);??
  • ????????????????progressBar.setVisibility(View.GONE);??
  • ????????????????rotateArrow();??
  • ????????????}?else?if?(currentStatus?==?STATUS_RELEASE_TO_REFRESH)?{??
  • ????????????????description.setText(getResources().getString(R.string.release_to_refresh));??
  • ????????????????arrow.setVisibility(View.VISIBLE);??
  • ????????????????progressBar.setVisibility(View.GONE);??
  • ????????????????rotateArrow();??
  • ????????????}?else?if?(currentStatus?==?STATUS_REFRESHING)?{??
  • ????????????????description.setText(getResources().getString(R.string.refreshing));??
  • ????????????????progressBar.setVisibility(View.VISIBLE);??
  • ????????????????arrow.clearAnimation();??
  • ????????????????arrow.setVisibility(View.GONE);??
  • ????????????}??
  • ????????????refreshUpdatedAtValue();??
  • ????????}??
  • ????}??
  • ??
  • ????/**?
  • ?????*?根據當前的狀態(tài)來旋轉箭頭。?
  • ?????*/??
  • ????private?void?rotateArrow()?{??
  • ????????float?pivotX?=?arrow.getWidth()?/?2f;??
  • ????????float?pivotY?=?arrow.getHeight()?/?2f;??
  • ????????float?fromDegrees?=?0f;??
  • ????????float?toDegrees?=?0f;??
  • ????????if?(currentStatus?==?STATUS_PULL_TO_REFRESH)?{??
  • ????????????fromDegrees?=?180f;??
  • ????????????toDegrees?=?360f;??
  • ????????}?else?if?(currentStatus?==?STATUS_RELEASE_TO_REFRESH)?{??
  • ????????????fromDegrees?=?0f;??
  • ????????????toDegrees?=?180f;??
  • ????????}??
  • ????????RotateAnimation?animation?=?new?RotateAnimation(fromDegrees,?toDegrees,?pivotX,?pivotY);??
  • ????????animation.setDuration(100);??
  • ????????animation.setFillAfter(true);??
  • ????????arrow.startAnimation(animation);??
  • ????}??
  • ??
  • ????/**?
  • ?????*?刷新下拉頭中上次更新時間的文字描述。?
  • ?????*/??
  • ????private?void?refreshUpdatedAtValue()?{??
  • ????????lastUpdateTime?=?preferences.getLong(UPDATED_AT?+?mId,?-1);??
  • ????????long?currentTime?=?System.currentTimeMillis();??
  • ????????long?timePassed?=?currentTime?-?lastUpdateTime;??
  • ????????long?timeIntoFormat;??
  • ????????String?updateAtValue;??
  • ????????if?(lastUpdateTime?==?-1)?{??
  • ????????????updateAtValue?=?getResources().getString(R.string.not_updated_yet);??
  • ????????}?else?if?(timePassed?<?0)?{??
  • ????????????updateAtValue?=?getResources().getString(R.string.time_error);??
  • ????????}?else?if?(timePassed?<?ONE_MINUTE)?{??
  • ????????????updateAtValue?=?getResources().getString(R.string.updated_just_now);??
  • ????????}?else?if?(timePassed?<?ONE_HOUR)?{??
  • ????????????timeIntoFormat?=?timePassed?/?ONE_MINUTE;??
  • ????????????String?value?=?timeIntoFormat?+?"分鐘";??
  • ????????????updateAtValue?=?String.format(getResources().getString(R.string.updated_at),?value);??
  • ????????}?else?if?(timePassed?<?ONE_DAY)?{??
  • ????????????timeIntoFormat?=?timePassed?/?ONE_HOUR;??
  • ????????????String?value?=?timeIntoFormat?+?"小時";??
  • ????????????updateAtValue?=?String.format(getResources().getString(R.string.updated_at),?value);??
  • ????????}?else?if?(timePassed?<?ONE_MONTH)?{??
  • ????????????timeIntoFormat?=?timePassed?/?ONE_DAY;??
  • ????????????String?value?=?timeIntoFormat?+?"天";??
  • ????????????updateAtValue?=?String.format(getResources().getString(R.string.updated_at),?value);??
  • ????????}?else?if?(timePassed?<?ONE_YEAR)?{??
  • ????????????timeIntoFormat?=?timePassed?/?ONE_MONTH;??
  • ????????????String?value?=?timeIntoFormat?+?"個月";??
  • ????????????updateAtValue?=?String.format(getResources().getString(R.string.updated_at),?value);??
  • ????????}?else?{??
  • ????????????timeIntoFormat?=?timePassed?/?ONE_YEAR;??
  • ????????????String?value?=?timeIntoFormat?+?"年";??
  • ????????????updateAtValue?=?String.format(getResources().getString(R.string.updated_at),?value);??
  • ????????}??
  • ????????updateAt.setText(updateAtValue);??
  • ????}??
  • ??
  • ????/**?
  • ?????*?正在刷新的任務,在此任務中會去回調注冊進來的下拉刷新監(jiān)聽器。?
  • ?????*??
  • ?????*?@author?guolin?
  • ?????*/??
  • ????class?RefreshingTask?extends?AsyncTask<Void,?Integer,?Void>?{??
  • ??
  • ????????@Override??
  • ????????protected?Void?doInBackground(Void...?params)?{??
  • ????????????int?topMargin?=?headerLayoutParams.topMargin;??
  • ????????????while?(true)?{??
  • ????????????????topMargin?=?topMargin?+?SCROLL_SPEED;??
  • ????????????????if?(topMargin?<=?0)?{??
  • ????????????????????topMargin?=?0;??
  • ????????????????????break;??
  • ????????????????}??
  • ????????????????publishProgress(topMargin);??
  • ????????????????sleep(10);??
  • ????????????}??
  • ????????????currentStatus?=?STATUS_REFRESHING;??
  • ????????????publishProgress(0);??
  • ????????????if?(mListener?!=?null)?{??
  • ????????????????mListener.onRefresh();??
  • ????????????}??
  • ????????????return?null;??
  • ????????}??
  • ??
  • ????????@Override??
  • ????????protected?void?onProgressUpdate(Integer...?topMargin)?{??
  • ????????????updateHeaderView();??
  • ????????????headerLayoutParams.topMargin?=?topMargin[0];??
  • ????????????header.setLayoutParams(headerLayoutParams);??
  • ????????}??
  • ??
  • ????}??
  • ??
  • ????/**?
  • ?????*?隱藏下拉頭的任務,當未進行下拉刷新或下拉刷新完成后,此任務將會使下拉頭重新隱藏。?
  • ?????*??
  • ?????*?@author?guolin?
  • ?????*/??
  • ????class?HideHeaderTask?extends?AsyncTask<Void,?Integer,?Integer>?{??
  • ??
  • ????????@Override??
  • ????????protected?Integer?doInBackground(Void...?params)?{??
  • ????????????int?topMargin?=?headerLayoutParams.topMargin;??
  • ????????????while?(true)?{??
  • ????????????????topMargin?=?topMargin?+?SCROLL_SPEED;??
  • ????????????????if?(topMargin?<=?hideHeaderHeight)?{??
  • ????????????????????topMargin?=?hideHeaderHeight;??
  • ????????????????????break;??
  • ????????????????}??
  • ????????????????publishProgress(topMargin);??
  • ????????????????sleep(10);??
  • ????????????}??
  • ????????????return?topMargin;??
  • ????????}??
  • ??
  • ????????@Override??
  • ????????protected?void?onProgressUpdate(Integer...?topMargin)?{??
  • ????????????headerLayoutParams.topMargin?=?topMargin[0];??
  • ????????????header.setLayoutParams(headerLayoutParams);??
  • ????????}??
  • ??
  • ????????@Override??
  • ????????protected?void?onPostExecute(Integer?topMargin)?{??
  • ????????????headerLayoutParams.topMargin?=?topMargin;??
  • ????????????header.setLayoutParams(headerLayoutParams);??
  • ????????????currentStatus?=?STATUS_REFRESH_FINISHED;??
  • ????????}??
  • ????}??
  • ??
  • ????/**?
  • ?????*?使當前線程睡眠指定的毫秒數。?
  • ?????*??
  • ?????*?@param?time?
  • ?????*????????????指定當前線程睡眠多久,以毫秒為單位?
  • ?????*/??
  • ????private?void?sleep(int?time)?{??
  • ????????try?{??
  • ????????????Thread.sleep(time);??
  • ????????}?catch?(InterruptedException?e)?{??
  • ????????????e.printStackTrace();??
  • ????????}??
  • ????}??
  • ??
  • ????/**?
  • ?????*?下拉刷新的監(jiān)聽器,使用下拉刷新的地方應該注冊此監(jiān)聽器來獲取刷新回調。?
  • ?????*??
  • ?????*?@author?guolin?
  • ?????*/??
  • ????public?interface?PullToRefreshListener?{??
  • ??
  • ????????/**?
  • ?????????*?刷新時會去回調此方法,在方法內編寫具體的刷新邏輯。注意此方法是在子線程中調用的,?你可以不必另開線程來進行耗時操作。?
  • ?????????*/??
  • ????????void?onRefresh();??
  • ??
  • ????}??
  • ??
  • }??
  • 這 個類是整個下拉刷新功能中最重要的一個類,注釋已經寫得比較詳細了,我再簡單解釋一下。首先在RefreshableView的構造函數中動態(tài)添加了剛剛 定義的pull_to_refresh這個布局作為下拉頭,然后在onLayout方法中將下拉頭向上偏移出了屏幕,再給ListView注冊了 touch事件。之后每當手指在ListView上滑動時,onTouch方法就會執(zhí)行。在onTouch方法中的第一行就調用了 setIsAbleToPull方法來判斷ListView是否滾動到了最頂部,只有滾動到了最頂部才會執(zhí)行后面的代碼,否則就視為正常的 ListView滾動,不做任何處理。當ListView滾動到了最頂部時,如果手指還在向下拖動,就會改變下拉頭的偏移值,讓下拉頭顯示出來,下拉的距 離設定為手指移動距離的1/2,這樣才會有拉力的感覺。如果下拉的距離足夠大,在松手的時候就會執(zhí)行刷新操作,如果距離不夠大,就僅僅重新隱藏下拉頭。

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

    另外每次在下拉的時候都還會調用updateHeaderView方法來改變下拉頭中的數據,比如箭頭方向的旋轉,下拉文字描述的改變等。更加深入的理解請大家仔細去閱讀RefreshableView中的代碼。
    現在我們已經把下拉刷新的所有功能都完成了,接下來就要看一看如何在項目中引入下拉刷新了。打開或新建activity_main.xml作為程序主界面的布局,加入如下代碼:

    [html] view plaincopy
  • <RelativeLayout?xmlns:android="http://schemas.android.com/apk/res/android"??
  • ????xmlns:tools="http://schemas.android.com/tools"??
  • ????android:layout_width="match_parent"??
  • ????android:layout_height="match_parent"??
  • ????tools:context=".MainActivity"?>??
  • ??
  • ????<com.example.pulltorefreshtest.RefreshableView??
  • ????????android:id="@+id/refreshable_view"??
  • ????????android:layout_width="fill_parent"??
  • ????????android:layout_height="fill_parent"?>??
  • ??
  • ????????<ListView??
  • ????????????android:id="@+id/list_view"??
  • ????????????android:layout_width="fill_parent"??
  • ????????????android:layout_height="fill_parent"?>??
  • ????????</ListView>??
  • ????</com.example.pulltorefreshtest.RefreshableView>??
  • ??
  • </RelativeLayout>??
  • 可以看到,我們在自定義的RefreshableView中加入了一個ListView,這就意味著給這個ListView加入了下拉刷新的功能,就是這么簡單!
    然后我們再來看一下程序的主Activity,打開或新建MainActivity,加入如下代碼:

    [java] view plaincopy
  • public?class?MainActivity?extends?Activity?{??
  • ??
  • ????RefreshableView?refreshableView;??
  • ????ListView?listView;??
  • ????ArrayAdapter<String>?adapter;??
  • ????String[]?items?=?{?"A",?"B",?"C",?"D",?"E",?"F",?"G",?"H",?"I",?"J",?"K",?"L"?};??
  • ??
  • ????@Override??
  • ????protected?void?onCreate(Bundle?savedInstanceState)?{??
  • ????????super.onCreate(savedInstanceState);??
  • ????????requestWindowFeature(Window.FEATURE_NO_TITLE);??
  • ????????setContentView(R.layout.activity_main);??
  • ????????refreshableView?=?(RefreshableView)?findViewById(R.id.refreshable_view);??
  • ????????listView?=?(ListView)?findViewById(R.id.list_view);??
  • ????????adapter?=?new?ArrayAdapter<String>(this,?android.R.layout.simple_list_item_1,?items);??
  • ????????listView.setAdapter(adapter);??
  • ????????refreshableView.setOnRefreshListener(new?PullToRefreshListener()?{??
  • ????????????@Override??
  • ????????????public?void?onRefresh()?{??
  • ????????????????try?{??
  • ????????????????????Thread.sleep(3000);??
  • ????????????????}?catch?(InterruptedException?e)?{??
  • ????????????????????e.printStackTrace();??
  • ????????????????}??
  • ????????????????refreshableView.finishRefreshing();??
  • ????????????}??
  • ????????},?0);??
  • ????}??
  • ??
  • }??
  • ?

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

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

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

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

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

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

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

    源碼下載,請點擊這里

    原文摘自:http://blog.csdn.net/guolin_blog/article/details/9255575

    轉載于:https://www.cnblogs.com/YangBinChina/p/3555877.html

    總結

    以上是生活随笔為你收集整理的Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。