关于ListView,在开发中经常会用到,但是一个原生的ListView并不能满足我们的开发需求,我们很多时候都会利用下拉刷新和上拉加载更多进行获取数据,这也算ListView的优化,这时候我们可以用第三方框架pullToRefreshListView,这是一个强大的第三方库,然而,今天我们要说的是自定义下拉刷新。
首先我们应该继承ListView,进行扩充,复写构造方法,进行初始化;

<span style="font-size:18px;">private void init() {
	upAnimation = new RotateAnimation(0, -180,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f);
	upAnimation.setDuration(200);
	upAnimation.setFillAfter(true);
	downAnimation = new RotateAnimation(-180, -360,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f);
	downAnimation.setDuration(200);
	downAnimation.setFillAfter(true);
	addHeadView();
}</span>



在初始化方法中定义了两个动画,这两个动画是在下拉刷新时,箭头的展示动画,同时在初始化中进行头部的添加;

<span style="font-size:18px;">private void addHeadView() {
	headView = View.inflate(getContext(), R.layout.layout_header, null);
	iv_arrow = (ImageView) headView.findViewById(R.id.iv_arrow);


	pb_rotate = (ProgressBar) headView.findViewById(R.id.pb_rotate);
	tv_state = (TextView) headView.findViewById(R.id.tv_state);
	tv_time = (TextView) headView.findViewById(R.id.tv_time);
	headView.measure(0, 0);
	headHeight = headView.getMeasuredHeight();
	headView.setPadding(0, -headHeight, 0, 0);
	addHeaderView(headView);
}</span>



整个ListView下拉刷新布局的显示和隐藏都是利用头部局的Padding来进行设置的,获取到头部的高,进行隐藏设置。

然后就是核心的功能OnTouchEvent()方法,


<span style="font-size:18px;">public boolean onTouchEvent(MotionEvent ev) {
	if (state == XIALA) {
		tv_state.setText(XIALA_STATE);
	}else if(state == SONGKAI){
		tv_state.setText(SONGKAI_STATE);
	}else if(state == ZHENGZAI){
		tv_state.setText(ZHENGZAI_STATE);
	}
	switch (ev.getAction()) {
	case MotionEvent.ACTION_DOWN:
		height = (int) ev.getY();
		break;
	case MotionEvent.ACTION_MOVE:
		if (state == ZHENGZAI) {
			break;
		}
		int y = (int) (ev.getY() - height);
		int offerY = y - headHeight;
		// 不要禁掉ListView的滑动事件
		if (offerY > -headHeight && getFirstVisiblePosition() == 0) {
			headView.setPadding(0, offerY, 0, 0);
			if (offerY > 0 && state == XIALA) {
				state = SONGKAI;
				setHeadView();
			} else if (offerY < 0 && state == SONGKAI) {
				state = XIALA;
				setHeadView();
			}
			return true;
		}
		break;
	case MotionEvent.ACTION_UP:
		if (state == XIALA) {
			headView.setPadding(0, -headHeight, 0, 0);
		} else if (state == SONGKAI) {
			state = ZHENGZAI;
			headView.setPadding(0, 0, 0, 0);
			setHeadView();


			if (onRefreshListener != null) {
				onRefreshListener.refresh();
			}


		}
		break;


	}
	return super.onTouchEvent(ev);
}</span>



在OnTouchEvent方法中通过ACTION_DOWN,ACTION_MOVE,ACTION_UP,三种状态进行设置,

来看ACTION_MOVE状态的处理:

显示判断是否在刷新状态,在的话直接终止,然后通过getY,得到滑动的时候的Y的坐标,和按下时得到的Y的坐标进行相减,得到移动的坐标,


<span style="font-size:18px;">if (offerY > -headHeight && getFirstVisiblePosition() == 0) {
			headView.setPadding(0, offerY, 0, 0);
			if (offerY > 0 && state == XIALA) {
				state = SONGKAI;
				setHeadView();
			} else if (offerY < 0 && state == SONGKAI) {
				state = XIALA;
				setHeadView();
			}
			return true;
		}</span>



