Android实现简单的下拉刷新pulltorefresh
程序员文章站
2024-03-04 19:03:30
网上下拉刷新的demo很多,但是总有各种不满意的地方,有些会下拉卡住,有些回弹不流畅,有些性能太低会各种卡顿,有些emptyview无法下拉......
自己...
网上下拉刷新的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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。