当前位置: 移动技术网 > IT编程>移动开发>Android > XListView实现下拉刷新和上拉加载原理解析

XListView实现下拉刷新和上拉加载原理解析

2019年07月24日  | 移动技术网IT编程  | 我要评论

信用卡刷卡套现,帝国全面战争国家全开,吉沢明歩中文字幕

xlistview是一个非常受欢迎的下拉刷新控件,但是已经停止维护了。之前写过一篇xlistview的使用介绍,用起来非常简单,这两天放假无聊,研究了下xlistview的实现原理,学到了很多,今天分享给大家。

    提前声明,为了让代码更好的理解,我对代码进行了部分删减和重构,如果大家想看原版代码,请去github自行下载。

    xlistview项目主要是三部分:xlistview,xlistviewheader,xlistviewfooter,分别是xlistview主体、header、footer的实现。下面我们分开来介绍。

    下面是修改之后的xlistviewheader代码

public class xlistviewheader extends linearlayout { 
 
  private static final string hint_normal = "下拉刷新"; 
  private static final string hint_ready = "松开刷新数据"; 
  private static final string hint_loading = "正在加载..."; 
 
  // 正常状态 
  public final static int state_normal = 0; 
  // 准备刷新状态,也就是箭头方向发生改变之后的状态 
  public final static int state_ready = 1; 
  // 刷新状态,箭头变成了progressbar 
  public final static int state_refreshing = 2; 
  // 布局容器,也就是根布局 
  private linearlayout container; 
  // 箭头图片 
  private imageview marrowimageview; 
  // 刷新状态显示 
  private progressbar mprogressbar; 
  // 说明文本 
  private textview mhinttextview; 
  // 记录当前的状态 
  private int mstate; 
  // 用于改变箭头的方向的动画 
  private animation mrotateupanim; 
  private animation mrotatedownanim; 
  // 动画持续时间 
  private final int rotate_anim_duration = 180; 
 
  public xlistviewheader(context context) { 
    super(context); 
    initview(context); 
  } 
 
  public xlistviewheader(context context, attributeset attrs) { 
    super(context, attrs); 
    initview(context); 
  } 
 
  private void initview(context context) { 
    mstate = state_normal; 
    // 初始情况下,设置下拉刷新view高度为0 
    linearlayout.layoutparams lp = new linearlayout.layoutparams( 
        layoutparams.match_parent, 0); 
    container = (linearlayout) layoutinflater.from(context).inflate( 
        r.layout.xlistview_header, null); 
    addview(container, lp); 
    // 初始化控件 
    marrowimageview = (imageview) findviewbyid(r.id.xlistview_header_arrow); 
    mhinttextview = (textview) findviewbyid(r.id.xlistview_header_hint_textview); 
    mprogressbar = (progressbar) findviewbyid(r.id.xlistview_header_progressbar); 
    // 初始化动画 
    mrotateupanim = new rotateanimation(0.0f, -180.0f, 
        animation.relative_to_self, 0.5f, animation.relative_to_self, 
        0.5f); 
    mrotateupanim.setduration(rotate_anim_duration); 
    mrotateupanim.setfillafter(true); 
    mrotatedownanim = new rotateanimation(-180.0f, 0.0f, 
        animation.relative_to_self, 0.5f, animation.relative_to_self, 
        0.5f); 
    mrotatedownanim.setduration(rotate_anim_duration); 
    mrotatedownanim.setfillafter(true); 
  } 
 
  // 设置header的状态 
  public void setstate(int state) { 
    if (state == mstate) 
      return; 
 
    // 显示进度 
    if (state == state_refreshing) { 
      marrowimageview.clearanimation(); 
      marrowimageview.setvisibility(view.invisible); 
      mprogressbar.setvisibility(view.visible); 
    } else { 
      // 显示箭头 
      marrowimageview.setvisibility(view.visible); 
      mprogressbar.setvisibility(view.invisible); 
    } 
 
    switch (state) { 
    case state_normal: 
      if (mstate == state_ready) { 
        marrowimageview.startanimation(mrotatedownanim); 
      } 
      if (mstate == state_refreshing) { 
        marrowimageview.clearanimation(); 
      } 
      mhinttextview.settext(hint_normal); 
      break; 
    case state_ready: 
      if (mstate != state_ready) { 
        marrowimageview.clearanimation(); 
        marrowimageview.startanimation(mrotateupanim); 
        mhinttextview.settext(hint_ready); 
      } 
      break; 
    case state_refreshing: 
      mhinttextview.settext(hint_loading); 
      break; 
    } 
 
    mstate = state; 
  } 
 
