欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中Listview下拉刷新和上拉加载更多的多种实现方案

程序员文章站 2024-02-23 11:04:16
 listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明。      方案一:添加头...

 listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明。

     方案一:添加头布局和脚布局

        android系统为listview提供了addfootview和addheadview两个api。这样可以直接自定义一个view,以添加视图的形式实现下来刷新和上拉加载。

     实现步骤   

       1、创建一个类继承listview:class pulltorefreshlistview extends listview; 

     2、在构造方法中添加headview:addheaderview(headview);

       3、获取headview的高。测量控件的高可以有两方法getmeasuredheight和getheight,getmeasuredheight()在onmeasure方法执行之后才能获取到;getheight()  在onlayout方法执行之后才能获取到值;

       4、显示和隐藏headview,通过setpadding实现,当向下滑,且第一条可见item是第0条的时候才需要设置headview的paddingtop来显示headview。          

       显示:headview.setpadding(0,0,0,0);     

       隐藏:headview.setpadding(0,-headviewheight,0,0);

      5、下拉刷新三种状态的判断,移动的时候,当paddingtop < 0 的时候,说明headview没有完全显示出来,进入下拉刷新状态;移动的时候,当paddingtop >= 0 的时候,   说明headview已经完全显示出来了,进入松开以新状态;手指抬起的时候,且当前状态是松开刷新状态的时候,进入正在刷新状态; 当已经是“正在刷新”状态时,   则不允许再做”下拉刷新”和”松开刷新”的操作了,在move事件中加入判断,如果已经是正在刷新状态了,则不处理下拉的操作了。      

       6、下拉箭头的转动。下拉刷新是向下,松开刷新时向上。旋转动画通过属性动画实现。隐藏箭头的时候要清除动画:iv_arrow.clearanimation();  如果不隐藏动画效果,设置view.gone之后还是看得见的。    

       7、headview显示时,当手指松开时的处理,松开时如果是“正在刷新”状态,则把headvie完全显示;松开时如果是“下拉刷新”状态,则把headview完全隐藏    

       8、增加footerview:addfooterview(footerview)。当listview处于空闲状态,并且最后一条可见item是listview中的最后一条数据时显示footview,   footerview显示出来后,listview不会自动上滑把footerview显示出来的,所以需要手动设置:setselection(getcount() - 1);即选中最后一条。     

      9、增加回调监听器。当listview处于刷新状态的时候会调用onrefreshing()方法;当listview处于加载更多的时候会调用onloadmore()。加载完成后通知控件加载完成。

       具体实现:

