下拉刷新的在android程序中很常见,自己也耐着性子完成了对它的具体实现。
首先你得知道刷新控件也是一个ListView,你用自己的方式实现了一个自定义ListView,
这个ListView具有下拉刷新功能。创建自己的ListView:
public class RefreshListView extends ListView implements OnScrollListener{
public RefreshListView(Context context) {
super(context);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
}
包括了几个不同的构造方法。下拉时能显示“下拉刷新”是因为ListView能够设置一个头部View,initView(context)
初始化headerView。
private void initView(Context context){
//LayoutInflater作用是加载布局
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.header_layout, null);
measureView(header);
headerHeight = header.getMeasuredHeight();
Log.i("headerHeight", ""+headerHeight);
topPadding(-headerHeight);
this.addHeaderView(header);
this.setOnScrollListener(this);
Time t = new Time();
t.setToNow();
updateTime = t.hour*60+t.minute;
Log.i(TAG, "init--->"+updateTime);
}
把headerView放入布局中,总得告诉父布局你所占用的宽和高啊!调用measuerView()方法进行初始化。
private void measureView(View view){
//LayoutParams are used by views to tell their parents
//how they want to be laid out.
//LayoutParams被view用来告诉它们的父布局它们应该被怎样安排
ViewGroup.LayoutParams p = view.getLayoutParams();
if(p==null){
//两个参数:width,height
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
//getChildMeasureSpec:获取子View的widthMeasureSpec、heightMeasureSpec值,
//之所以widthMeasureSpec和heightMeasureSpec的获取方法不一样是因为listview中不限定高度
// 但是限定了宽度
int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int height;
int tempHeight = p.height;
if(tempHeight>0){
height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
}else{
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(width, height);
}
toPadding ()方法用于设定headerView的头边距,用于进行隐藏和显示以及拖动,初始化为隐藏。
private void topPadding(int topPadding){
//设置顶部提示的边距
//除了顶部用参数值topPadding外,其他三个用header默认的值
header.setPadding(header.getPaddingLeft(), topPadding,
header.getPaddingRight(), header.getPaddingBottom());
//使header无效,将来调用onDraw()重绘View
<span style="white-space:pre"> </span>header.invalidate();
}
实现OnScrollListener接口主要是为了监听手指是否在屏幕上面,还有就是当前界面上的的第一个
ListItem是否是的第一个item。(当在ListView顶部的时候才能下拉刷新)
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
this.scrollState = scrollState;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
this.firstVisibleItem = firstVisibleItem;
}
重载onTouchEvent 监听用户手势状态包括按下,滑动,以及放开状态。
@Override
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if(firstVisibleItem == 0){
isFlag = true;//ListView最顶端按下,标志值设为真
startY = (int)ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
Time t = new Time();
t.setToNow();
System.out.println(t.hour + "时" + t.minute + "分" + t.second + "秒");
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if(state == RELEASE){
state = REFRESH;
//加载数据
refreshViewByState();
iRefreshlistener.onRefresh();
}else if(state == PULL){
state = NONE;
isFlag = false;
refreshViewByState();
}
break;
}
return super.onTouchEvent(ev);
}
private void onMove(MotionEvent ev){
//如果不是最顶端按下,则直接返回
if(!isFlag){
return;
}
int currentY = (int)ev.getY();//当前的Y值
int space = currentY - startY;//用户向下拉的距离
int topPadding = space - headerHeight;//顶部提示View距顶部的距离值
switch (state) {
//正常状态
case NONE:
if(space>0){
state = PULL;//下拉的距离大于0,则将状态改为PULL(提示下拉更新)
refreshViewByState();//根据状态的不同更新View
}
break;
case PULL:
topPadding(topPadding);
if(space>headerHeight+30//下拉的距离大于header的高度加30且用户滚动屏幕,手指仍在屏幕上
&&scrollState == SCROLL_STATE_TOUCH_SCROLL ){
state = RELEASE;//将状态改为RELEASE(提示松开更新)
refreshViewByState();
}
break;
case RELEASE:
if(topPadding > headerHeight + 50){
topPadding = headerHeight + 50;
}
topPadding(topPadding);
if(space<headerHeight+30){//用户下拉的距离不够
state = PULL; //将状态改为PULL
refreshViewByState();
}else if(space<=0){ //用户下拉的距离为非正值
state = NONE; //将状态改为NONE
isFlag = false; //标志改为false
refreshViewByState();
}
break;
}
根据用户的滑动距离改变state的值,并调用refreshViewByState()方法根据state更新控件的显示方式。
private void refreshViewByState(){
//提示
TextView tips = (TextView)header.findViewById(R.id.tips);
//箭头
ImageView arrow = (ImageView)header.findViewById(R.id.arrow);
//进度条
ProgressBar progress = (ProgressBar)header.findViewById(R.id.progress);
//箭头的动画效果1,由0度转向180度,即箭头由朝下转为朝上
RotateAnimation animation1 = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF,0.5f,
RotateAnimation.RELATIVE_TO_SELF,0.5f);
animation1.setDuration(500);
animation1.setFillAfter(true);
//箭头的动画效果2,由180度转向0度,即箭头由朝上转为朝下
RotateAnimation animation2 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF,0.5f,
RotateAnimation.RELATIVE_TO_SELF,0.5f);
animation2.setDuration(500);
animation2.setFillAfter(true);
switch (state) {
case NONE: //正常状态
arrow.clearAnimation(); //清除箭头动画效果
topPadding(-headerHeight); //设置header距离顶部的距离
break;
case PULL: //下拉状态
arrow.setVisibility(View.VISIBLE); //箭头设为可见
progress.setVisibility(View.GONE); //进度条设为不可见
tips.setText("下拉可以刷新"); //提示文字设为"下拉可以刷新"
arrow.clearAnimation(); //清除之前的动画效果,并将其设置为动画效果2
arrow.setAnimation(animation2);
break;
case RELEASE: //释放状态
arrow.setVisibility(View.VISIBLE); //箭头设为可见
progress.setVisibility(View.GONE); //进度条设为不可见
tips.setText("松开可以刷新"); //提示文字设为"松开可以刷新"
arrow.clearAnimation(); //清除之前的动画效果,并将其设置为动画效果2
arrow.setAnimation(animation1);
break;
case REFRESH: //更新状态
topPadding(50); //距离顶部的距离设置为50
arrow.setVisibility(View.GONE); //箭头设为不可见
progress.setVisibility(View.VISIBLE); //进度条设为可见
tips.setText("正在刷新..."); //提示文字设为""正在刷新..."
arrow.clearAnimation(); //清除动画效果
break;
}
这就是下拉刷新实现的流程了。