  public void setvisiableheight(int height) { 
    if (height < 0) 
      height = 0; 
    linearlayout.layoutparams lp = (linearlayout.layoutparams) container 
        .getlayoutparams(); 
    lp.height = height; 
    container.setlayoutparams(lp); 
  } 
 
  public int getvisiableheight() { 
    return container.getheight(); 
  } 
 
  public void show() { 
    container.setvisibility(view.visible); 
  } 
 
  public void hide() { 
    container.setvisibility(view.invisible); 
  } 
 
} 

    xlistviewheader继承自linearlayout,用来实现下拉刷新时的界面展示,可以分为三种状态:正常、准备刷新、正在加载。
    在linearlayout布局里面,主要有指示箭头、说明文本、圆形加载条三个控件。在构造函数中,调用了initview()进行控件的初始化操作。在添加布局文件的时候,指定高度为0,这是为了隐藏header,然后初始化动画,是为了完成箭头的旋转动作。
    setstate()是设置header的状态,因为header需要根据不同的状态,完成控件隐藏、显示、改变文字等操作,这个方法主要是在xlistview里面调用。除此之外,还有setvisiableheight()和getvisiableheight(),这两个方法是为了设置和获取header中根布局文件的高度属性,从而完成拉伸和收缩的效果,而show()和hide()则显然就是完成显示和隐藏的效果。
    下面是header的布局文件

<?xml version="1.0" encoding="utf-8"?> 
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:gravity="bottom" > 
 
  <relativelayout 
    android:id="@+id/xlistview_header_content" 
    android:layout_width="match_parent" 
    android:layout_height="60dp" 
    tools:ignore="uselessparent" > 
 
    <textview 
      android:id="@+id/xlistview_header_hint_textview" 
      android:layout_width="100dp" 
      android:layout_height="wrap_content" 
      android:layout_centerinparent="true" 
      android:gravity="center" 
      android:text="正在加载" 
      android:textcolor="@android:color/black" 
      android:textsize="14sp" /> 
 
    <imageview 
      android:id="@+id/xlistview_header_arrow" 
      android:layout_width="30dp" 
      android:layout_height="wrap_content" 
      android:layout_centervertical="true" 
      android:layout_toleftof="@id/xlistview_header_hint_textview" 
      android:src="@drawable/xlistview_arrow" /> 
 
    <progressbar 
      android:id="@+id/xlistview_header_progressbar" 
      style="@style/progressbar_style" 
      android:layout_width="30dp" 
      android:layout_height="30dp" 
      android:layout_centervertical="true" 
      android:layout_toleftof="@id/xlistview_header_hint_textview" 
      android:visibility="invisible" /> 
  </relativelayout> 
 
</linearlayout> 

    说完了header,我们再看看footer。footer是为了完成加载更多功能时候的界面展示,基本思路和header是一样的,下面是footer的代码

public class xlistviewfooter extends linearlayout { 
 
  // 正常状态 
  public final static int state_normal = 0; 
  // 准备状态 
  public final static int state_ready = 1; 
  // 加载状态 
  public final static int state_loading = 2; 
 
  private view mcontentview; 
  private view mprogressbar; 
  private textview mhintview; 
 
  public xlistviewfooter(context context) { 
    super(context); 
    initview(context); 
  } 
 
  public xlistviewfooter(context context, attributeset attrs) { 
    super(context, attrs); 
    initview(context); 
  } 
 
  private void initview(context context) { 
 
    linearlayout moreview = (linearlayout) layoutinflater.from(context) 
        .inflate(r.layout.xlistview_footer, null); 
    addview(moreview); 
    moreview.setlayoutparams(new linearlayout.layoutparams( 
        layoutparams.match_parent, layoutparams.wrap_content)); 
 
    mcontentview = moreview.findviewbyid(r.id.xlistview_footer_content); 
    mprogressbar = moreview.findviewbyid(r.id.xlistview_footer_progressbar); 
    mhintview = (textview) moreview 
        .findviewbyid(r.id.xlistview_footer_hint_textview); 
  } 
 
