PullToRefresh是一套實作非常好的下拉重新整理庫(下載下傳位址:https://github.com/chrisbanes/Android-PullToRefresh),它支援:
1.ListView
2.ExpandableListView
3.GridView
4.WebView
5.ScrollView
6.Horizontal ScrollView
7.ViewPager
等多種常用的需要重新整理的View類型,而且使用起來也十分友善。下載下傳下來解壓裡面有extras,library,sample這三個檔案夾。sample是demo,libarary是類庫,我們主要用到的是library這個檔案夾。下面就用官網給的demo來具體說明如何使用吧,下面介紹下ListView的使用
使用時直接在布局中引用即可
<com.handmark.pulltorefresh.library.PullToRefreshListView
android:id="@+id/pull_refresh_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:divider="#19000000"
android:dividerHeight="4dp"
android:fadingEdge="none"
android:fastScrollEnabled="false"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"
android:smoothScrollbar="true" />
在其他布局檔案中,我們也許會看到xmlns:ptr="http://schemas.android.com/apk/res-auto"這個自定義的命名空間,看一下這個控件裡面的一些基本屬性:
1.ptr:ptrDrawable="";//這裡可以設定自己的上拉下拉圖示。
2.ptr:ptrHeaderBackground="";//上拉時底部的背景色,下拉時頭部的背景色
3.ptr:ptrHeaderTextColor="";//上拉,下拉時Header,Footer顯示的字型顔色
4.ptr:ptrHeaderSubTextColor="";//上拉,下拉Header,Footer中上次重新整理時間的顔色
5.ptr:ptrShowIndicator="";//true時會在控件的右上角和右下角出現設定的icon
6.ptr:ptrAnimationStyle="";//顯示時候圖示的取值 ;flip:翻轉;rotate:旋轉
7.ptr:ptrRotateDrawableWhilePulling="";//當動畫為rotate時,下拉是否旋轉
8.ptr:ptrRefreshableViewBackground="";//設定整個控件布局的背景顔色
9.ptr:ptrScrollingWhileRefreshingEnabled="";//重新整理的時候是否允許ListView或者GridView滾動。推薦使用true
10.ptr:prtListViewExtrasEnabled="";//決定Header,Footer以何種方式加入PullToRefreshListView.其中為true時,就是以Header的方式加入,在滾動重新整理時頭部會跟着一起滾動;為false時,就是以Footer的方式加入,在滾動的時候底部會跟着一起滾動。
11.ptr:ptrMode="";//設定是上拉,下拉還是兩者都支援。both:兩者都支援;disabled:禁用下拉重新整理;pullFromStart:僅支援下拉重新整理;pullUpFromBottom:僅支援上拉加載;manualOnly:隻允許手動觸發。(注意:如果不在代碼設定它預設的就隻有下拉重新整理)
以上隻是一些基本屬性:一般你用的什麼控件它都包含原有控件的所有屬性,如你使用的是PullToRefreshListView那麼它裡面同樣包含ListView的一些屬性。同樣上面的屬性你都可以在代碼中去set***使用。
Activity代碼:
public final class PullToRefreshListActivity extends ListActivity {
static final int MENU_MANUAL_REFRESH = 0;
static final int MENU_DISABLE_SCROLL = 1;
static final int MENU_SET_MODE = 2;
static final int MENU_DEMO = 3;
private LinkedList<String> mListItems;
private PullToRefreshListView mPullRefreshListView;
private ArrayAdapter<String> mAdapter;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ptr_list);
mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
// mPullRefreshListView.setMode(Mode.BOTH);
// Set a listener to be invoked when the list should be refreshed.
mPullRefreshListView
.setOnRefreshListener(
//雙向滑動
// new OnRefreshListener2<ListView>() {
//
// @Override
// public void onPullDownToRefresh(
// PullToRefreshBase<ListView> refreshView) {
// // TODO Auto-generated method stub
// new GetDataTask().execute();
// }
//
// @Override
// public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
// // TODO Auto-generated method stub
// new GetDataTask().execute();
// }
//
// }
// );
//單方向滑動
new OnRefreshListener<ListView>() {
@Override
public void onRefresh(
PullToRefreshBase<ListView> refreshView) {
String label = DateUtils.formatDateTime(
getApplicationContext(),
System.currentTimeMillis(),
DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_ABBREV_ALL);
// Update the LastUpdatedLabel
refreshView.getLoadingLayoutProxy()
.setLastUpdatedLabel(label);
// Do work to refresh the list here.
new GetDataTask().execute();
}
});
// Add an end-of-list listener
mPullRefreshListView
.setOnLastItemVisibleListener(new OnLastItemVisibleListener() {
@Override
public void onLastItemVisible() {
Toast.makeText(PullToRefreshListActivity.this,
"End of List!", Toast.LENGTH_SHORT).show();
}
});
ListView actualListView = mPullRefreshListView.getRefreshableView();
// Need to use the Actual ListView when registering for Context Menu
registerForContextMenu(actualListView);
mListItems = new LinkedList<String>();
mListItems.addAll(Arrays.asList(mStrings));
mAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, mListItems);
/**
* Add Sound Event Listener
*/
SoundPullEventListener<ListView> soundListener = new SoundPullEventListener<ListView>(
this);
soundListener.addSoundEvent(State.PULL_TO_REFRESH, R.raw.pull_event);
soundListener.addSoundEvent(State.RESET, R.raw.reset_sound);
soundListener.addSoundEvent(State.REFRESHING, R.raw.refreshing_sound);
mPullRefreshListView.setOnPullEventListener(soundListener);
// You can also just use setListAdapter(mAdapter) or
// mPullRefreshListView.setAdapter(mAdapter)
actualListView.setAdapter(mAdapter);
}
private class GetDataTask extends AsyncTask<Void, Void, String[]> {
@Override
protected String[] doInBackground(Void... params) {
// Simulates a background job.
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
}
return mStrings;
}
@Override
protected void onPostExecute(String[] result) {
mListItems.addFirst("Added after refresh...");
mAdapter.notifyDataSetChanged();
// Call onRefreshComplete when the list has been refreshed.
mPullRefreshListView.onRefreshComplete();
super.onPostExecute(result);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_MANUAL_REFRESH, 0, "Manual Refresh");
menu.add(
0,
MENU_DISABLE_SCROLL,
1,
mPullRefreshListView.isScrollingWhileRefreshingEnabled() ? "Disable Scrolling while Refreshing"
: "Enable Scrolling while Refreshing");
menu.add(
0,
MENU_SET_MODE,
0,
mPullRefreshListView.getMode() == Mode.BOTH ? "Change to MODE_PULL_DOWN"
: "Change to MODE_PULL_BOTH");
menu.add(0, MENU_DEMO, 0, "Demo");
return super.onCreateOptionsMenu(menu);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
menu.setHeaderTitle("Item: "
+ getListView().getItemAtPosition(info.position));
menu.add("Item 1");
menu.add("Item 2");
menu.add("Item 3");
menu.add("Item 4");
super.onCreateContextMenu(menu, v, menuInfo);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem disableItem = menu.findItem(MENU_DISABLE_SCROLL);
disableItem
.setTitle(mPullRefreshListView
.isScrollingWhileRefreshingEnabled() ? "Disable Scrolling while Refreshing"
: "Enable Scrolling while Refreshing");
MenuItem setModeItem = menu.findItem(MENU_SET_MODE);
setModeItem
.setTitle(mPullRefreshListView.getMode() == Mode.BOTH ? "Change to MODE_FROM_START"
: "Change to MODE_PULL_BOTH");
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_MANUAL_REFRESH:
new GetDataTask().execute();
mPullRefreshListView.setRefreshing(false);
break;
case MENU_DISABLE_SCROLL:
mPullRefreshListView
.setScrollingWhileRefreshingEnabled(!mPullRefreshListView
.isScrollingWhileRefreshingEnabled());
break;
case MENU_SET_MODE:
mPullRefreshListView
.setMode(mPullRefreshListView.getMode() == Mode.BOTH ? Mode.PULL_FROM_START
: Mode.BOTH);
break;
case MENU_DEMO:
mPullRefreshListView.demo();
break;
}
return super.onOptionsItemSelected(item);
}
private String[] mStrings = {"Abbaye de Belloc",
"Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
"Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu",
"Airag", "Airedale", "Aisy Cendre", "Allgauer Emmentaler",
"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam",
"Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis",
"Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
"Allgauer Emmentaler"
};
}
具體說下代碼,初始化PullToRefreshListView,重新整理模式可以在xml檔案中進行設定,也可以在java代碼中通過setMode()設定,然後設定監聽器OnRefreshListener,但這個監聽器隻能監聽一個方向的,如果要實作兩個方向那麼就使用OnRefreshListener2監聽器,同時别忘了了設定模式為Mode.BOTH。
但是PullToRefresh作者已經好久不更新了,現在很多人也在此基礎上做了很多的擴充,大家可以上網自己搜下。google官方在最新的android.support.v4包中增加了一個新類SwipeRefreshLayout,這個類的作用就是提供官方的下拉重新整理,并且效果相當不錯,而上拉加載更多則用我們自定義的listview,也是相當簡單,我們在下面的例子就用一個最簡單的例子去實作一個我們的上拉加載、下拉重新整理的功能。它是一個viewgroup,但是它隻允許有一個子控件,子控件能是任何view,使用的時候,所在類實作OnRefreshListener接口,在onRefresh()方法中實作所要完成的任務。好了,現在思路明确了,SwipeRefreshLayout已經實作了下拉重新整理的功能,我們要做的就是實作一個能夠上拉加載的元件,然後放到SwipeRefreshLayout中即可,好,上代碼:
自定義一個ListView:
public class LoadMoreListView extends ListView implements OnScrollListener {
/**
* 滑動到最下面時的上拉操作
*/
private int mTouchSlop;
/**
* ListView的加載中footer
*/
private View mListViewFooter;
/**
* 上拉監聽器, 到了最底部的上拉加載操作
*/
private OnLoadMoreListener mOnLoadMoreListener;
/**
* 按下時的y坐标
*/
private int mYDown;
/**
* 擡起時的y坐标, 與mYDown一起用于滑動到底部時判斷是上拉還是下拉
*/
private int mLastY;
/**
* 是否在加載中 ( 上拉加載更多 )
*/
private boolean isLoading = false;
/**
* 加載更多的監聽器
*/
public static interface OnLoadMoreListener {
public void onLoad();
}
public LoadMoreListView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mListViewFooter = LayoutInflater.from(context).inflate(
R.layout.listview_footer, null, false);
this.addFooterView(mListViewFooter);
mListViewFooter.setVisibility(View.GONE);
this.setOnScrollListener(this);
}
public LoadMoreListView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
/**
* @param loadMoreListener
*/
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
mOnLoadMoreListener = loadMoreListener;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 按下
mYDown = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
// 移動
mLastY = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP:
// 擡起
if (canLoad()) {
loadData();
}
break;
default:
break;
}
return super.dispatchTouchEvent(event);
}
/**
* @param loading
*/
public void setLoading(boolean loading) {
isLoading = loading;
if (isLoading) {
mListViewFooter.setVisibility(View.VISIBLE);
} else {
mListViewFooter.setVisibility(View.GONE);
mYDown = 0;
mLastY = 0;
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 滾動時到了最底部也可以加載更多
if (canLoad()) {
loadData();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
/**
* 是否可以加載更多, 條件是:到了最底部, listView不在加載中且為上拉操作.
*
* @return
*/
private boolean canLoad() {
return isBottom() && !isLoading && isPullUp();
}
/**
* 如果到了最底部,而且是上拉操作.那麼執行onLoad方法
*/
private void loadData() {
if (mOnLoadMoreListener != null) {
// 設定狀态
setLoading(true);
//
mOnLoadMoreListener.onLoad();
}
}
/**
* 判斷是否到了最底部
*/
private boolean isBottom() {
if (this.getAdapter() != null) {
return this.getLastVisiblePosition() == (this.getAdapter()
.getCount() - 1);
}
return false;
}
/**
* 是否是上拉操作
*
* @return
*/
private boolean isPullUp() {
return (mYDown - mLastY) >= mTouchSlop;
}
}
我們實作了OnScrollListener監聽器,同時寫了一個OnLoadMoreListener 監聽器接口,然後在OnScrollListener中回調相關定義的方法,而接口的具體操作就需要我們在ListView的實作類中實作了
而主布局就相當簡單了,直接在SwipeRefreshLayout布局中加入我們的ListView
<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" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.xmr.swiperefreshlayout.LoadMoreListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.xmr.swiperefreshlayout.LoadMoreListView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
MainActivity類如下:
public class MainActivity extends Activity implements OnRefreshListener,
OnLoadMoreListener {
private SwipeRefreshLayout swipeLayout;
private LoadMoreListView listView;
private ArrayAdapter<String> adapter;
private List<String> list;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setData();
setListener();
}
/**
* 初始化布局
*/
private void initView() {
swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
swipeLayout.setColorSchemeResources(R.color.color_bule2,
R.color.color_bule, R.color.color_bule2, R.color.color_bule3);
listView = (LoadMoreListView) findViewById(R.id.list);
list = new ArrayList<String>();
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);
}
/**
* 添加資料
*/
private void setData() {
for (int i = 0; i < 10; i++) {
list.add("itemText" + i);
}
adapter.notifyDataSetChanged();
}
/**
* 設定監聽
*/
private void setListener() {
swipeLayout.setOnRefreshListener(this);
listView.setLoadMoreListener(this);
}
/**
* 下拉重新整理
*/
@Override
public void onRefresh() {
swipeLayout.postDelayed(new Runnable() {
@Override
public void run() {
// 更新資料 更新完後調用該方法結束重新整理
HashMap<String, String> map = new HashMap<String, String>();
list.add("itemText" + "重新整理");
adapter.notifyDataSetChanged();
swipeLayout.setRefreshing(false);
}
}, 2000);
}
/**
* 加載更多
*/
@Override
public void onLoad() {
swipeLayout.postDelayed(new Runnable() {
@Override
public void run() {
// 更新資料 更新完後調用該方法結束重新整理
listView.setLoading(false);
HashMap<String, String> map = new HashMap<String, String>();
list.add("itemText"+ "更多");
adapter.notifyDataSetChanged();
}
}, 2000);
}
}
最終效果如下:

當然這隻是一個最簡單的實作,我們完全可以發揮我們的想象力去創造我們想要的效果,我這算是個抛磚引玉的效果吧,技術有限,多有不足之處,請大家多多指教。
參考:
https://github.com/chrisbanes/Android-PullToRefresh
http://blog.csdn.net/ueryueryuery/article/details/17440465/
http://www.itnose.net/detail/6341754.html
http://blog.csdn.net/hhhccckkk/article/details/46971835
http://www.2cto.com/kf/201505/399695.html
http://blog.csdn.net/hongshengpeng/article/details/50187259
http://www.cnblogs.com/JinyaoLi/p/3852424.html
http://blog.csdn.net/zhongkejingwang/article/details/38868463
https://github.com/bingoogolapple/BGARefreshLayout-Android
https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh
源代碼