ListView实现下拉刷新-3-将顶部布局动态的显示出来

本文详细解析了使用ListView实现下拉刷新的原理及关键代码,包括触摸事件处理、状态判断及UI更新等环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在第二篇中主要讨论了将顶部布局加载到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,也希望自己可以有比较深刻的体会,和大家一起分享!

 一直在路上...



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值