  /** 
   * 设置当前的状态 
   * 
   * @param state 
   */ 
  public void setstate(int state) { 
 
    mprogressbar.setvisibility(view.invisible); 
    mhintview.setvisibility(view.invisible); 
 
    switch (state) { 
    case state_ready: 
      mhintview.setvisibility(view.visible); 
      mhintview.settext(r.string.xlistview_footer_hint_ready); 
      break; 
 
    case state_normal: 
      mhintview.setvisibility(view.visible); 
      mhintview.settext(r.string.xlistview_footer_hint_normal); 
      break; 
 
    case state_loading: 
      mprogressbar.setvisibility(view.visible); 
      break; 
 
    } 
 
  } 
 
  public void setbottommargin(int height) { 
    if (height > 0) { 
 
      linearlayout.layoutparams lp = (linearlayout.layoutparams) mcontentview 
          .getlayoutparams(); 
      lp.bottommargin = height; 
      mcontentview.setlayoutparams(lp); 
    } 
  } 
 
  public int getbottommargin() { 
    linearlayout.layoutparams lp = (linearlayout.layoutparams) mcontentview 
        .getlayoutparams(); 
    return lp.bottommargin; 
  } 
 
  public void hide() { 
    linearlayout.layoutparams lp = (linearlayout.layoutparams) mcontentview 
        .getlayoutparams(); 
    lp.height = 0; 
    mcontentview.setlayoutparams(lp); 
  } 
 
  public void show() { 
    linearlayout.layoutparams lp = (linearlayout.layoutparams) mcontentview 
        .getlayoutparams(); 
    lp.height = layoutparams.wrap_content; 
    mcontentview.setlayoutparams(lp); 
  } 
 
} 

    从上面的代码里面,我们可以看出,footer和header的思路是一样的,只不过,footer的拉伸和显示效果不是通过高度来模拟的,而是通过设置bottommargin来完成的。
    下面是footer的布局文件 

<?xml version="1.0" encoding="utf-8"?> 
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" > 
 
  <relativelayout 
    android:id="@+id/xlistview_footer_content" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:padding="5dp" 
    tools:ignore="uselessparent" > 
 
    <progressbar 
      android:id="@+id/xlistview_footer_progressbar" 
      style="@style/progressbar_style" 
      android:layout_width="30dp" 
      android:layout_height="30dp" 
      android:layout_centerinparent="true" 
      android:visibility="invisible" /> 
 
    <textview 
      android:id="@+id/xlistview_footer_hint_textview" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_centerinparent="true" 
      android:text="@string/xlistview_footer_hint_normal" 
      android:textcolor="@android:color/black" 
      android:textsize="14sp" /> 
  </relativelayout> 
 
</linearlayout> 

    在了解了header和footer之后,我们就要介绍最核心的xlistview的代码实现了。
    在介绍代码实现之前,我先介绍一下xlistview的实现原理。
    首先,一旦使用xlistview,footer和header就已经添加到我们的listview上面了,xlistview就是通过继承listview,然后处理了屏幕点击事件和控制滑动实现效果的。所以,如果我们的adapter中getcount()返回的值是20,那么其实xlistview里面是有20+2个item的,这个数量即使我们关闭了xlistview的刷新和加载功能,也是不会变化的。header和footer通过addheaderview和addfooterview添加上去之后,如果想实现下拉刷新和上拉加载功能,那么就必须有拉伸效果,所以就像上面的那样,header是通过设置height,footer是通过设置bottommargin来模拟拉伸效果。那么回弹效果呢?仅仅通过设置高度或者是间隔是达不到模拟回弹效果的,因此,就需要用scroller来实现模拟回弹效果。在说明原理之后,我们开始介绍xlistview的核心实现原理。
    再次提示,下面的代码经过我重构了,只是为了看起来更好的理解。

public class xlistview extends listview { 
 
