关于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);
}
}