当前位置: 移动技术网 > 移动技术>移动开发>Android > Android实现简单的下拉刷新pulltorefresh

Android实现简单的下拉刷新pulltorefresh

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

网上下拉刷新的demo很多,但是总有各种不满意的地方,有些会下拉卡住,有些回弹不流畅,有些性能太低会各种卡顿,有些emptyview无法下拉...... 

自己写的才是最合适自己的,代码很简单,也很容易修改,稍微阅读下代码就能改出自己需要的各种效果。

首先,重写listview,自定义touch事件,为了使emptyview也可下拉,emptyview也加上touch事件。 如果要实现gridview,把这里的listview改成gridview即可。

pullablelistview :

public class pullablelistview extends listview {
  private boolean inited;
  private float density;
  private int mdowny, mmovey;
  private int mpully;
  private boolean ispull;
  private pulllistener mpulllistener;
  private velocitytracker mvelocitytracker;

  public interface pulllistener {

    public boolean onpulldownstart();

    public void onpulldown(int movey);

    public void onpulldowndrop();
  }

  public pullablelistview(context context, attributeset attrs, int defstyle) {
    super(context, attrs, defstyle);
    init();
  }

  public pullablelistview(context context, attributeset attrs) {
    super(context, attrs);
    init();
  }

  public pullablelistview(context context) {
    super(context);
    init();
  }

  private void init() {
    if (!inited) {
      density = getresources().getdisplaymetrics().density;
    }
  }

  public void setpulllistener(pulllistener mpulllistener) {
    this.mpulllistener = mpulllistener;
  }

  public boolean ispulling() {
    return ispull;
  }

  @override
  public void setemptyview(view emptyview) {
    super.setemptyview(emptyview);
    // 重写emptyview的touch事件,使显示emptyview时也可以下拉刷新
    emptyview.setontouchlistener(new ontouchlistener() {
      @override
      public boolean ontouch(view v, motionevent ev) {
        if (mvelocitytracker == null) {
          mvelocitytracker = velocitytracker.obtain();
        }
        mvelocitytracker.addmovement(ev);
        switch (ev.getaction()) {
          case motionevent.action_down:
            mdowny = (int) ev.gety();
            break;
          case motionevent.action_move:
            mmovey = (int) ev.gety();
            if (!ispull) {
              mvelocitytracker.computecurrentvelocity(1000, 8000f);
              if (mvelocitytracker.getyvelocity() > 500 // 下拉速度大于500
                  && math.abs(mmovey - mdowny) > 20 * density) { // 下拉距离超过20dp
                mpully = mmovey;
                if (mpulllistener.onpulldownstart()) {
                  ispull = true;
                }
              }
            } else {
              // 阻尼下拉(随着下拉距离增加,阻力增加)
              mpulllistener.onpulldown(mmovey - mpully + v.getscrolly());
              // 等阻力下拉(阻力恒定,不随下拉距离增加而增加)
              // mpulllistener.onpulldown(mmovey - mpully);
              if (mmovey < mpully) {
                ispull = false;
              }
              return true;
            }
            break;
          case motionevent.action_up:
            if (mvelocitytracker != null) {
              mvelocitytracker.clear();
              mvelocitytracker.recycle();
              mvelocitytracker = null;
            }
            if (ispull) {
              mpully = 0;
              ispull = false;
              mpulllistener.onpulldowndrop();
              return true;
            }
            break;
        }
        return true;
      }
    });
  }

  @override
  public boolean onintercepttouchevent(motionevent ev) {
    if (ispull) {
      // 正在下拉时,阻住touch事件向下传递,同时会向各个childview发送action_canle事件,
      // 使之前捕捉到了action_down事件的childview回复到正常状态
      return true;
    }

    return super.onintercepttouchevent(ev);
  }

