重新編輯了下,之前得有點bug,界面也不美觀。新的下載地址 http://download.csdn.net/detail/xizhao88/7638623,歡迎指教。
網上看了幾個都不是太滿意,要嘛只是上拉刷新,要嘛就是加載更多,沒有一個完整的。自己抽時間寫了個,還是有好些地方需要優化。寫的不好還請指教,謝謝。
PullListView頂部放刷新View(PullRefreshView),底部放加載更多View(PullLoadMoreView).
例子下載地址http://download.csdn.net/detail/xizhao88/7168167
package com.zhangliucheng.pulllistview;
@SuppressLint("NewApi")
public class PullListView extends RelativeLayout implements OnTouchListener, OnScrollListener, HeaderTriggerRefresh, FooterTriggerLoadMore {
public static final int triggerHeight = 100;
private int defaultColor = Color.LTGRAY;
private Handler mHandler;
private boolean listViewIsRefreshing = false;
private boolean listViewIsLoadMore = false;
private PullRefreshView refreshView;
private PullLoadMoreView loadMoreView;
private ListView mListView;
private ScrollState scrollState = ScrollState.NORMAL;
private ListViewPullDelegate listViewDelegate;
private enum ScrollState {
NORMAL,
TOP,
DWON
}
public interface ListViewPullDelegate {
public void pullListViewTriggerRefresh(ListView listView);
public void pullListViewTriggerLoadMore(ListView listView);
}
public void setListViewDelegate(ListViewPullDelegate listViewDelegate) {
this.listViewDelegate = listViewDelegate;
}
public PullListView(Context context) {
super(context);
initView();
}
public PullListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public ListView getListView() {
return mListView;
}
public void setAdapter(ListAdapter adapter) {
mListView.setAdapter(adapter);
}
/**
* 設置刷新和加載更多View的背景色
* @param colorId
*/
public void setRefreshAndLoadMoreViewBackground(int colorId) {
refreshView.setBackgroundColor(colorId);
loadMoreView.setBackgroundColor(colorId);
}
/**
* 設置尖頭圖片
* @param imageId
*/
public void setArrowImage(int imageId) {
refreshView.setArrowImage(imageId);
loadMoreView.setArrowImage(imageId);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
//正在刷新或者加載,直接返回
if(listViewIsRefreshing || listViewIsLoadMore) {
return false;
//listView滑動到頂部,並且繼續向下滑動
} else if(scrollState == ScrollState.TOP) {
return refreshView.headerOnTouch(v, event);
//向上滑動
} else if(scrollState == ScrollState.DWON) {
return loadMoreView.footerOnTouch(v, event);
}
return false;
}
@Override
public void refresh() {
setListViewIsRefreshing(true);
if(null != listViewDelegate){
listViewDelegate.pullListViewTriggerRefresh(this.mListView);
}
}
@Override
public void refreshFinish() {
mHandler.sendEmptyMessage(0);
}
@Override
public void loadMore() {
setListViewIsLoadMore(true);
if(null != listViewDelegate){
listViewDelegate.pullListViewTriggerLoadMore(this.mListView);
}
}
@Override
public void loadMoreFinish() {
mHandler.sendEmptyMessage(1);
}
/**
* 設置RefreshView狀態
* @param isRefreshing true 進入刷新狀態,否則進入正常狀態
*/
private void setListViewIsRefreshing(boolean isRefreshing) {
if(!listViewIsRefreshing && isRefreshing) {
refreshView.setRefresh();
listViewIsRefreshing = true;
} else if(!isRefreshing) {
refreshView.setNormal();
listViewIsRefreshing = false;
}
}
/**
* 設置LoadMoreView狀態
* @param isLoadMore true 進入加載更多,否則進入正常狀態
*/
private void setListViewIsLoadMore(boolean isLoadMore) {
if(!listViewIsLoadMore && isLoadMore) {
loadMoreView.setRefresh();
listViewIsLoadMore = true;
} else if(!isLoadMore) {
loadMoreView.setNormal();
listViewIsLoadMore = false;
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if(firstVisibleItem == 0) {
setPullDirection(true);
scrollState = ScrollState.TOP;
} else if((firstVisibleItem + visibleItemCount) == totalItemCount) {
setPullDirection(false);
scrollState = ScrollState.DWON;
} else {
scrollState = ScrollState.NORMAL;
}
}
private void initView() {
mHandler = new MyHandler();
//添加刷新view
refreshView = new PullRefreshView(getContext(), this);
refreshView.setId(1);
addView(refreshView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
//添加listView
mListView = new ListView(getContext());
mListView.setId(2);
addView(mListView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
//添加加載更多
loadMoreView = new PullLoadMoreView(getContext(), this);
loadMoreView.setId(3);
addView(loadMoreView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
//設置監聽事件
mListView.setOnTouchListener(this);
mListView.setOnScrollListener(this);
setRefreshAndLoadMoreViewBackground(defaultColor);
}
/**
* 但listView滑動到頂部或者頂部會掉用該方法,重新設置佈局方式
* @param downward 爲true的時候(listView下拉到頂部), loadMoreView -below- listView,listView -blow -refreshView.
* 爲false的時候(listView上拉到底部),listView -above -refreshView, loadMoreView -above- listView
*/
private void setPullDirection(boolean downward) {
if(downward && scrollState == ScrollState.NORMAL) {
LayoutParams refreshParams = new LayoutParams(refreshView.getLayoutParams().width,
refreshView.getLayoutParams().height);
refreshParams.topMargin = -triggerHeight;
updateViewLayout(refreshView, refreshParams);
LayoutParams listViewParams = new LayoutParams(mListView.getLayoutParams().width,
mListView.getLayoutParams().height);
listViewParams.addRule(BELOW, refreshView.getId());
updateViewLayout(mListView, listViewParams);
LayoutParams loadMoreParams = new LayoutParams(loadMoreView.getLayoutParams().width,
loadMoreView.getLayoutParams().height);
loadMoreParams.addRule(BELOW, mListView.getId());
loadMoreParams.bottomMargin = -triggerHeight;
updateViewLayout(loadMoreView, loadMoreParams);
} else if(!downward && scrollState == ScrollState.NORMAL){
LayoutParams loadMoreParams = new LayoutParams(loadMoreView.getLayoutParams().width,
loadMoreView.getLayoutParams().height);
loadMoreParams.bottomMargin = -triggerHeight;
loadMoreParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
updateViewLayout(loadMoreView, loadMoreParams);
LayoutParams listViewParams = new LayoutParams(mListView.getLayoutParams().width,
mListView.getLayoutParams().height);
listViewParams.addRule(ABOVE, loadMoreView.getId());
updateViewLayout(mListView, listViewParams);
LayoutParams refreshParams = new LayoutParams(refreshView.getLayoutParams().width,
refreshView.getLayoutParams().height);
refreshParams.topMargin = -triggerHeight;
refreshParams.addRule(ABOVE, mListView.getId());
updateViewLayout(refreshView, refreshParams);
}
}
@SuppressLint("HandlerLeak")
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
setListViewIsRefreshing(false);
break;
case 1:
setListViewIsLoadMore(false);
default:
break;
}
}
}
public static class AnimatorListenerNullImpl implements AnimatorListener {
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
}
}
}
package com.zhangliucheng.pulllistview;
import com.zhangliucheng.pulllistview.PullListView.AnimatorListenerNullImpl;
@SuppressLint({ "NewApi", "ViewConstructor" })
public class PullLoadMoreView extends LinearLayout {
private final int trigger = PullListView.triggerHeight;
private final String normalInfo = "釋放加載更多...";
private final String refreshInfo = "正在加載...";
private FooterTriggerLoadMore mFooterTriggerLoadmore;
private ImageView mArrow;
private ProgressBar mProgress;
private TextView mLabel;
public interface FooterTriggerLoadMore {
public void loadMore();
public void loadMoreFinish();
}
public PullLoadMoreView(Context context, FooterTriggerLoadMore footerTriggerLoadmore) {
super(context);
this.mFooterTriggerLoadmore = footerTriggerLoadmore;
initView();
}
private float downY = 0;
public boolean footerOnTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float distance = event.getRawY() - downY;
if(distance > 0) {
scrollDistance(0);
return false;
}
scrollDistance(Math.abs(distance));;
break;
case MotionEvent.ACTION_UP:
endScroll();
break;
}
return true;
}
public void setArrowImage(int imageId) {
mArrow.setBackgroundResource(imageId);
}
public void scrollDistance(float distance) {
//isPulling爲ture,說明當前在向上pull,反之
boolean isPulling = getPaddingBottom() - distance < 0;
setPaddingBottom((int)distance);
if(distance > trigger && isPulling) {
setTriggerAnim(true);
} else if(distance < trigger && !isPulling){
setTriggerAnim(false);
}
}
public void endScroll() {
if(getPaddingBottom() > trigger) {
mFooterTriggerLoadmore.loadMore();
} else if(getPaddingBottom() < trigger){
mFooterTriggerLoadmore.loadMoreFinish();
}
}
/**
* 恢復正常狀態,將尖頭圖片顯示,等待框隱藏,並設置label的信息
*/
public void setNormal() {
ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingBottom", getPaddingBottom(), 0);
oa.setInterpolator(new LinearInterpolator());
oa.setDuration(200);
oa.start();
oa.addListener(new AnimatorListenerNullImpl() {
@Override
public void onAnimationEnd(Animator arg0) {
mArrow.setVisibility(View.VISIBLE);
mArrow.setRotation(-180);
mProgress.setVisibility(View.GONE);
mLabel.setText(normalInfo);
}
});
}
/**
* 設置正在刷新,將尖頭圖片隱藏,等待框顯示出來,並設置label的信息
*/
public void setRefresh() {
ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingBottom", getPaddingBottom(), trigger);
oa.setInterpolator(new LinearInterpolator());
oa.setDuration(200);
oa.start();
oa.addListener(new AnimatorListenerNullImpl() {
@Override
public void onAnimationEnd(Animator arg0) {
mArrow.setVisibility(View.GONE);
mProgress.setVisibility(View.VISIBLE);
mLabel.setText(refreshInfo);
}
});
}
/**
* 這個方法供 ObjectAnimator.ofInt(this, "paddingBottom", getPaddingBottom(), trigger);使用
* @param f
*/
public void setPaddingBottom(int f) {
setPadding(0, 0, 0, f);
//這句話很關鍵
((View)getParent()).setPadding(0, -f, 0, 0);
}
/**
* 下拉或上拉的時候,尖頭產生動畫
* @param isOpen
*/
public void setTriggerAnim(boolean isOpen) {
if(isOpen) {
if (mArrow.getRotation() == -180) {
mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(0).start();
}
} else {
if (mArrow.getRotation() == 0) {
mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(-180).start();
}
}
}
private void initView() {
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(HORIZONTAL);
layout.setGravity(Gravity.CENTER_VERTICAL);
addView(layout, LayoutParams.MATCH_PARENT, trigger);
RelativeLayout relLayout = new RelativeLayout(getContext());
LayoutParams relParams = new LayoutParams(80, 80);
relParams.leftMargin = 100;
layout.addView(relLayout, relParams);
//尖頭圖片
mArrow = new ImageView(getContext());
mArrow.setRotation(-180);
RelativeLayout.LayoutParams arrowParams = new RelativeLayout.LayoutParams(30, 80);
arrowParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
relLayout.addView(mArrow, arrowParams);
//進度條
mProgress = new ProgressBar(getContext());
mProgress.setVisibility(View.GONE);
RelativeLayout.LayoutParams progressPrams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
progressPrams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
relLayout.addView(mProgress, progressPrams);
//文字說明
mLabel = new TextView(getContext());
mLabel.setTextColor(Color.BLACK);
mLabel.setText(normalInfo);
mLabel.setTextSize(20);
LinearLayout.LayoutParams namePrams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
namePrams.leftMargin = 50;
layout.addView(mLabel, namePrams);
}
}
package com.zhangliucheng.pulllistview;
import com.zhangliucheng.pulllistview.PullListView.AnimatorListenerNullImpl;
/**
*
* 在listView的上面,默認是看不見的,下拉listView的時候纔會出現
*
* @author zhangliucheng
*
*/
@SuppressLint({ "NewApi", "ViewConstructor" })
public class PullRefreshView extends LinearLayout {
private final int trigger = PullListView.triggerHeight;
private final String normalInfo = "釋放刷新...";
private final String refreshInfo = "正在刷新...";
private HeaderTriggerRefresh mHeaderTriggerRefresh;
private ImageView mArrow;
private ProgressBar mProgress;
private TextView mLabel;
public interface HeaderTriggerRefresh {
public void refresh();
public void refreshFinish();
}
public PullRefreshView(Context context, HeaderTriggerRefresh headerTriggerRefresh) {
super(context);
this.mHeaderTriggerRefresh = headerTriggerRefresh;
initView();
}
private float downY = 0;
public boolean headerOnTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float distance = event.getRawY() - downY;
//如果是向上pull,直接滾動到0,並且返回
if(distance < 0) {
scrollDistance(0);
return false;
}
scrollDistance(distance);
break;
case MotionEvent.ACTION_UP:
endScroll();
break;
}
return true;
}
public void setArrowImage(int imageId) {
mArrow.setBackgroundResource(imageId);
}
public void scrollDistance(float distance) {
//isPulling爲ture,說明當前在向下pull,反之
boolean isPulling = getPaddingTop() - distance < 0;
setPaddingTop((int)distance);
//paddingTop大於trigger,並且當前在向下pull,觸發動畫
if(distance > trigger && isPulling) {
setTriggerAnim(true);
} else if(distance < trigger && !isPulling){
setTriggerAnim(false);
}
}
public void endScroll() {
if(getPaddingTop() > trigger) {
mHeaderTriggerRefresh.refresh();
} else if(getPaddingTop() < trigger){
mHeaderTriggerRefresh.refreshFinish();
}
}
/**
* 恢復正常狀態,將尖頭圖片顯示,等待框隱藏,並設置label的信息
*/
public void setNormal() {
ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingTop", getPaddingTop(), 0);
oa.setInterpolator(new LinearInterpolator());
oa.setDuration(200);
oa.start();
oa.addListener(new AnimatorListenerNullImpl() {
@Override
public void onAnimationEnd(Animator arg0) {
mArrow.setVisibility(View.VISIBLE);
mArrow.setRotation(0);
mProgress.setVisibility(View.GONE);
mLabel.setText(normalInfo);
}
});
}
/**
* 設置正在刷新,將尖頭圖片隱藏,等待框顯示出來,並設置label的信息
*/
public void setRefresh() {
ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingTop", getPaddingTop(), trigger);
oa.setInterpolator(new LinearInterpolator());
oa.setDuration(200);
oa.start();
oa.addListener(new AnimatorListenerNullImpl() {
@Override
public void onAnimationEnd(Animator arg0) {
mArrow.setVisibility(View.GONE);
mProgress.setVisibility(View.VISIBLE);
mLabel.setText(refreshInfo);
}
});
}
/**
* 這個方法供 ObjectAnimator.ofInt(this, "paddingTop", getPaddingTop(), trigger);使用
* @param f
*/
public void setPaddingTop(int f) {
setPadding(0, f, 0, 0);
((View)getParent()).setPadding(0, 0, 0,-f);
}
/**
* 下拉或上拉的時候,尖頭產生動畫
* @param isOpen
*/
public void setTriggerAnim(boolean isOpen) {
if(isOpen) {
if (mArrow.getRotation() == 0) {
mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(-180).start();
}
} else {
if (mArrow.getRotation() == -180) {
mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(0).start();
}
}
}
private void initView() {
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(HORIZONTAL);
layout.setGravity(Gravity.CENTER_VERTICAL);
addView(layout, LayoutParams.MATCH_PARENT, trigger);
RelativeLayout relLayout = new RelativeLayout(getContext());
LayoutParams relParams = new LayoutParams(80, 80);
relParams.leftMargin = 100;
layout.addView(relLayout, relParams);
//尖頭圖片
mArrow = new ImageView(getContext());
RelativeLayout.LayoutParams arrowParams = new RelativeLayout.LayoutParams(30, 80);
arrowParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
relLayout.addView(mArrow, arrowParams);
//進度條
mProgress = new ProgressBar(getContext());
mProgress.setVisibility(View.GONE);
RelativeLayout.LayoutParams progressPrams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
progressPrams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
relLayout.addView(mProgress, progressPrams);
//文字說明
mLabel = new TextView(getContext());
mLabel.setTextColor(Color.BLACK);
mLabel.setText(normalInfo);
mLabel.setTextSize(20);
LinearLayout.LayoutParams namePrams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
namePrams.leftMargin = 50;
layout.addView(mLabel, namePrams);
}
}
xml佈局裏面
<com.zhangliucheng.pulllistview.PullListView
android:id="@+id/pullListView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Actvity裏面
mPullListView = (PullListView) findViewById(R.id.pullListView);
mPullListView.setAdapter(arrayAdapter);
mPullListView.setArrowImage(R.drawable.black_arrow);
mPullListView.setListViewDelegate(new ListViewPullDelegate() {
@Override
public void pullListViewTriggerRefresh(ListView listView) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
/** 這邊添加刷新 **/
mPullListView.refreshFinish();
}
}, 1000);
}
@Override
public void pullListViewTriggerLoadMore(ListView listView) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
arrayAdapter.notifyDataSetChanged();
}
});
//該方法和mPullListView.refreshFinish() 都會在主線程裏面執行,所以可以在任何線程調用這兩方法
mPullListView.loadMoreFinish();
}
}).start();
}
});
簡單的效果圖