import com.itheima.pulltorefreshlistview.r;
import android.content.context;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;
import android.view.animation.rotateanimation;
import android.widget.abslistview;
import android.widget.imageview;
import android.widget.listview;
import android.widget.progressbar;
import android.widget.textview;
public class pulltorefreshlistview extends listview {
  private view headerview;
  private float downy;
  private int headerviewheight;
  /** 状态:下拉刷新 */
  private static final int state_pull_to_refresh = 0;
  /** 状态:松开刷新 */
  private static final int state_release_refresh = 1;
  /** 状态:正在刷新 */
  private static final int state_refreshing = 2;
  /** 当前状态 */
  private int currentstate = state_pull_to_refresh;  // 默认是下拉刷新状态
  private imageview iv_arrow;
  private progressbar progress_bar;
  private textview tv_state;
  private rotateanimation upanim;
  private rotateanimation downanim;
  private onrefreshinglistener monrefreshinglistener;
  private view footerview;
  private int footerviewheight;
  /** 正在加载更多 */
  private boolean loadingmore;
  public pulltorefreshlistview(context context, attributeset attrs) {
    super(context, attrs);
    initheaderview();
    initfooterview();
  }
  private void initheaderview() {
    headerview = view.inflate(getcontext(), r.layout.header_view, null);
    iv_arrow = (imageview) headerview.findviewbyid(r.id.iv_arrow);
    progress_bar = (progressbar) headerview.findviewbyid(r.id.progress_bar);
    showrefreshingprogressbar(false);
    tv_state = (textview) headerview.findviewbyid(r.id.tv_state);
    headerview.measure(0, 0);  // 主动触发测量,mesure内部会调用onmeasure
    headerviewheight = headerview.getmeasuredheight();
    hideheaderview();
    super.addheaderview(headerview);
    upanim = createrotateanim(0f, -180f);
    downanim = createrotateanim(-180f, -360f);
  }
  private void initfooterview() {
    footerview = view.inflate(getcontext(), r.layout.footer_view, null);
    footerview.measure(0, 0);// 主动触发测量,mesure内部会调用onmeasure
    footerviewheight = footerview.getmeasuredheight();
    hidefooterview();
    super.addfooterview(footerview);
    super.setonscrolllistener(new onscrolllistener() {
      // 当listview滚动的状态发生改变的时候会调用这个方法
      @override
      public void onscrollstatechanged(abslistview view, int scrollstate) {
        if (scrollstate == onscrolllistener.scroll_state_idle  // listview处于空闲状态
            && getlastvisibleposition() == getcount() - 1  // 界面上可见的最后一条item是listview中最后的一条item
            && loadingmore == false              // 如果当前没有去做正在加载更多的事情
            ) {
          loadingmore = true;
          showfooterview();
          setselection(getcount() - 1);
          if (monrefreshinglistener != null) {
            monrefreshinglistener.onloadmore();
          }
        }
      }
      // 当listview滚动的时候会调用这个方法
      @override
      public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) {
      }
    });
  }
  private void hidefooterview() {
    int paddingtop = -footerviewheight;
    setfooterviewpaddingtop(paddingtop);
  }
  private void showfooterview() {
    int paddingtop = 0;
    setfooterviewpaddingtop(paddingtop);
  }
  private void setfooterviewpaddingtop(int paddingtop) {
    footerview.setpadding(0, paddingtop, 0, 0);
  }
  /**
   * 设置显示进度的圈圈
   * @param showprogressbar 如果是true,则显示progressbar,否则的话显示箭头
   */
  private void showrefreshingprogressbar(boolean showprogressbar) {
    progress_bar.setvisibility(showprogressbar ? view.visible : view.gone);
    iv_arrow.setvisibility(!showprogressbar ? view.visible : view.gone);
    if (showprogressbar) {
      iv_arrow.clearanimation();  // 有动画的view要清除动画才能真正的隐藏
    }
  }
  /**
   * 创建旋转动画
   * @param fromdegrees 从哪个角度开始转
   * @param todegrees 转到哪个角度
   * @return 
   */
  private rotateanimation createrotateanim(float fromdegrees, float todegrees) {
    int pivotxtype = rotateanimation.relative_to_self;    // 旋转点的参照物
    int pivotytype = rotateanimation.relative_to_self;    // 旋转点的参照物
    float pivotxvalue = 0.5f;  // 旋转点x方向的位置
    float pivotyvalue = 0.5f;  // 旋转点y方向的位置
    rotateanimation ra = new rotateanimation(fromdegrees, todegrees, pivotxtype, pivotxvalue, pivotytype, pivotyvalue);
    ra.setduration(300);
    ra.setfillafter(true);  // 让动画停留在结束位置
    return ra;
  }
  /** 隐藏headerview */
  private void hideheaderview() {
    int paddingtop = -headerviewheight;
    setheaderviewpaddingtop(paddingtop);
  }
  /** 显示headerview */
  private void showheaderview() {
    int paddingtop = 0;
    setheaderviewpaddingtop(paddingtop);
  }
  /**
   * 设置headerview的paddingtop
   * @param paddingtop
   */
  private void setheaderviewpaddingtop(int paddingtop) {
    headerview.setpadding(0, paddingtop, 0, 0);
  }
  @override
  public boolean ontouchevent(motionevent ev) {
    switch (ev.getaction()) {
    case motionevent.action_down:
      downy = ev.gety();
      break;
    case motionevent.action_move:
      if (currentstate == state_refreshing) {
        // 如果当前已经是“正在刷新“的状态了,则不用去处理下拉刷新了
        return super.ontouchevent(ev);
      }
      int fingermovedistancey = (int) (ev.gety() - downy);    // 手指移动的距离
      // 如果是向下滑动,并且界面上可见的第一条item是listview的索引为0的item时我们才处理下拉刷新的操作
      if (fingermovedistancey > 0 && getfirstvisibleposition() == 0) {
        int paddingtop = -headerviewheight + fingermovedistancey;
        setheaderviewpaddingtop(paddingtop);
        if (paddingtop < 0 && currentstate != state_pull_to_refresh) {
          // 如果paddingtop小于0,说明headerview没有完全显示出来,则进入下拉刷新的状态
          currentstate = state_pull_to_refresh;
          tv_state.settext("下拉刷新");
          iv_arrow.startanimation(downanim);
          showrefreshingprogressbar(false);
          // 让箭头转一下
        } else if (paddingtop >= 0 && currentstate != state_release_refresh) {
          // 如果paddingtop>=0,说明headerview已经完全显示出来,则进入松开刷新的状态
          currentstate = state_release_refresh;
          tv_state.settext("松开刷新");
          iv_arrow.startanimation(upanim);
          showrefreshingprogressbar(false);
        }
        return true;
      }
      break;
    case motionevent.action_up:
      if (currentstate == state_release_refresh) {
        // 如果当前状态是松开刷新,并且抬起了手,则进入正在刷新状态
        currentstate = state_refreshing;
        tv_state.settext("正在刷新");
        showrefreshingprogressbar(true);
        showheaderview();
        if (monrefreshinglistener != null) {
          monrefreshinglistener.onrefreshing();
        }
      } else if (currentstate == state_pull_to_refresh) {
        // 如果抬起手时是下拉刷新状态,则把headerview完成隐藏
        hideheaderview();
      }
      break;
    }
    return super.ontouchevent(ev);
  }
  public void setonrefreshinglistener(onrefreshinglistener monrefreshinglistener) {
    this.monrefreshinglistener = monrefreshinglistener;
  }
  /** listview刷新的监听器 */
  public interface onrefreshinglistener {
    /** 当listview可以刷新数据的时候会调用这个方法 */
    void onrefreshing();
    /** 当listview可以加载更多 的时候会调用这个方法 */
    void onloadmore();
  }
  /** 联网刷新数据的操作已经完成了 */
  public void onrefreshcomplete() {
    hideheaderview();
    currentstate = state_pull_to_refresh;
    showrefreshingprogressbar(false);
  }
  /** 加载更多新数据的操作已经完成了 */
  public void onloadmorecomplete() {
    hidefooterview();
    loadingmore = false;
  }
}

 方案二: listview的多种样式显示

        设置listview的适配器的时候可以实现两个方法: getviewtypecount()和getitemviewtype(),前者指定条目的种类,后者返回具体的类型,这样可以根据不同的类型设计相关的样式,包括上拉加载更多,和下拉刷新,两者类似,因此这里仅仅给出加载更多的写法。具体实现如下:

        1、重写getviewtypecount()和getitemviewtype(),这里包括普通的item条目和加载更多的条目,所以getviewtypecount()返回值为2;