  @override
  public boolean ontouchevent(motionevent ev) {
    if (mvelocitytracker == null) {
      mvelocitytracker = velocitytracker.obtain();
    }
    mvelocitytracker.addmovement(ev);
    switch (ev.getaction()) {
      case motionevent.action_down:
        mdowny = (int) ev.gety();
        break;
      case motionevent.action_move:
        mmovey = (int) ev.gety();
        if (!ispull) {
          if (getfirstvisibleposition() == 0) {
            view view = getchildat(0);
            mvelocitytracker.computecurrentvelocity(1000, 8000f);
            if (mvelocitytracker.getyvelocity() > 500// 下拉速度大于500
                && (view == null || view.gettop() == getpaddingtop()) // 已拉动到顶部
                && math.abs(mmovey - mdowny) > 15 * density) { // 下拉距离超过20dp
              mpully = mmovey;
              if (mpulllistener.onpulldownstart()) {
                // 根据返回值确认是否进入下拉状态
                ispull = true;
              }
            }
          }
        } else {
          // 阻尼下拉(随着下拉距离增加,阻力增加)
          mpulllistener.onpulldown(mmovey - mpully);
          // 等阻力下拉(阻力恒定,不随下拉距离增加而增加)
          // mpulllistener.onpulldown(mmovey - mpully - getscrolly());
          if (mmovey < mpully) {
            ispull = false;
          }
          return true;
        }
        break;
      case motionevent.action_up:
        if (mvelocitytracker != null) {
          mvelocitytracker.clear();
          mvelocitytracker.recycle();
          mvelocitytracker = null;
        }
        if (ispull) {
          mpully = 0;
          ispull = false;
          mpulllistener.onpulldowndrop();
          return true;
        }
        break;
      case motionevent.action_cancel:
        break;
    }
    return super.ontouchevent(ev);
  }
}

然后是外层的linearylayer,监听pullablelistview的下拉回调,实现下拉效果。同时提供listview(gridview)的外部接口,如 setemptyview(view view),setadapter(listadapter adapter)...等等,这里只提供部分我需要使用的,可以根据自身需求去提供外部接口。 
代码中r.drawable.pulltorefresh 和 r.drawable.loading 分别是下拉箭头 和 刷新滚动条 的图片,这里不提供了,自己随意找两张图片贴上就行了。 

pulltorefreshview: 

public class pulltorefreshview extends linearlayout {

  protected static final string tag = "pulltorefreshview";

  /**
   * 下拉阻力系数
   */
  private static final float scall_pull_doww = 2.0f;

  private view mview;

  private pullablelistview mlistview;

  private textview mpulltv;

  private imageview mprogressbar;

  private view mpullv;

  private view memptyview;

  private boolean isinited;

  private boolean canrefresh;

  private boolean isrefreshing;

  private boolean ispullable = true;

  private int mormargin;

  private objectanimator marrowrotateanimator;

  private animation mproanimation;

  private pulltorefreshlistener mpulltorefreshlistener;

  public pulltorefreshview(context context, attributeset attrs, int defstyle) {
    super(context, attrs, defstyle);
    initview(context);
  }

  public pulltorefreshview(context context, attributeset attrs) {
    super(context, attrs);
    initview(context);
  }

  public pulltorefreshview(context context) {
    super(context);
    initview(context);
  }

  public interface pulltorefreshlistener {
    /**
     * do data refresh here
     */
    public void onrefreshstart();

    /**
     * do view update here
     */
    public void onrefreshfinished();
  }

  private void initview(context context) {
    if (!isinited) {
      isinited = true;
      mview = layoutinflater.from(context).inflate(r.layout.view_pulltorefresh, null);
      mprogressbar = (imageview) mview.findviewbyid(r.id.iv_pulltorefresh_arrow);
      mprogressbar.setimageresource(r.drawable.pulltorefresh);
      mpulltv = (textview) mview.findviewbyid(r.id.tv_pulltorefresh);
      mpullv = mview.findviewbyid(r.id.ly_pulltorefresh_pull);
      mlistview = (pullablelistview) mview.findviewbyid(r.id.gv_smarturc_urcs);
      mlistview.setpulllistener(mpulllistener);
      layoutparams lp = new layoutparams(layoutparams.match_parent,
          layoutparams.match_parent);
      addview(mview, lp);
      layoutparams lparams = (layoutparams) mpullv.getlayoutparams();
      mormargin = lparams.topmargin;
      mproanimation = animationutils.loadanimation(getcontext(),
          r.anim.anim_progressbar);
    }
  }