当移动的长度大于负的头部的高度,同时ListView的第一个的Item是0的时候,才进行事件的拦截,这是为了防止禁掉ListView的滑动事件,当offerY>0的时候,这时候头部布局的状态是松开刷新状态,反之,下拉刷新状态。在setHeadView()方法中进行状态的设置,

来看ACTION_UP时候做了哪些事:


<span style="font-size:18px;">case MotionEvent.ACTION_UP:
		if (state == XIALA) {
			headView.setPadding(0, -headHeight, 0, 0);
		} else if (state == SONGKAI) {
			state = ZHENGZAI;
			headView.setPadding(0, 0, 0, 0);
			setHeadView();


			if (onRefreshListener != null) {
				onRefreshListener.refresh();
			}


		}
		break;

	}</span>



当状态是下拉刷新状态时,这时候松开应该隐藏头部布局,当时松开刷新状态时,显示头部布局,并通过回调进行数据的获取,

对于setHeadView()方法:


<span style="font-size:18px;">private void setHeadView() {
	switch (state) {
	case SONGKAI:
		tv_state.setText(SONGKAI_STATE);
		iv_arrow.startAnimation(upAnimation);
		break;
	case XIALA:
		tv_state.setText(XIALA_STATE);
			iv_arrow.startAnimation(downAnimation);
		break;


	case ZHENGZAI:
		tv_state.setText(ZHENGZAI_STATE);
		iv_arrow.clearAnimation();
		tv_time.setText("最后刷新:" + getDate());
		iv_arrow.setVisibility(View.INVISIBLE);
		pb_rotate.setVisibility(View.VISIBLE);
		break;


	default:
		break;
	}
}</span>



在这个方法中设置状态的显示和时间的显示,同时图标执行动画,其中getDate()方法是获取当前的时间进行最后刷新的显示。

当数据添加完后执行// 关闭刷新refreshCpmplete()方法进行头部布局的隐藏,状态的设置。



<span style="font-size:18px;">public void refreshCpmplete() {
	state = SONGKAI;
	headView.setPadding(0, -headHeight, 0, 0);
	pb_rotate.setVisibility(View.INVISIBLE);
	tv_state.setText(XIALA_STATE);
	iv_arrow.setVisibility(View.VISIBLE);
}</span>



使用下拉刷新时,只需要调用回调函数进行异步数据的获取


<span style="font-size:18px;">public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
	this.onRefreshListener = onRefreshListener;
}</span>



同时你可以通过调用setFirstDate()方法进行第一次时间的设置。通过setPullState()setUnlinkState()方法进行下拉,上拉刷新显示的是什么字,


源代码:


package com.example.androidtest;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.R.string;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Switch;
import android.widget.TextView;

public class MyRefreshListView extends ListView {
	// 设置三种状态
	private final int XIALA = 0;
	private final int SONGKAI = 1;
	private final int ZHENGZAI = 2;
	// 状态的标示
	private int state = XIALA;
	// 三种状态显示的字体
	private static String XIALA_STATE = "下拉刷新";
	private static String SONGKAI_STATE = "松开刷新";
	private static String ZHENGZAI_STATE = "正在刷新";

	// 头部的高度
	private int headHeight;
	private View headView;
	private ImageView iv_arrow;
	private ProgressBar pb_rotate;
	private TextView tv_state;
	private TextView tv_time;

