在第二篇中主要讨论了将顶部布局加载到ListView中,重点分析了init,measureView和topping三个方法的实现;
这一篇主要是收尾部分,即判断状态,加载相应的函数并实现函数回调机制;
onTouchEvent:判断手势动作的方法:
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: ///当前是在下拉;
if (firstVisibleItem == 0 ) { ///根据 当前可见的第一个的编号判断是不是在顶部;
isRemark = true ; ///设置在顶部下拉的标志
startY = ( int ) ev.getY(); ///记录开始时手指的位置高度;
}
break ;
case MotionEvent.ACTION_MOVE:
onMove(ev); ///判断是否还在移动;
break ;
case MotionEvent.ACTION_UP: ///
if (state == RELESE) {
state = REFLASHING;
// 加载最新数据;
reflashViewByState();
iReflashListener.onReflash(); ///响应事件;
} else if (state == PULL) {
state = NONE;
isRemark = false ;
reflashViewByState();
}
break ;
}
return super .onTouchEvent(ev);
}
|
首先调用移动事件对象的getAction方法获得动作,并用case来和MotionEvent的静态常量做比较,判断具体的动作类型。 (MotionEvent类是继承自抽象类InputEvent的,官方描述类的作用是:用于报告运动(鼠标、笔、手指,轨迹球)事件。运动事件可能持有 绝对或相对运动和其他数据,根据设备的类型)
动作类型:
MotionEvent.ACTION_DOWN:下拉动作,判断 当前的第一个Item的标号是不是0,firstVisibleItem在onScroll函数中就被赋好值了,并且这是由系统自动调用的。不是0的话, 就表示还没有到顶部;如果是0的话表示到了顶部,就要设置一个标志(isRemark)用来通知后面的相关函数,可以对顶部的距离进行调整,从而将顶部布 局显示出来。还要设置一个startY,记录当前手指所在的Y坐标位置,从而在下一次记录位置时,计算手指移动的距离,据此来显示顶部要显示的部分。
MotionEvent.ACTION_MOVE: 直接调用onMove函数,如果isRemark是true的话,表明已经到了顶部,onMove就会做相关的处理;如果是false的话,表明还未到顶 部,onMove就会直接返回。具体的onMove函数会在下面部分讨论,现在只要了解到onMove的调用时机和所起到的作用就可以了。
MotionEvent.ACTION_UP:根据状态state来判断:
ReflashListView定义了几种状态:
final int NONE = 0 ; // 正常状态;
final int PULL = 1 ; // 提示下拉状态;
final int RELESE = 2 ; // 提示释放状态;
final int REFLASHING = 3 ; // 刷新状态;
|
如果state是正常状态的话,上拉对顶部布局是不起作用的;
如果state是下拉状态的话,顶部布局还没有完全展开,说明你现在是想放弃刷新的操作,那么此时就将state置为正常状态,将isRemark置为false,表明还未到顶部;并调用reflashViewByState方法来重新显示顶部布局;
如果state是提示释放状态,现在上拉表示要刷新,调用reflashViewByState方法来重新显示顶部布局,并调用接口的函数,用接口回调机制,在Activity中实现函数体部分;
onMove:判断移动的距离和绘制顶部布局
private void onMove(MotionEvent ev) {
if (!isRemark) { ///不是顶部下拉则直接返回;
return ;
}
int tempY = ( int ) ev.getY(); ///记录当前手指的高度;
int space = tempY - startY; ///计算出移动的距离;
int topPadding = space - headerHeight; ///和顶部的高度作比较;
switch (state) {
case NONE:
if (space > 0 ) { ///说明此时开始下拉,可以设置状态为下拉;
state = PULL;
reflashViewByState();
}
break ;
case PULL:
topPadding(topPadding);
if (space > headerHeight + 30 ///说明此时下拉距离超过高度30了,不能再拉了;
&& scrollState == SCROLL_STATE_TOUCH_SCROLL) {
state = RELESE;
reflashViewByState();
}
break ;
case RELESE: ///表示已经到头了,不能下拉了,松开就可以刷新;
topPadding(topPadding);
if (space < headerHeight + 30 ) { ///到头了,但手指向上移动了,但还是可以看见头部的
state = PULL;
reflashViewByState();
} else if (space <= 0 ) { ///到头了,手指移出顶部;
state = NONE;
isRemark = false ;
reflashViewByState();
}
break ;
}
}
|
首先isRemark是false表示还没有到达顶部布局,就直接返回;
之后记录当前手指的位置(tempY),与在onTouchEvent中记录的手指位置做减,得到手指移动的相对距离(space)。将距离与顶部布局的 高度做比较,得到一个新的tapping,作用是如果传入到第二篇中介绍的tapping函数中,就可以实时显示出顶部布局;
接着根据之前的状态和距离的大小,对布局的显示和新的状态做相应的调整:
如果state为NULL,并且spcae>0,就表示开始下移了,将state设置为PULL即下移状态,并调用reflashViewByState()重新加载布局;
如果state为PULL,调用topping,将顶部布局重新绘制。之后再判断space > headerHeight + 30 && scrollState == SCROLL_STATE_TOUCH_SCROLL:条件成立的话说明此时下拉距离超过高度30了并且手指此时是静止的,那么就将state状态置为 RELESE,表示释放可以刷新,并调用reflashViewByState()重新加载布局;
如果state为PULL,调用 topping,将顶部布局重新绘制。之后再判断space < headerHeight + 30,表示到头了,但手指向上移动了,但还是可以看见头部的,这时就要将state置为PULL,因为没有一下子移动到头,并调用 reflashViewByState;或者else space <= 0到头了,手指移出顶部,就要state = NONE,isRemark = false并调用reflashViewByState;
reflashViewByState:设置顶部布局的控件内容;
private void reflashViewByState() {
TextView tip = (TextView) header.findViewById(R.id.tip);
ImageView arrow = (ImageView) header.findViewById(R.id.arrow);
ProgressBar progress = (ProgressBar) header.findViewById(R.id.progress);
RotateAnimation anim = new RotateAnimation( 0 , 180 ,
RotateAnimation.RELATIVE_TO_SELF, 0 .5f,
RotateAnimation.RELATIVE_TO_SELF, 0 .5f);
anim.setDuration( 500 );
anim.setFillAfter( true );
RotateAnimation anim1 = new RotateAnimation( 180 , 0 ,
RotateAnimation.RELATIVE_TO_SELF, 0 .5f,
RotateAnimation.RELATIVE_TO_SELF, 0 .5f);
anim1.setDuration( 500 );
anim1.setFillAfter( true );
switch (state) {
case NONE:
arrow.clearAnimation();
topPadding(-headerHeight);
break ;
case PULL:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText( "下拉可以刷新!" );
arrow.clearAnimation();
arrow.setAnimation(anim1);
break ;
case RELESE:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText( "松开可以刷新!" );
arrow.clearAnimation();
arrow.setAnimation(anim);
break ;
case REFLASHING:
topPadding( 50 );
arrow.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
tip.setText( "正在刷新..." );
arrow.clearAnimation();
break ;
}
}
|
在switch之前的部分是获得顶部布局的控件,和设置动画(其实个人感觉可以在初始化的时候就设置,这样就不要每次都重新设置一遍);
之后根据状态来显示顶部布局:
如果state是NONE:隐藏顶部布局;
如果state是PULL:下拉;
如果state是RELESE:松开刷新;
如果state是REFLASHING:正在刷新;
reflashComplete:获取完数据;
public void reflashComplete() {
state = NONE;
isRemark = false ;
reflashViewByState();
TextView lastupdatetime = (TextView) header
.findViewById(R.id.lastupdate_time);
SimpleDateFormat format = new SimpleDateFormat( "yyyy年MM月dd日 hh:mm:ss" );
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
lastupdatetime.setText(time);
}
|
主要是状态和标志的设置,记录刷新的时间,以便下次刷新时查看;
最后是接口的回调机制:
public interface IReflashListener{
public void onReflash();
}
public void setInterface(IReflashListener iReflashListener){
this .iReflashListener = iReflashListener;
}
|
在Activity中,实现了接口IReflashListener,并重写了onReflash函数
public void onReflash() {
Handler handler = new Handler();
handler.postDelayed( new Runnable() {
public void run() {
//获取最新数据
setReflashData();
//通知界面显示
showList(apk_list);
//通知listview 刷新数据完毕;
listview.reflashComplete();
}
}, 2000 );
}
|
关于函数回调机制,因为篇幅有限,就不具体讨论,可以看看我的博客: 常用但忽略的anroid知识2-回调问题
就这样,我们把用ListView来实现下拉刷新给全部分析了一遍,说说我在学习过程中的收获:不在惧怕developer的api文档了,看见一个常用的类我会去了解他与其他类的继承关系。对接口回调机制也有了比较深刻的理解;真心希望这三篇会给你带来一些新的理解。
接下来的几天中,我会开始学习自定义VIewGroup,也希望自己可以有比较深刻的体会,和大家一起分享!
一直在路上...
时间: 2024-08-13 15:31:17