  private final static int scrollback_header = 0; 
  private final static int scrollback_footer = 1; 
  // 滑动时长 
  private final static int scroll_duration = 400; 
  // 加载更多的距离 
  private final static int pull_load_more_delta = 100; 
  // 滑动比例 
  private final static float offset_radio = 2f; 
  // 记录按下点的y坐标 
  private float lasty; 
  // 用来回滚 
  private scroller scroller; 
  private ixlistviewlistener mlistviewlistener; 
  private xlistviewheader headerview; 
  private relativelayout headerviewcontent; 
  // header的高度 
  private int headerheight; 
  // 是否能够刷新 
  private boolean enablerefresh = true; 
  // 是否正在刷新 
  private boolean isrefreashing = false; 
  // footer 
  private xlistviewfooter footerview; 
  // 是否可以加载更多 
  private boolean enableloadmore; 
  // 是否正在加载 
  private boolean isloadingmore; 
  // 是否footer准备状态 
  private boolean isfooteradd = false; 
  // total list items, used to detect is at the bottom of listview. 
  private int totalitemcount; 
  // 记录是从header还是footer返回 
  private int mscrollback; 
 
  private static final string tag = "xlistview"; 
 
  public xlistview(context context) { 
    super(context); 
    initview(context); 
  } 
 
  public xlistview(context context, attributeset attrs) { 
    super(context, attrs); 
    initview(context); 
  } 
 
  public xlistview(context context, attributeset attrs, int defstyle) { 
    super(context, attrs, defstyle); 
    initview(context); 
  } 
 
  private void initview(context context) { 
 
    scroller = new scroller(context, new decelerateinterpolator()); 
 
    headerview = new xlistviewheader(context); 
    footerview = new xlistviewfooter(context); 
 
    headerviewcontent = (relativelayout) headerview 
        .findviewbyid(r.id.xlistview_header_content); 
    headerview.getviewtreeobserver().addongloballayoutlistener( 
        new ongloballayoutlistener() { 
          @suppresswarnings("deprecation") 
          @override 
          public void ongloballayout() { 
            headerheight = headerviewcontent.getheight(); 
            getviewtreeobserver() 
                .removeglobalonlayoutlistener(this); 
          } 
        }); 
    addheaderview(headerview); 
 
  } 
 
  @override 
  public void setadapter(listadapter adapter) { 
    // 确保footer最后添加并且只添加一次 
    if (isfooteradd == false) { 
      isfooteradd = true; 
      addfooterview(footerview); 
    } 
    super.setadapter(adapter); 
 
  } 
 
  @override 
  public boolean ontouchevent(motionevent ev) { 
 
    totalitemcount = getadapter().getcount(); 
    switch (ev.getaction()) { 
    case motionevent.action_down: 
      // 记录按下的坐标 
      lasty = ev.getrawy(); 
      break; 
    case motionevent.action_move: 
      // 计算移动距离 
      float deltay = ev.getrawy() - lasty; 
      lasty = ev.getrawy(); 
      // 是第一项并且标题已经显示或者是在下拉 
      if (getfirstvisibleposition() == 0 
          && (headerview.getvisiableheight() > 0 || deltay > 0)) { 
        updateheaderheight(deltay / offset_radio); 
      } else if (getlastvisibleposition() == totalitemcount - 1 
          && (footerview.getbottommargin() > 0 || deltay < 0)) { 
        updatefooterheight(-deltay / offset_radio); 
      } 
      break; 
 
    case motionevent.action_up: 
 
      if (getfirstvisibleposition() == 0) { 
        if (enablerefresh 
            && headerview.getvisiableheight() > headerheight) { 
          isrefreashing = true; 
          headerview.setstate(xlistviewheader.state_refreshing); 
          if (mlistviewlistener != null) { 
            mlistviewlistener.onrefresh(); 
          } 
        } 
        resetheaderheight(); 
      } else if (getlastvisibleposition() == totalitemcount - 1) { 
        if (enableloadmore 
            && footerview.getbottommargin() > pull_load_more_delta) { 
          startloadmore(); 
        } 
        resetfooterheight(); 
      } 
      break; 
    } 
    return super.ontouchevent(ev); 
  } 
 
  @override 
  public void computescroll() { 
 
    // 松手之后调用 
    if (scroller.computescrolloffset()) { 
 
      if (mscrollback == scrollback_header) { 
        headerview.setvisiableheight(scroller.getcurry()); 
      } else { 
        footerview.setbottommargin(scroller.getcurry()); 
      } 
      postinvalidate(); 
    } 
    super.computescroll(); 
 
  } 
 
