public class PullToLoadListView extends ListView implements OnScrollListener {
private static final String TAG = ();
private static final int STATE_NON = 0;
private static final int STATE_PULL_TO_REFRESH = 1;
private static final int STATE_RELEASE_TO_REFRESH = 2;
private static final int STATE_REFRESHING = 3;
private int state;
private int firstVisibleItem;
private int lastVisisibleItem;
private float prevY = 0;
private View headerView;
private View footerView;
// header widgets
private ProgressBar headerProgressBar;
private ImageView headerImageArrow;
private TextView headerText;
private RotateAnimation headerArrowAnim;
private RotateAnimation headerArrowReverseAnim;
// footer widgets
private ProgressBar footerProgressBar;
private TextView footerText;
private boolean headerIsHanding = false;
private boolean footerIsHanding = false;
private int headerHeight;
private int footerHeight;
private ResetAnimation resetAnim;
private OnLoadingListener onLoadingListener;
private OnScrollListener onScrollListener;
public PullToLoadListView(Context context) {
super(context);
init(context);
}
public PullToLoadListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
state = STATE_NON;
firstVisibleItem = 0;
lastVisisibleItem = 0;
LayoutInflater inflater = (context);
headerView = (.view_pull_header, null);
footerView = (.view_pull_footer, null);
headerProgressBar = (ProgressBar) ();
headerImageArrow = (ImageView) ();
headerText = (TextView) ();
headerArrowAnim = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
(300);
(true);
headerArrowReverseAnim = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
(300);
(true);
footerProgressBar = (ProgressBar) ();
footerText = (TextView) ();
measureView(headerView);
measureView(footerView);
headerHeight = ();
footerHeight = ();
(0, -1 * (), 0, 0);
(0, -1 * (), 0, 0);
();
();
addHeaderView(headerView, null, false);
addFooterView(footerView, null, false);
(this);
}
private void measureView(View view) {
lp = ();
if(lp == null) {
lp = new (.MATCH_PARENT, .WRAP_CONTENT);
}
int childWidthSpec = (0, 0, );
int childHeightSpec;
if( > 0) {
childHeightSpec = (0, );
} else {
childHeightSpec = (0, );
}
(childWidthSpec, childHeightSpec);
}
private void resetHeader() {
// (0, -1 * headerHeight, 0, 0);
resetAnim = new ResetAnimation(headerView, headerHeight, ());
();
}
private void resetFooter() {
resetAnim = new ResetAnimation(footerView, footerHeight, ());
();
}
private void changeHeaderViewByState(int state) {
if( == state) {
return ;
}
int prevState = ;
= state;
switch(state) {
case STATE_NON:
();
();
();
("Pull Down To Refresh");
break;
case STATE_PULL_TO_REFRESH:
();
();
("Pull Down To Refresh");
if(prevState == STATE_RELEASE_TO_REFRESH) {
(headerArrowReverseAnim);
} else {
();
}
break;
case STATE_RELEASE_TO_REFRESH:
();
();
(headerArrowAnim);
("Release To Refresh");
break;
case STATE_REFRESHING:
();
();
();
("Refreshing");
break;
default:
break;
}
}
private void changeFooterViewByState(int state) {
if( == state) {
return ;
}
= state;
switch(state) {
case STATE_NON:
();
("Pull Up To Refresh");
break;
case STATE_PULL_TO_REFRESH:
();
("Pull Up To Refresh");
break;
case STATE_RELEASE_TO_REFRESH:
();
("Release To Refresh");
break;
case STATE_REFRESHING:
();
("Refreshing");
break;
default:
break;
}
}
@Override
public void setOnScrollListener(OnScrollListener l) {
= l;
}
public void setOnLoadingListener(OnLoadingListener onLoadingListener) {
= onLoadingListener;
}
public void loadCompleted() {
if(headerIsHanding) {
changeHeaderViewByState(STATE_NON);
resetHeader();
headerIsHanding = false;
}
if(footerIsHanding) {
changeFooterViewByState(STATE_NON);
resetFooter();
footerIsHanding = false;
}
}
private void handleMoveHeaderEvent(MotionEvent ev) {
headerIsHanding = true;
float tempY = ();
float vector = tempY - prevY;
vector /= 2;
prevY = tempY;
if(vector > 0) {
int newPadding = (int) (() + vector);
newPadding = (newPadding, headerHeight / 2);
(0, newPadding, 0, 0);
if(state != STATE_REFRESHING) {
if(newPadding > 0) {
changeHeaderViewByState(STATE_RELEASE_TO_REFRESH);
} else {
changeHeaderViewByState(STATE_PULL_TO_REFRESH);
}
}
} else {
if(state == STATE_RELEASE_TO_REFRESH || state == STATE_PULL_TO_REFRESH) {
int newPadding = (int) (() + vector);
newPadding = (newPadding, -1 * headerHeight);
(0, newPadding, 0, 0);
if(newPadding <= -1 * headerHeight) {
changeHeaderViewByState(STATE_NON);
headerIsHanding = false;
} else if(newPadding <= 0) {
changeHeaderViewByState(STATE_PULL_TO_REFRESH);
} else {
}
}
}
}
private void handleMoveFooterEvent(MotionEvent ev) {
footerIsHanding = true;
float tempY = ();
float vector = tempY - prevY;
vector /= 2;
prevY = tempY;
if(vector < 0) {
int newPadding = (int) (() - vector);
if(newPadding > 0) {
newPadding = 0;
}
(0, newPadding, 0, 0);
if(state != STATE_REFRESHING) {
if(newPadding < 0) {
changeFooterViewByState(STATE_PULL_TO_REFRESH);
} else {
changeFooterViewByState(STATE_RELEASE_TO_REFRESH);
}
}
} else {
int newPadding = (int) (() - vector);
newPadding = (newPadding, footerHeight);
(0, newPadding, 0, 0);
if(newPadding <= -1 * footerHeight) {
changeFooterViewByState(STATE_NON);
footerIsHanding = false;
} else if(newPadding < 0) {
changeFooterViewByState(STATE_PULL_TO_REFRESH);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch(()) {
case MotionEvent.ACTION_DOWN:
prevY = ();
break;
case MotionEvent.ACTION_UP:
if(state == STATE_RELEASE_TO_REFRESH) {
if(headerIsHanding) {
changeHeaderViewByState(STATE_REFRESHING);
if(onLoadingListener != null) {
();
}
}
if(footerIsHanding) {
changeFooterViewByState(STATE_REFRESHING);
if(onLoadingListener != null) {
();
}
}
} else if(state == STATE_PULL_TO_REFRESH) {
if(headerIsHanding) {
changeHeaderViewByState(STATE_NON);
resetHeader();
headerIsHanding = false;
}
if(footerIsHanding) {
changeFooterViewByState(STATE_NON);
resetFooter();
footerIsHanding = false;
}
} else if(state == STATE_NON) {
headerIsHanding = false;
footerIsHanding = false;
} else {
// state == STATE_REFRESHING
// ignore
}
break;
case MotionEvent.ACTION_MOVE:
if(resetAnim == null || !) {
if(state != STATE_REFRESHING) {
Adapter adapter = getAdapter();
if(adapter == null) {
handleMoveHeaderEvent(ev);
} else {
final int count = ();
if(count <= 0) {
handleMoveHeaderEvent(ev);
} else {
float tempY = ();
float vector = tempY - prevY;
if(firstVisibleItem == 0 && lastVisisibleItem == count - 1) {
if(headerIsHanding) {
handleMoveHeaderEvent(ev);
} else if(footerIsHanding) {
handleMoveFooterEvent(ev);
} else {
if(vector > 0) {
handleMoveHeaderEvent(ev);
} else if(vector < 0) {
handleMoveFooterEvent(ev);
} else {
// ignore vector == 0
}
}
} else if(firstVisibleItem == 0 && vector > 0) {
handleMoveHeaderEvent(ev);
} else if(lastVisisibleItem == count - 1 && vector < 0) {
handleMoveFooterEvent(ev);
} else {
// ignore
}
}
}
}
}
break;
default:
break;
}
return (ev);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(onScrollListener != null) {
(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
= firstVisibleItem;
= firstVisibleItem + visibleItemCount - 1;
if(onScrollListener != null) {
(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
static class ResetAnimation extends Thread {
static final int DURATION = 600;
static final int INTERVAL = 5;
View view;
int orignalHeight;
int paddingTop;
boolean run = false;
ResetAnimation(View view, int orignalHeight, int paddingTop) {
= view;
= orignalHeight;
= paddingTop;
}
public void run() {
run = true;
int total = orignalHeight * 2 + paddingTop;
int timeTotal = DURATION / INTERVAL;
int piece = total / timeTotal;
int time = 0;
final View view = ;
final int paddingTop = ;
do {
final int nextPaddingTop = paddingTop - time * piece;
(new Runnable() {
public void run() {
(0, nextPaddingTop, 0, 0);
();
}
});
try {
sleep(INTERVAL);
} catch (InterruptedException e) {
();
}
time ++;
} while(time < timeTotal);
run = false;
}
}
public interface OnLoadingListener {
public void onLoadNew();
public void onLoadMore();
}
}