	public MyRefreshListView(Context context, AttributeSet attrs,
			int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	public MyRefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public MyRefreshListView(Context context) {
		super(context);
		init();
	}

	private RotateAnimation upAnimation, downAnimation;

	private void init() {
		// 图标执行的动画的设置
		upAnimation = new RotateAnimation(0, -180,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		upAnimation.setDuration(200);
		upAnimation.setFillAfter(true);
		downAnimation = new RotateAnimation(-180, -360,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		downAnimation.setDuration(200);
		downAnimation.setFillAfter(true);
		addHeadView();
	}

	// 添加头部布局
	private void addHeadView() {
		headView = View.inflate(getContext(), R.layout.layout_header, null);
		iv_arrow = (ImageView) headView.findViewById(R.id.iv_arrow);

		pb_rotate = (ProgressBar) headView.findViewById(R.id.pb_rotate);
		tv_state = (TextView) headView.findViewById(R.id.tv_state);
		tv_time = (TextView) headView.findViewById(R.id.tv_time);
		headView.measure(0, 0);
		headHeight = headView.getMeasuredHeight();
		headView.setPadding(0, -headHeight, 0, 0);
		addHeaderView(headView);
	}

	// 手指down的时候记录下Y的坐标
	private int height;

	/**
	 * 设置下拉刷新时,显示的状态。
	 * 
	 * @param state
	 */
	public void setPullState(String state) {
		XIALA_STATE = state;
	}

	/**
	 * 设置松开刷新显示的状态。
	 * 
	 * @param state
	 */
	public void setUnlinkState(String state) {
		SONGKAI_STATE = state;
	}

	/**
	 * 设置正在刷新显示的状态
	 * 
	 * @param state
	 */
	public void setNowState(String state) {
		ZHENGZAI_STATE = state;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (state == XIALA) {
			tv_state.setText(XIALA_STATE);
		} else if (state == SONGKAI) {
			tv_state.setText(SONGKAI_STATE);
		} else if (state == ZHENGZAI) {
			tv_state.setText(ZHENGZAI_STATE);
		}
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			height = (int) ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			if (state == ZHENGZAI) {
				break;
			}
			int y = (int) (ev.getY() - height);
			int offerY = y - headHeight;
			// 不要禁掉ListView的滑动事件
			if (offerY > -headHeight && getFirstVisiblePosition() == 0) {
				headView.setPadding(0, offerY, 0, 0);
				if (offerY > 0 && state == XIALA) {
					state = SONGKAI;
					setHeadView();
				} else if (offerY < 0 && state == SONGKAI) {
					state = XIALA;
					setHeadView();
				}
				return true;
			}
			break;
		case MotionEvent.ACTION_UP:
			if (state == XIALA) {
				headView.setPadding(0, -headHeight, 0, 0);
			} else if (state == SONGKAI) {
				state = ZHENGZAI;
				headView.setPadding(0, 0, 0, 0);
				setHeadView();

				if (onRefreshListener != null) {
					onRefreshListener.refresh();
				}

			}
			break;

		}
		return super.onTouchEvent(ev);
	}

	private void setHeadView() {
		switch (state) {
		case SONGKAI:
			tv_state.setText(SONGKAI_STATE);
			iv_arrow.startAnimation(upAnimation);
			break;
		case XIALA:
			tv_state.setText(XIALA_STATE);
			iv_arrow.startAnimation(downAnimation);
			break;

		case ZHENGZAI:
			tv_state.setText(ZHENGZAI_STATE);
			iv_arrow.clearAnimation();
			tv_time.setText("最后刷新:" + getDate());
			iv_arrow.setVisibility(View.INVISIBLE);
			pb_rotate.setVisibility(View.VISIBLE);
			break;

		default:
			break;
		}
	}

	/**
	 * 设置第一次时间的显示
	 * @param name
	 */
	public void setFirstDate(String name) {
		tv_time.setText(name);
	}

	// 定义下拉刷新的回调
	private OnRefreshListener onRefreshListener;

	public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
		this.onRefreshListener = onRefreshListener;
	}

	public interface OnRefreshListener {
		public void refresh();
	}

	private String getDate() {
		SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
		return format.format(new Date());
	}

	// 关闭刷新
	public void refreshCpmplete() {
		state = SONGKAI;
		headView.setPadding(0, -headHeight, 0, 0);
		pb_rotate.setVisibility(View.INVISIBLE);
		tv_state.setText(XIALA_STATE);
		iv_arrow.setVisibility(View.VISIBLE);
	}

}