  public void setpullrefreshenable(boolean enable) { 
    enablerefresh = enable; 
 
    if (!enablerefresh) { 
      headerview.hide(); 
    } else { 
      headerview.show(); 
    } 
  } 
 
  public void setpullloadenable(boolean enable) { 
    enableloadmore = enable; 
    if (!enableloadmore) { 
      footerview.hide(); 
      footerview.setonclicklistener(null); 
    } else { 
      isloadingmore = false; 
      footerview.show(); 
      footerview.setstate(xlistviewfooter.state_normal); 
      footerview.setonclicklistener(new onclicklistener() { 
        @override 
        public void onclick(view v) { 
          startloadmore(); 
        } 
      }); 
    } 
  } 
 
  public void stoprefresh() { 
    if (isrefreashing == true) { 
      isrefreashing = false; 
      resetheaderheight(); 
    } 
  } 
 
  public void stoploadmore() { 
    if (isloadingmore == true) { 
      isloadingmore = false; 
      footerview.setstate(xlistviewfooter.state_normal); 
    } 
  } 
 
  private void updateheaderheight(float delta) { 
    headerview.setvisiableheight((int) delta 
        + headerview.getvisiableheight()); 
    // 未处于刷新状态,更新箭头 
    if (enablerefresh && !isrefreashing) { 
      if (headerview.getvisiableheight() > headerheight) { 
        headerview.setstate(xlistviewheader.state_ready); 
      } else { 
        headerview.setstate(xlistviewheader.state_normal); 
      } 
    } 
 
  } 
 
  private void resetheaderheight() { 
    // 当前的可见高度 
    int height = headerview.getvisiableheight(); 
    // 如果正在刷新并且高度没有完全展示 
    if ((isrefreashing && height <= headerheight) || (height == 0)) { 
      return; 
    } 
    // 默认会回滚到header的位置 
    int finalheight = 0; 
    // 如果是正在刷新状态,则回滚到header的高度 
    if (isrefreashing && height > headerheight) { 
      finalheight = headerheight; 
    } 
    mscrollback = scrollback_header; 
    // 回滚到指定位置 
    scroller.startscroll(0, height, 0, finalheight - height, 
        scroll_duration); 
    // 触发computescroll 
    invalidate(); 
  } 
 
  private void updatefooterheight(float delta) { 
    int height = footerview.getbottommargin() + (int) delta; 
    if (enableloadmore && !isloadingmore) { 
      if (height > pull_load_more_delta) { 
        footerview.setstate(xlistviewfooter.state_ready); 
      } else { 
        footerview.setstate(xlistviewfooter.state_normal); 
      } 
    } 
    footerview.setbottommargin(height); 
 
  } 
 
  private void resetfooterheight() { 
    int bottommargin = footerview.getbottommargin(); 
    if (bottommargin > 0) { 
      mscrollback = scrollback_footer; 
      scroller.startscroll(0, bottommargin, 0, -bottommargin, 
          scroll_duration); 
      invalidate(); 
    } 
  } 
 
  private void startloadmore() { 
    isloadingmore = true; 
    footerview.setstate(xlistviewfooter.state_loading); 
    if (mlistviewlistener != null) { 
      mlistviewlistener.onloadmore(); 
    } 
  } 
 
  public void setxlistviewlistener(ixlistviewlistener l) { 
    mlistviewlistener = l; 
  } 
 
  public interface ixlistviewlistener { 
 
    public void onrefresh(); 
 