@override
  public int getviewtypecount() {
    return super.getviewtypecount() + 1;
  }
  @override
  public int getitemviewtype(int position) {
    if (position == getcount() - 1) {
      return 0;
    } else {
      return addviewtype(position); //构造一个方法出来,方便子类修改,添加更多的样式
    }
  }
  public int addviewtype(int position) {
    return 1;
  }

         2、在getview()中针对不同的类型添加布局:

 @override
  public view getview(int position, view convertview, viewgroup parent) {
    baseholdle holdle;
    if (convertview == null) {
      if (getitemviewtype(position) == 0) { //type为0 表示应该加载加载更多的视图
        holdle = getloadmoreholdle();
      } else {                //否则为普通视图
        holdle = getspecialbaseholdle(position);
      }
    } else {
      holdle = (baseholdle) convertview.gettag();
    }
    if (getitemviewtype(position) == 0) {   //加载更多视图,请求网络获取数据
      if (havemore()) {
        holdle.setdataandrefreshholdleview(loadmoreholdle.loadmore_loding);
        triggleloadmoredata();
      } else {               
        holdle.setdataandrefreshholdleview(loadmoreholdle.loadmore_none);
      }
    } else {                 //普通视图视图,请求网络获取数据
      t data = (t) mdata.get(position);
      holdle.setdataandrefreshholdleview(data);
    }
    mholdleview = holdle.mholdleview;
    mholdleview.setscalex(0.6f);
    mholdleview.setscaley(0.6f);
    viewcompat.animate(mholdleview).scalex(1).scaley(1).setduration(400).setinterpolator(new overshootinterpolator(4)).start();
    return mholdleview;
  }

           3、具体的加载更多视图的实现

