效果图:
源代码:
listview_header.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="20dp"
android:paddingBottom="20dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/arrow_down"
android:layout_marginRight="20dp"
android:layout_toLeftOf="@+id/header_refresh_text"
android:id="@+id/header_image" />
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/header_image"
android:visibility="gone"
android:id="@+id/header_progressbar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉可以刷新"
android:textSize="14sp"
android:layout_centerHorizontal="true"
android:id="@+id/header_refresh_text"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_below="@+id/header_refresh_text"
android:layout_alignLeft="@+id/header_refresh_text"
android:layout_marginTop="5dp"
android:id="@+id/header_time_text"/>
</RelativeLayout>
</LinearLayout>
DownRefreshListView.java
public class DownRefreshListView extends ListView implements AbsListView.OnScrollListener {
/**
* 刷新数据接口,用于调用Activity中的方法
* 在Activity中实现该接口
*/
public interface IRefreshListener{
void onRefresh();
}
/**
* 刷新数据的监听器
*/
private IRefreshListener iRefreshListener;
/**
* 提供设置接口的方法,以便Activity传入接口
* @param listener
*/
public void setRefreshListener(IRefreshListener listener){
this.iRefreshListener = listener;
}
/**
* 顶部布局状态
*/
private final int NONE = 0;
private final int PULL = 1;//下拉刷新
private final int RELEASE = 2;//松开刷新
private final int REFRESHING = 3;//正在刷新
/**
* 顶部布局
*/
private View header;
/**
* 布局在父布局中所占的高度
*/
private int headerHeight;
/**
* 图片
*/
private ImageView imageArrow;
/**
* 进度条
*/
private ProgressBar progressBar;
/**
* 刷新文本
*/
private TextView refreshTxt;
/**
* 上次刷新时间
*/
private TextView timeTxt;
/**
* 滚动状态
*/
private int scrollState = 0;
/**
* ListView是否滚动到最顶端
*/
private boolean bHeader = false;
/**
* 鼠标按下的位置
*/
private float startY;
/**
* 移动状态
*/
private int state = NONE;
public DownRefreshListView(Context context) {
super(context);
init(context);
}
public DownRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DownRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化ListView,添加顶部布局文件到ListView中
*/
private void init(Context context){
/**
* 获取顶部布局,添加到ListView中
*/
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.listview_header, null);
//通知父布局所占的空间
measureView(header);
//获取布局所占的高度
headerHeight = header.getMeasuredHeight();
Log.i("tag", headerHeight+"");
//设置布局的上边距为-height,以隐藏布局
setPadding(-headerHeight);
this.addHeaderView(header);
imageArrow = (ImageView)header.findViewById(R.id.header_image);
refreshTxt = (TextView)header.findViewById(R.id.header_refresh_text);
timeTxt = (TextView)header.findViewById(R.id.header_time_text);
progressBar = (ProgressBar)header.findViewById(R.id.header_progressbar);
this.setOnScrollListener(this);
}
/**
* 通知父布局所占的空间
* @param view
*/
private void measureView(View view){
/**
* 获取view的布局参数
*/
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params==null){
params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
/**
* 获取实际宽度
* spec : 左右边距 margin
* padding : 左右边距 padding
*/
int width = ViewGroup.getChildMeasureSpec(0,0,params.width);
/**
* 获取实际高度
*/
int height;
int tempHeight = params.height;
if (tempHeight > 0){
//填充布局
height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
}else{
//不填充
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(width, height);
}
/**
* 设置顶部布局的上边距,以控制顶部布局的显示与隐藏
* @param topPadding
*/
private void setPadding(int topPadding){
header.setPadding(header.getPaddingLeft(), topPadding,
header.getPaddingRight(), header.getPaddingBottom());
header.invalidate();
}
/**
* 记录滚动状态
* @param view
* @param scrollState
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollState;
}
/**
* 根据firstVisibleItem判断是否滚动到ListView最顶端
* @param view
* @param firstVisibleItem
* @param visibleItemCount
* @param totalItemCount
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
bHeader = (firstVisibleItem == 0);
}
/**
* 触摸ListView的事件
* @param ev
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
/**
* 只在顶端时处理
*/
if (!bHeader){
super.onTouchEvent(ev);
}
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
/**
* 记录按下的位置
*/
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
/**
* 计算移动的距离
*/
float dist = ev.getY()-startY;
/**
* 根据移动距离切换状态
*/
switch (state){
case NONE:
/**
* 当移动距离大于0时,切换到下拉刷新状态
*/
if (dist > 0){
state = PULL;
refreshView();
}
break;
case PULL:
/**
* 当移动距离大于一定高度时,切换为松开刷新状态
*/
if (dist > headerHeight+30 && scrollState == SCROLL_STATE_TOUCH_SCROLL){
state = RELEASE;
refreshView();
}
break;
case RELEASE:
if (dist < headerHeight+30){
state = PULL;
refreshView();
} else if (dist <= 0){
state = NONE;
refreshView();
}
break;
}
/**
* 更新顶部布局的高度
*/
setPadding((int)(dist-headerHeight));
break;
case MotionEvent.ACTION_UP:
if (state == RELEASE){
state = REFRESHING;
refreshView();
/**
* 调用Activity中的刷新方法
*/
if (iRefreshListener != null) {
iRefreshListener.onRefresh();
}
}else if (state == PULL){
state = NONE;
refreshView();
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 根据状态刷新界面
*/
private void refreshView(){
/**
* 箭头旋转的动画
**/
RotateAnimation animation = new RotateAnimation( //箭头朝上
0,//起始角度
180,//结束角度
RotateAnimation.RELATIVE_TO_SELF,//相对自身旋转
0.5f,//相对位置
RotateAnimation.RELATIVE_TO_SELF,
0.5f
);
animation.setDuration(500);//动画的时间
animation.setFillAfter(true);
RotateAnimation animation1 = new RotateAnimation( //箭头朝下
180,//起始角度
0,//结束角度
RotateAnimation.RELATIVE_TO_SELF,//相对自身旋转
0.5f,//相对位置
RotateAnimation.RELATIVE_TO_SELF,
0.5f
);
animation1.setDuration(500);
animation1.setFillAfter(true);
switch (state){
case NONE:
setPadding(-headerHeight);
imageArrow.clearAnimation();
break;
case PULL:
imageArrow.setVisibility(View.VISIBLE);
imageArrow.clearAnimation();
imageArrow.setAnimation(animation1);
progressBar.setVisibility(View.GONE);
refreshTxt.setText("下拉可以刷新");
break;
case RELEASE:
imageArrow.setVisibility(View.VISIBLE);
imageArrow.clearAnimation();
imageArrow.setAnimation(animation);
progressBar.setVisibility(View.GONE);
refreshTxt.setText("松开可以刷新");
break;
case REFRESHING:
setPadding(0);
imageArrow.setVisibility(View.GONE);
imageArrow.clearAnimation();
progressBar.setVisibility(View.VISIBLE);
refreshTxt.setText("正在刷新");
break;
}
}
/**
* 刷新数据完成的操作
*/
public void refreshCompleted(){
//重新获取布局所占的高度
headerHeight = header.getMeasuredHeight();
Log.i("tag", headerHeight+"");
state = NONE;
refreshView();
/**
* 获取系统时间,记录上次刷新时间
*/
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
timeTxt.setText("上次刷新时间:"+format.format(date));
}
}
测试:
listview_down_refresh.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<DownRefreshListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/down_refresh_listview">
</DownRefreshListView>
</LinearLayout>
ListViewDownRefreshActivity.java
public class ListViewDownRefreshActivity extends Activity {
private List<Map<String, Object>> data;
private SimpleAdapter adapter;
private DownRefreshListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview_down_refresh);
data = new ArrayList<Map<String,Object>>();
for (int i=0;i<10;i++){
Map<String,Object> map = new HashMap<String, Object>();
map.put("pic", R.drawable.toggle_button_off);
map.put("txt", "慕课网"+i);
map.put("info", "详细信息详细信息详细信息");
data.add(map);
}
adapter = new SimpleAdapter(this, data,
R.layout.listview_item,
new String[]{"pic","txt", "info"},
new int[]{R.id.item_image, R.id.item_text, R.id.item_info});
listView = (DownRefreshListView)findViewById(R.id.down_refresh_listview);
listView.setAdapter(adapter);
/**
* 实现刷新数据的接口
*/
listView.setRefreshListener(new DownRefreshListView.IRefreshListener() {
@Override
public void onRefresh() {
/**
* 添加延时,以便看清刷新效果
*/
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
/**
* 获取最新数据
*/
for (int i = 0; i < 2; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("pic", R.drawable.toggle_button_off);
map.put("txt", "慕课网" + i);
map.put("info", "刷新数据刷新数据刷新数据");
data.add(0, map);
}
adapter = new SimpleAdapter(ListViewDownRefreshActivity.this, data,
R.layout.listview_item,
new String[]{"pic", "txt", "info"},
new int[]{R.id.item_image, R.id.item_text, R.id.item_info});
/**
* 通知界面显示
*/
adapter.notifyDataSetChanged();
/**
* 通知ListView刷新数据完毕
*/
listView.refreshCompleted();
}
}, 2000);
}
});
}
}
参考资料:Android的ListView下拉刷新