    public void onloadmore(); 
  } 
} 

    在三个构造函数中,都调用initview进行了header和footer的初始化,并且定义了一个scroller,并传入了一个减速的插值器,为了模仿回弹效果。在initview方法里面,因为header可能还没初始化完毕,所以通过globallayoutlistener来获取了header的高度,然后addheaderview添加到了listview上面。
    通过重写setadapter方法,保证footer最后天假,并且只添加一次。
    最重要的,要属ontouchevent了。在方法开始之前,通过getadapter().getcount()获取到了item的总数,便于计算位置。这个操作在源代码中是通过scrollerlistener完成的,因为scrollerlistener在这里没大有用,所以我直接去掉了,然后把位置改到了这里。如果在setadapter里面获取的话,只能获取到没有header和footer的item数量。
    在action_down里面,进行了lasty的初始化,lasty是为了判断移动方向的,因为在action_move里面,通过ev.getrawy()-lasty可以计算出手指的移动趋势,如果>0,那么就是向下滑动,反之向上。getrowy()是获取元y坐标,意思就是和window和view坐标没有关系的坐标,代表在屏幕上的绝对位置。然后在下面的代码里面,如果第一项可见并且header的可见高度>0或者是向下滑动,就说明用户在向下拉动或者是向上拉动header,也就是指示箭头显示的时候的状态,这时候调用了updateheaderheight,来更新header的高度,实现header可以跟随手指动作上下移动。这里有个offset_radio,这个值是一个移动比例,就是说,你手指在y方向上移动400px,如果比例是2,那么屏幕上的控件移动就是400px/2=200px,可以通过这个值来控制用户的滑动体验。下面的关于footer的判断与此类似,不再赘述。
   当用户移开手指之后,action_up方法就会被调用。在这里面,只对可见位置是0和item总数-1的位置进行了处理,其实正好对应header和footer。如果位置是0,并且可以刷新,然后当前的header可见高度>原始高度的话,就说明用户确实是要进行刷新操作,所以通过setstate改变header的状态,如果有监听器的话,就调用onrefresh方法,然后调用resetheaderheight初始化header的状态,因为footer的操作如出一辙,所以不再赘述。但是在footer中有一个pull_load_more_delta,这个值是加载更多触发条件的临界值,只有footer的间隔超过这个值之后,才能够触发加载更多的功能,因此我们可以修改这个值来改变用户体验。
    说到现在,大家应该明白基本的原理了,其实xlistview就是通过对用户手势的方向和距离的判断,来动态的改变header和footer实现的功能,所以如果我们也有类似的需求,就可以参照这种思路进行自定义。
    下面再说几个比较重要的方法。
    前面我们说道,在action_move里面,会不断的调用下面的updatexxxx方法,来动态的改变header和fooer的状态,

private void updateheaderheight(float delta) { 
    headerview.setvisiableheight((int) delta 
        + headerview.getvisiableheight()); 
    // 未处于刷新状态,更新箭头 
    if (enablerefresh && !isrefreashing) { 
      if (headerview.getvisiableheight() > headerheight) { 
        headerview.setstate(xlistviewheader.state_ready); 
      } else { 
        headerview.setstate(xlistviewheader.state_normal); 
      } 
    } 
 
  } 
 
private void updatefooterheight(float delta) { 
    int height = footerview.getbottommargin() + (int) delta; 
    if (enableloadmore && !isloadingmore) { 
      if (height > pull_load_more_delta) { 
        footerview.setstate(xlistviewfooter.state_ready); 
      } else { 
        footerview.setstate(xlistviewfooter.state_normal); 
      } 
    } 
    footerview.setbottommargin(height); 
 
  } 

    在移开手指之后,会调用下面的resetxxx来初始化header和footer的状态

private void resetheaderheight() { 
    // 当前的可见高度 
    int height = headerview.getvisiableheight(); 
    // 如果正在刷新并且高度没有完全展示 
    if ((isrefreashing && height <= headerheight) || (height == 0)) { 
      return; 
    } 
    // 默认会回滚到header的位置 
    int finalheight = 0; 
    // 如果是正在刷新状态,则回滚到header的高度 
    if (isrefreashing && height > headerheight) { 
      finalheight = headerheight; 
    } 
    mscrollback = scrollback_header; 
    // 回滚到指定位置 
    scroller.startscroll(0, height, 0, finalheight - height, 
        scroll_duration); 
    // 触发computescroll 
    invalidate(); 
  } 
 
private void resetfooterheight() { 
    int bottommargin = footerview.getbottommargin(); 
    if (bottommargin > 0) { 
      mscrollback = scrollback_footer; 
      scroller.startscroll(0, bottommargin, 0, -bottommargin, 
          scroll_duration); 
      invalidate(); 
    } 
  } 

    我们可以看到,滚动操作不是通过直接的设置高度来实现的,而是通过scroller.startscroll()来实现的,通过调用此方法,computescroll()就会被调用,然后在这个里面,根据mscrollback区分是哪一个滚动,然后再通过设置高度和间隔,就可以完成收缩的效果了。
    至此,整个xlistview的实现原理就完全的搞明白了,以后如果做滚动类的自定义控件,应该也有思路了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网