I used to find and write it on the Internet. After I found it on the Internet, I changed some comments. Some comments may not be correct, because my English is very bad.
Import java. text. SimpleDateFormat;
Import java. util. Date;
Import android. content. Context;
Import android. util. AttributeSet;
Import android. util. Log;
Import android. view. LayoutInflater;
Import android. view. MotionEvent;
Import android. view. View;
Import android. view. View. OnTouchListener;
Import android. view. ViewGroup;
Import android. view. View. MeasureSpec;
Import android. view. animation. Animation;
Import android. view. animation. LinearInterpolator;
Import android. view. animation. RotateAnimation;
Import android. widget. AbsListView;
Import android. widget. ImageView;
Import android. widget. LinearLayout;
Import android. widget. ListView;
Import android. widget. ProgressBar;
Import android. widget. TextView;
Import android. widget. AbsListView. OnScrollListener;
Public class PullToRefreshListView extends ListView implements OnScrollListener {
Private String TAG = "tag ";
Private Context con;
Private LayoutInflater mInflater;
Private ListView lv;
Private LinearLayout headView; // the header view of ListView, which is used to refresh
Private ImageView arrowImg; // drop-down arrow
Private TextView tipsTv, lastUpdateTV; // The prompt and last updated
Private ProgressBar pb;
Private Animation animation, reverseAnimation;
Private final int RELEASE_TO_REFRESH = 1; // release
Private Final int pull_to_refresh = 2; // pull-down refresh
Private Final int refreshing = 3; // refreshing
Private Final int done = 4; // complete
Private Final int loading = 5;
// Percentage of the actual padding distance to the offset on the Interface
Private Final int ratio = 3;
// Ensure that the value of starty is recorded only once in a complete touch event
Private Boolean isrecored;
Private int headcontentwidth;
Private int headcontentheight;
Private int starty;
Private int firstitemindex;
Private int state = 1;
Private Boolean isback;
Private onrefreshlistener refreshlistener;
Private Boolean isrefreshable;
Public pulltorefreshlistview (context con, listview LV ){
Super (CON );
This. Con = con;
This. lv = lv;
Init ();
}
Public pulltorefreshlistview (context con, attributeset ATTR, listview LV ){
Super (con, ATTR );
This. Con = con;
This. lv = lv;
Init ();
}
// The following methods are the comments I wrote for the pull-down refresh of ListView, which may not be correct,
Private void init (){
MInflater = LayoutInflater. from (con );
HeadView = (LinearLayout) mInflater. inflate (R. layout. list_head, null );
ArrowImg = (ImageView) headView. findViewById (R. id. list_head_arrowImg );
TipsTv = (TextView) headView. findViewById (R. id. list_head_tipsText );
LastUpdateTV = (TextView) headView. findViewById (R. id. list_head_lastUpdateText );
ArrowImg. setMinimumHeight (50 );
ArrowImg. setMinimumWidth (70 );
// Calculate the height and width of the head
MeasureView (headView );
HeadContentHeight = headView. getMeasuredHeight ();
HeadContentWidth = headView. getMeasuredWidth ();
// The initial state is to hide the headView layout.
HeadView. setPadding (0,-1 * headContentHeight, 0, 0 );
// V data isSelectable
Lv. addHeaderView (headView, null, false );
// Drop-down animation // rotate from 0 degrees to-180 degrees,
Animation = new RotateAnimation (0,-180, RotateAnimation. RELATIVE_TO_SELF, 0.5f, RotateAnimation. RELATIVE_TO_SELF, 0.5f );
Animation. setInterpolator (new LinearInterpolator ());
Animation. setDuration (250 );
Animation. setFillAfter (true );
// Restore the animation
ReverseAnimation = new RotateAnimation (0,-180, RotateAnimation. RELATIVE_TO_SELF, 0.5f, RotateAnimation. RELATIVE_TO_SELF, 0.5f );
Reverseanimation. setinterpolator (New linearinterpolator ());
Reverseanimation. setduration (200); // animation time
Reverseanimation. setfillafter (true); // if it is set to true
State = done;
Isrefreshable = false;
}
Public void onscrollstatechanged (abslistview view, int scrollstate ){
}
Public void onscroll (abslistview view, int firstvisibleitem,
Int visibleitemcount, int totalitemcount ){
Firstitemindex = firstvisibleitem;
}
// Estimate the height and width of the prediction control
Private void measureview (view child ){
Viewgroup. layoutparams P = Child. getlayoutparams ();
If (P = NULL ){
// Create
P = new viewgroup. layoutparams (viewgroup. layoutparams. fill_parent, viewgroup. layoutparams. wrap_content );
} // Spec padding childdimension
Int childweightspec = viewgroup. getchildmeasurespec (0, 0 + 0, P. width );
Int lpheight = P. height;
Int childheightspec;
If (lpheight> 0) {// with the height of this subclass, create an exact size for it
Childheightspec = measurespec. makemeasurespec (lpheight, measurespec. Exactly );
} Else {// No. The default value is 0 // size mode.
Childheightspec = measurespec. makemeasurespec (0, measurespec. Exactly );
} // Widthmeasurespec heightmeasurespec
Child. Measure (childweightspec, childheightspec );
}
// Touch-screen events. This is a method inherited from listview.
@ Override
Public Boolean ontouchevent (motionevent event ){
// Trigger with a refresh status
If (isrefreshable ){
Switch (event. getaction ()){
// Record the current position of Y at the time of down
Case motionevent. action_down:
If (firstitemindex = 0 &&! Isrecored ){
Starty = (INT) event. Gety ();
Isrecored = true;
}
Break;
Case MotionEvent. ACTION_UP:
If (state! = REFRESHING & state! = LOADING ){
If (state = DONE ){
}
// Refresh the status from the drop-down list to the done status
If (state = PULL_TO_REFRESH ){
State = DONE;
ChangeHeaderViewByState ();
}
// From release to refresh status to done status
If (state = RELEASE_TO_REFRESH ){
State = REFRESHING;
ChangeHeaderViewByState ();
OnRefresh ();
}
}
IsRecored = false;
IsBack = false;
Break;
Case MotionEvent. ACTION_MOVE:
Int tempY = (int) event. getY ();
If (state! = REFRESHING & state! = LOADING & isRecored ){
// Ensure that the current position is always in the head during the padding setting process. Otherwise, if the list exceeds the screen, the list will be rolled at the same time when it is being pushed.
// You can just refresh it.
If (state = RELEASE_TO_REFRESH ){
SetSelection (0 );
// Pushed up to the extent that the screen is sufficient to cover up the head, but not to the full extent
If (tempY-startY)/RATIO State = PULL_TO_REFRESH;
ChangeHeaderViewByState ();
Log. v (TAG, "changed from release refresh status to pull-down refresh status ");
}
// Pushed to the top
Else if (Tempy-starty) <= 0 ){
State = done;
Changeheaderviewbystate ();
Log. V (TAG, "changed from release refresh status to done status ");
} // Pull down, or you haven't pushed it to the top of the screen to hide the head.
Else {
// You only need to update the value of paddingTop without special operations.
}
}
// The DONE or PULL_To_REFRESH status is not displayed when the refresh is released.
If (state = PULL_TO_REFRESH ){
SetSelection (0 );
// Drop-down to the RELEASE_TO_REFRESH status
If (tempY-startY)/RATIO> = headContentHeight ){
State = PULL_TO_REFRESH;
IsBack = true;
ChangeHeaderViewByState ();
Log. v (TAG, "changed from done or pull-down refresh status to release refresh ");
}
// Pushed to the top
Else if (tempY-startY <= 0 ){
State = DONE;
ChangeHeaderViewByState ();
Log. v (TAG, "changed from DOne or pull-down refresh status to done status ");
}
}
// In the done status
If (state = DONE ){
If (tempY-startY> 0 ){
State = PULL_TO_REFRESH;
ChangeHeaderViewByState ();
}
}
// Update the headView size.
If (state = PULL_TO_REFRESH ){
HeadView. setPadding (0,-1 * headContentHeight
+ (TempY-startY)/RATIO, 0, 0 );
}
// Update the paddingTop of the headView
If (state = PULL_TO_REFRESH ){
HeadView. setPadding (0, (tempY-startY)/RATIO
-HeadContentHeight, 0, 0 );
}
}
Break;
Default:
Break;
}
}
Return super. ontouchevent (event );
}
//*/
Private void changeheaderviewbystate (){
Switch (state ){
// Release the refresh status
Case release_to_refresh:
Arrowimg. setvisibility (view. Visible );
PB. setvisibility (view. Gone );
Tipstv. setvisibility (view. Visible );
Lastupdatetv. setvisibility (view. Visible );
Tipstv. settext ("Release refresh ");
ArrowImg. clearAnimation ();
ArrowImg. startAnimation (animation );
Log. v (TAG, "Current Status, release refresh ");
Break;
// Pull-down refresh status
Case PULL_TO_REFRESH:
ArrowImg. setVisibility (View. VISIBLE );
Pb. setVisibility (View. GONE );
// The status changes from RELEASE_To_REFRESH.
// Arrows are reversed up
If (isBack ){
IsBack = false;
ArrowImg. clearAnimation ();
ArrowImg. startAnimation (reverseAnimation );
TipsTv. setText ("pull-down refresh ");
Log. v (TAG, "Current Status, isBack true pull-down refresh ");
} Else {
TipsTv. setText ("pull-down refresh ");
Log. V (TAG, "Current Status, isback false pull-down refresh ");
}
Break;
// Refreshing
Case refreshing:
Headview. setpadding (0, 0, 0, 0 );
Arrowimg. setvisibility (view. Gone );
Arrowimg. clearanimation ();
PB. setvisibility (view. Visible );
TipsTv. setText ("refreshing ....");
LastUpdateTV. setVisibility (View. VISIBLE );
Log. v (TAG, "Current Status, refreshing ...");
Break;
// Refresh complete
Case DONE:
HeadView. setPadding (0,-1 * headContentHeight, 0, 0 );
ArrowImg. clearAnimation ();
ArrowImg. setImageResource (R. drawable. downicon );
// ArrowImg. setBackgroundDrawable (getResources (). getDrawable (R. drawable. downicon ));
Pb. setVisibility (View. GONE );
TipsTv. setText ("pull-down refresh ");
LastUpdateTV. setVisibility (View. VISIBLE );
Log. v (TAG, "Current Status, done ");
Break;
Default:
Break;
}
}
/* // Public boolean onTouch (View v, MotionEvent event ){
// Trigger with a refresh status
If (isRefreshable ){
Switch (event. getAction ()){
// Record the current position of Y at the time of down
Case MotionEvent. ACTION_DOWN:
If (firstItemIndex = 0 &&! IsRecored) {// is pressed at the first position, and the Y value is not recorded.
StartY = (int) event. getY ();
IsRecored = true;
}
Break;
// When releasing
Case MotionEvent. ACTION_UP:
If (state! = REFRESHING & state! = LOADING ){
If (state = DONE ){
}
// Refresh the status from the drop-down list to the done status
If (state = PULL_TO_REFRESH ){
State = DONE;
ChangeHeaderViewByState ();
}
// From release to refresh status to done status
If (state = RELEASE_TO_REFRESH ){
State = REFRESHING;
ChangeHeaderViewByState ();
OnRefresh ();
}
}
IsRecored = false;
IsBack = false;
Break;
Case MotionEvent. ACTION_MOVE:
Int tempY = (int) event. getY ();
If (! IsRecored & firstItemIndex = 0 ){
IsRecored = true;
StartY = tempY;
}
If (state! = REFRESHING & isRecored & state! = LOADING ){
// Ensure that the current position is always in the head during the padding setting process. Otherwise, if the list exceeds the screen, the list will be rolled at the same time when it is being pushed.
// You can just refresh it.
}
Break;
Default:
Break;
}
}
Return true;
}
//*/
Public void onRefreshComplete (){
State = DONE;
Simpledateformat simpdate = new simpledateformat ("YYYY mm dd hh: mm ");
String date = simpdate. Format (new date ());
Lastupdatetv. settext ("Latest Update" + date );
Changeheaderviewbystate ();
}
Public void onrefresh (){
If (refreshlistener! = NULL ){
Refreshlistener. onrefresh ();
}
}
Public void setonrefreshlistener (onrefreshlistener refreshlistener ){
This. refreshListener = refreshListener;
IsRefreshable = true;
}
Public interface OnRefreshListener {
Public void onRefresh ();
}
}