  private pulllistener mpulllistener = new pulllistener() {

    @override
    public boolean onpulldownstart() {
      if (isrefreshing || !ispullable) {
        return false;
      }
      mpulltv.settext("下拉刷新");
      mprogressbar.setrotation(0f);
      mprogressbar.setimageresource(r.drawable.pulltorefresh);
      if (mprogressbar.getanimation() != null) {
        mprogressbar.clearanimation();
      }
      return true;
    }

    @override
    public void onpulldown(int movey) {
      if (isrefreshing || !ispullable) {
        return;
      }
      movey = (int) math.max(0, movey / scall_pull_doww);
      mview.scrollto(0, -movey);
      memptyview.scrollto(0, -movey);
      if (!canrefresh
          && math.abs(mview.getscrolly()) > math.abs(mormargin)) {
        mpulltv.settext("松开刷新");
        canrefresh = true;
        if (marrowrotateanimator != null) {
          marrowrotateanimator.cancel();
        }
        float rotation = mprogressbar.getrotation();
        marrowrotateanimator = objectanimator.offloat(mprogressbar, "rotation",
            rotation, 180f);
        marrowrotateanimator.setduration(100).start();
      } else if (canrefresh
          && math.abs(mview.getscrolly()) <= math.abs(mormargin)) {
        mpulltv.settext("下拉刷新");
        canrefresh = false;
        if (marrowrotateanimator != null) {
          marrowrotateanimator.cancel();
        }
        float rotation = mprogressbar.getrotation();
        marrowrotateanimator = objectanimator.offloat(mprogressbar, "rotation",
            rotation, 0f);
        marrowrotateanimator.setduration(100).start();
      }
    }

    @override
    public void onpulldowndrop() {
      if (canrefresh) {
        setrefreshing();
      } else {
        isrefreshing = false;
        backto(mview.getscrolly(), 0);
      }
    }
  };

  private void backto(final int from, final int to) {
    objectanimator.ofint(mview, "scrolly", from, to).setduration(300)
        .start();
    objectanimator.ofint(memptyview, "scrolly", from, to).setduration(300)
        .start();
  }

  /**
   * 设置为正在刷新状态
   */
  public void setrefreshing() {
    isrefreshing = true;
    mprogressbar.setimageresource(r.drawable.loading);
    mprogressbar.startanimation(mproanimation);
    mpulltv.settext("正在刷新");
    backto(mview.getscrolly(), mormargin);
    if (mpulltorefreshlistener != null) {
      mpulltorefreshlistener.onrefreshstart();
    }
  }

  /**
   * 刷新完成
   */
  public void setrrefreshfinish() {
    if (isrefreshing) {
      isrefreshing = false;
      backto(mview.getscrolly(), 0);
    }
    if (mpulltorefreshlistener != null) {
      mpulltorefreshlistener.onrefreshfinished();
    }
  }

  public void setpullable(boolean pullable) {
    ispullable = pullable;
  }

  public void setpulltorefreshlistener(
      pulltorefreshlistener mpulltorefreshlistener) {
    this.mpulltorefreshlistener = mpulltorefreshlistener;
  }

  public void setadapter(listadapter adapter) {
    mlistview.setadapter(adapter);
  }

  public void setemptyview(view emptyview) {
    mlistview.setemptyview(emptyview);
    this.memptyview = emptyview;
  }

  public void setonitemclicklistener(onitemclicklistener itemclicklistener) {
    mlistview.setonitemclicklistener(itemclicklistener);
  }

  public void setonitemlongclicklistener(onitemlongclicklistener itemlongclicklistener) {
    mlistview.setonitemlongclicklistener(itemlongclicklistener);
  }
}

