以前弄的,邊從網上找邊寫,在網上找到後,改了下,有些注釋可能不對,因為我英語很爛
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();
}
}