以前弄的,边从网上找边写,在网上找到后,改了下,有些注释可能不对,因为我英语很烂
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; //ListView的头部视图,用来下来刷新
private ImageView arrowImg; //下拉箭头
private TextView tipsTv,lastUpdateTV; //提示和最后一次更新的
private ProgressBar pb;
private Animation animation, reverseAnimation;
private final int RELEASE_TO_REFRESH = 1; //释放
private final int PULL_TO_REFRESH = 2; //下拉刷新
private final int REFRESHING = 3; //正在刷新
private final int DONE = 4 ; //完成
private final int LOADING = 5;
// 实际的padding的距离与界面上偏移距离的比例
private final int RATIO = 3;
// 用于保证startY的值在一个完整的touch事件中只被记录一次
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();
}
//下面这些个方法是ListView 的下拉刷新用的 注释是我写的,可能不太对,
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);
//计算head的高宽
measureView(headView);
headContentHeight = headView.getMeasuredHeight();
headContentWidth = headView.getMeasuredWidth();
//初始状态是隐藏headView的布局
headView.setPadding(0, -1*headContentHeight, 0, 0);
//v data isSelectable
lv.addHeaderView(headView, null, false);
//下拉动画 //从0度一直旋转到-180度位置,
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);
//恢复动画
reverseAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setInterpolator(new LinearInterpolator());
reverseAnimation.setDuration(200); //动画时间
reverseAnimation.setFillAfter(true);// 设置为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;
}
//估量、预测控件的高和宽
private void measureView(View child){
ViewGroup.LayoutParams p = child.getLayoutParams();
if(p == null){
//为空就创建一个
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){//有这个子类的高度,就创建一个精确的大小尺寸给它
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
}else{//没有就默认为0 //size mode
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
} //widthMeasureSpec heightMeasureSpec
child.measure(childWeightSpec, childHeightSpec);
}
//触屏事件 这是继承自ListView里的方法
@Override
public boolean onTouchEvent(MotionEvent event) {
// 用一个表示可刷新的状态来触发
if(isRefreshable){
switch (event.getAction()) {
// 在down时候记录当前Y的位置
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){
}
//由下拉刷新状态,到done状态
if(state == PULL_TO_REFRESH){
state = DONE;
changeHeaderViewByState();
}
//由松开刷新状态,到done状态
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){
// 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
// 可以松手去刷新了
if(state == RELEASE_TO_REFRESH){
setSelection(0);
// 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
if (((tempY - startY) / RATIO < headContentHeight)&& (tempY - startY) > 0) {
state = PULL_TO_REFRESH;
changeHeaderViewByState();
Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");
}
// 一下子推到顶了
else if((tempY - startY) <= 0){
state = DONE;
changeHeaderViewByState();
Log.v(TAG, "由松开刷新状态转变到done状态");
}// 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
else {
// 不用进行特别的操作,只用更新paddingTop的值就行了
}
}
// 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
if (state == PULL_TO_REFRESH) {
setSelection(0);
// 下拉到可以进入RELEASE_TO_REFRESH的状态
if ((tempY - startY) / RATIO >= headContentHeight) {
state = PULL_TO_REFRESH;
isBack = true;
changeHeaderViewByState();
Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");
}
// 上推到顶了
else if (tempY - startY <= 0) {
state = DONE;
changeHeaderViewByState();
Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");
}
}
// done状态下
if (state == DONE) {
if (tempY - startY > 0) {
state = PULL_TO_REFRESH;
changeHeaderViewByState();
}
}
// 更新headView的size
if (state == PULL_TO_REFRESH) {
headView.setPadding(0, -1 * headContentHeight
+ (tempY - startY) / RATIO, 0, 0);
}
// 更新headView的paddingTop
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) {
//松开刷新状态
case RELEASE_TO_REFRESH:
arrowImg.setVisibility(View.VISIBLE);
pb.setVisibility(View.GONE);
tipsTv.setVisibility(View.VISIBLE);
lastUpdateTV.setVisibility(View.VISIBLE);
tipsTv.setText("松开刷新");
arrowImg.clearAnimation();
arrowImg.startAnimation(animation);
Log.v(TAG, "当前状态,松开刷新");
break;
//下拉刷新状态
case PULL_TO_REFRESH:
arrowImg.setVisibility(View.VISIBLE);
pb.setVisibility(View.GONE);
// 是由RELEASE_To_REFRESH状态转变来的
//箭头反转向上
if(isBack){
isBack = false;
arrowImg.clearAnimation();
arrowImg.startAnimation(reverseAnimation);
tipsTv.setText("下拉刷新");
Log.v(TAG, "当前状态,isBack true 下拉刷新");
}else{
tipsTv.setText("下拉刷新");
Log.v(TAG, "当前状态,isBack false 下拉刷新");
}
break;
//正在刷新
case REFRESHING:
headView.setPadding(0, 0, 0, 0);
arrowImg.setVisibility(View.GONE);
arrowImg.clearAnimation();
pb.setVisibility(View.VISIBLE);
tipsTv.setText("正在刷新....");
lastUpdateTV.setVisibility(View.VISIBLE);
Log.v(TAG, "当前状态,正在刷新...");
break;
//刷新完成
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("下拉刷新");
lastUpdateTV.setVisibility(View.VISIBLE);
Log.v(TAG, "当前状态,done");
break;
default:
break;
}
}
/*//public boolean onTouch(View v, MotionEvent event) {
// 用一个表示可刷新的状态来触发
if (isRefreshable) {
switch (event.getAction()) {
// 在down时候记录当前Y的位置
case MotionEvent.ACTION_DOWN:
if (firstItemIndex == 0 && !isRecored) { // 在第一个位置被按下,并且在没记录完Y值时
startY = (int) event.getY();
isRecored = true;
}
break;
//松开的时候
case MotionEvent.ACTION_UP:
if (state != REFRESHING && state != LOADING) {
if (state == DONE) {
}
// 由下拉刷新状态,到done状态
if (state == PULL_TO_REFRESH) {
state = DONE;
changeHeaderViewByState();
}
// 由松开刷新状态,到done状态
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){
// 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
// 可以松手去刷新了
}
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("最近更新 "+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();
}
}
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; //ListView的头部视图,用来下来刷新
private ImageView arrowImg; //下拉箭头
private TextView tipsTv,lastUpdateTV; //提示和最后一次更新的
private ProgressBar pb;
private Animation animation, reverseAnimation;
private final int RELEASE_TO_REFRESH = 1; //释放
private final int PULL_TO_REFRESH = 2; //下拉刷新
private final int REFRESHING = 3; //正在刷新
private final int DONE = 4 ; //完成
private final int LOADING = 5;
// 实际的padding的距离与界面上偏移距离的比例
private final int RATIO = 3;
// 用于保证startY的值在一个完整的touch事件中只被记录一次
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();
}
//下面这些个方法是ListView 的下拉刷新用的 注释是我写的,可能不太对,
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);
//计算head的高宽
measureView(headView);
headContentHeight = headView.getMeasuredHeight();
headContentWidth = headView.getMeasuredWidth();
//初始状态是隐藏headView的布局
headView.setPadding(0, -1*headContentHeight, 0, 0);
//v data isSelectable
lv.addHeaderView(headView, null, false);
//下拉动画 //从0度一直旋转到-180度位置,
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);
//恢复动画
reverseAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setInterpolator(new LinearInterpolator());
reverseAnimation.setDuration(200); //动画时间
reverseAnimation.setFillAfter(true);// 设置为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;
}
//估量、预测控件的高和宽
private void measureView(View child){
ViewGroup.LayoutParams p = child.getLayoutParams();
if(p == null){
//为空就创建一个
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){//有这个子类的高度,就创建一个精确的大小尺寸给它
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
}else{//没有就默认为0 //size mode
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
} //widthMeasureSpec heightMeasureSpec
child.measure(childWeightSpec, childHeightSpec);
}
//触屏事件 这是继承自ListView里的方法
@Override
public boolean onTouchEvent(MotionEvent event) {
// 用一个表示可刷新的状态来触发
if(isRefreshable){
switch (event.getAction()) {
// 在down时候记录当前Y的位置
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){
}
//由下拉刷新状态,到done状态
if(state == PULL_TO_REFRESH){
state = DONE;
changeHeaderViewByState();
}
//由松开刷新状态,到done状态
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){
// 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
// 可以松手去刷新了
if(state == RELEASE_TO_REFRESH){
setSelection(0);
// 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
if (((tempY - startY) / RATIO < headContentHeight)&& (tempY - startY) > 0) {
state = PULL_TO_REFRESH;
changeHeaderViewByState();
Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");
}
// 一下子推到顶了
else if((tempY - startY) <= 0){
state = DONE;
changeHeaderViewByState();
Log.v(TAG, "由松开刷新状态转变到done状态");
}// 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
else {
// 不用进行特别的操作,只用更新paddingTop的值就行了
}
}
// 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
if (state == PULL_TO_REFRESH) {
setSelection(0);
// 下拉到可以进入RELEASE_TO_REFRESH的状态
if ((tempY - startY) / RATIO >= headContentHeight) {
state = PULL_TO_REFRESH;
isBack = true;
changeHeaderViewByState();
Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");
}
// 上推到顶了
else if (tempY - startY <= 0) {
state = DONE;
changeHeaderViewByState();
Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");
}
}
// done状态下
if (state == DONE) {
if (tempY - startY > 0) {
state = PULL_TO_REFRESH;
changeHeaderViewByState();
}
}
// 更新headView的size
if (state == PULL_TO_REFRESH) {
headView.setPadding(0, -1 * headContentHeight
+ (tempY - startY) / RATIO, 0, 0);
}
// 更新headView的paddingTop
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) {
//松开刷新状态
case RELEASE_TO_REFRESH:
arrowImg.setVisibility(View.VISIBLE);
pb.setVisibility(View.GONE);
tipsTv.setVisibility(View.VISIBLE);
lastUpdateTV.setVisibility(View.VISIBLE);
tipsTv.setText("松开刷新");
arrowImg.clearAnimation();
arrowImg.startAnimation(animation);
Log.v(TAG, "当前状态,松开刷新");
break;
//下拉刷新状态
case PULL_TO_REFRESH:
arrowImg.setVisibility(View.VISIBLE);
pb.setVisibility(View.GONE);
// 是由RELEASE_To_REFRESH状态转变来的
//箭头反转向上
if(isBack){
isBack = false;
arrowImg.clearAnimation();
arrowImg.startAnimation(reverseAnimation);
tipsTv.setText("下拉刷新");
Log.v(TAG, "当前状态,isBack true 下拉刷新");
}else{
tipsTv.setText("下拉刷新");
Log.v(TAG, "当前状态,isBack false 下拉刷新");
}
break;
//正在刷新
case REFRESHING:
headView.setPadding(0, 0, 0, 0);
arrowImg.setVisibility(View.GONE);
arrowImg.clearAnimation();
pb.setVisibility(View.VISIBLE);
tipsTv.setText("正在刷新....");
lastUpdateTV.setVisibility(View.VISIBLE);
Log.v(TAG, "当前状态,正在刷新...");
break;
//刷新完成
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("下拉刷新");
lastUpdateTV.setVisibility(View.VISIBLE);
Log.v(TAG, "当前状态,done");
break;
default:
break;
}
}
/*//public boolean onTouch(View v, MotionEvent event) {
// 用一个表示可刷新的状态来触发
if (isRefreshable) {
switch (event.getAction()) {
// 在down时候记录当前Y的位置
case MotionEvent.ACTION_DOWN:
if (firstItemIndex == 0 && !isRecored) { // 在第一个位置被按下,并且在没记录完Y值时
startY = (int) event.getY();
isRecored = true;
}
break;
//松开的时候
case MotionEvent.ACTION_UP:
if (state != REFRESHING && state != LOADING) {
if (state == DONE) {
}
// 由下拉刷新状态,到done状态
if (state == PULL_TO_REFRESH) {
state = DONE;
changeHeaderViewByState();
}
// 由松开刷新状态,到done状态
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){
// 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
// 可以松手去刷新了
}
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("最近更新 "+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();
}
}