layout-view_pulltorefresh: 

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#cccccc"
  android:orientation="vertical" >

  <linearlayout
    android:id="@+id/ly_pulltorefresh_pull"
    android:layout_width="wrap_content"
    android:layout_height="48dp"
    android:layout_gravity="center_horizontal"
    android:layout_margintop="-48dp" >

    <imageview
      android:id="@+id/iv_pulltorefresh_arrow"
      android:layout_width="20dp"
      android:layout_height="match_parent"
      android:scaletype="fitcenter"
      android:src="@drawable/pulltorefresh" />

    <textview
      android:id="@+id/tv_pulltorefresh"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_marginbottom="4dp"
      android:layout_marginleft="8dp"
      android:gravity="center"
      android:textcolor="@android:color/white"
      android:textsize="16sp" />
  </linearlayout>

  <com.example.pulltorefresh.pullablelistview
    android:id="@+id/gv_smarturc_urcs"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:overscrollmode="never"
    android:scrollingcache="false" >
  </com.example.pulltorefresh.pullablelistview>

</linearlayout>

anim-anim_progressbar: 

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
  android:fromdegrees="0"
  android:todegrees="360"
  android:pivotx="50%"
  android:pivoty="50%"
  android:repeatcount="infinite"
  android:repeatmode="restart"
  android:duration="800"
  android:interpolator="@android:anim/linear_interpolator"/>

最后是demo activity: 

public class pulltorefreshactivity extends activity {

  private pulltorefreshview mpulltorefreshview;
  private list<string> data = new arraylist<string>();
  private myadapter madapter;
  private handler mhandler;

  @override
  protected void oncreate(bundle savedinstancestate) {
    // todo auto-generated method stub
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_pulltorefresh);
    mhandler = new handler();
    mpulltorefreshview = (pulltorefreshview) findviewbyid(r.id.pulltorefreshview1);
    madapter = new myadapter();
    mpulltorefreshview.setadapter(madapter);
    mpulltorefreshview.setemptyview(findviewbyid(r.id.empty));
    mpulltorefreshview.setonitemlongclicklistener(new onitemlongclicklistener() {
      @override
      public boolean onitemlongclick(adapterview<?> parent, view view, int position, long id) {
        toast.maketext(getapplicationcontext(), "long click : " + data.get(position),
            toast.length_short).show();
        return true;
      }
    });
    mpulltorefreshview.setonitemclicklistener(new onitemclicklistener() {
      @override
      public void onitemclick(adapterview<?> parent, view view, int position, long id) {
        toast.maketext(getapplicationcontext(), data.get(position), toast.length_short)
            .show();
      }
    });
    mpulltorefreshview.setpulltorefreshlistener(new pulltorefreshlistener() {
      @override
      public void onrefreshstart() {
        // 模拟刷新数据
        mhandler.postdelayed(new runnable() {
          @override
          public void run() {
            data.add(string.valueof((int) (math.random() * 1000)));
            mpulltorefreshview.setrrefreshfinish();
          }
        }, 2000);
      }

      @override
      public void onrefreshfinished() {
        // 更新视图
        madapter.notifydatasetchanged();
      }
    });
//    mhandler.postdelayed(new runnable() {
//      @override
//      public void run() {
//        // todo auto-generated method stub
//        mpulltorefreshview.setrefreshing();
//      }
//    }, 500);
  }

  public class myadapter extends baseadapter {

    @override
    public int getcount() {
      // todo auto-generated method stub
      return data.size();
    }

    @override
    public object getitem(int position) {
      // todo auto-generated method stub
      return data.get(position);
    }

    @override
    public long getitemid(int position) {
      // todo auto-generated method stub
      return position;
    }

    @override
    public view getview(int position, view convertview, viewgroup parent) {
      // todo auto-generated method stub
      if (convertview == null) {
        convertview = new textview(pulltorefreshactivity.this);
      }
      textview textview = (textview) convertview;
      textview.settextsize(typedvalue.complex_unit_sp, 40f);
      textview.setpadding(30, 30, 30, 30);
      textview.settext(data.get(position));
      return convertview;
    }

  }
}

layout-activity_pulltorefresh: 

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

  <com.example.pulltorefresh.pulltorefreshview
    android:id="@+id/pulltorefreshview1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignparentleft="true"
    android:layout_alignparenttop="true" >
  </com.example.pulltorefresh.pulltorefreshview>

  <linearlayout
    android:id="@+id/empty"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:padding="60dp" >

    <imageview
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/ic_launcher" />

    <textview
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="no data" />
  </linearlayout>

</relativelayout>

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网