private baseholdle getloadmoreholdle() {
    if (mloadmoreholdle == null) {
      mloadmoreholdle = new loadmoreholdle();
    }
    return mloadmoreholdle;
  }
  public class loadmoreholdle extends baseholdle {
  @bind(r.id.item_loadmore_container_loading)
  linearlayout itemloadmorecontainerloading;
  @bind(r.id.item_loadmore_container_retry)
  linearlayout itemloadmorecontainerretry;
  @bind(r.id.item_loadmore_tv_retry)
  textview item_loadmore_tv_retry;
  public static final int loadmore_loding = 0;
  public static final int loadmore_error = 1;
  public static final int loadmore_none = 2;
  private int mcurretstate;
  @override
  public void refreshholdleview(object data) {
    itemloadmorecontainerloading.setvisibility(view.gone);
    itemloadmorecontainerretry.setvisibility(view.gone);
    mcurretstate = (int) data;
    switch (mcurretstate) {
      case loadmore_loding:
        itemloadmorecontainerloading.setvisibility(view.visible);
        break;
      case loadmore_error:
        itemloadmorecontainerretry.setvisibility(view.visible);
        break;
      case loadmore_none:
        break;
    }
  }
  @override
  public view ininviewholdle() {
    view view = view.inflate(uiutils.getcontext(), r.layout.itemloadmore, null);
    butterknife.bind(this, view);
    return view;
  }
}
//holder基类,提取公共的方法
public abstract class baseholdle<t> {
  public view mholdleview;
  public t mdata;
  public baseholdle() {
    mholdleview = ininviewholdle();
    mholdleview.settag(this);
  }
  public void setdataandrefreshholdleview(t mdata) {
    this.mdata = mdata;
    refreshholdleview(mdata);
  }
  public abstract void refreshholdleview(t data);
  public abstract view ininviewholdle();
}

方案三: swiperefreshlayout实现下来刷新

     swiperefreshlayout对下不兼容,且只有下拉刷新功能没有上拉加载更多的功能。当时作为andriod5.0之后的新特性,使用起来方便,可以直接调用系统的api。使用方法也较为简单。具体实现如下:

    首先声明控件,设置颜色: 

 refreshlayout = (swiperefreshlayout) findviewbyid(r.id.refresh);
   refreshlayout.setonrefreshlistener(this); 
   refreshlayout.setcolorschemeresources(android.r.color.holo_blue_bright,
      android.r.color.holo_green_light,android.r.color.holo_orange_light,
      android.r.color.holo_red_light);
   refreshlayout.setprogressbackgroundcolor(r.color.refresh_bg);
   refreshlayout.setprogressbackgroundcolor(r.color.refresh_bg);

    写一个类实现swiperefreshlayout.onrefreshlistener,重写onrefresh()方法:

@override  
  public void onrefresh() { 
   refreshlayout.postdelayed(new runnable() {
    @override
    public void run() {
     //请求网络,获取数据        
     refreshlayout.setrefreshing(false);
    }
    },3000);
  }

以上所述是小编给大家介绍的android中listview下拉刷新和上拉加载更多的多种实现方案,希望对大家有所帮助