Android自定义控件开发实战之实现ListView下拉刷新实例代码
程序员文章站
2024-02-27 23:55:09
这篇博客为大家介绍一个android常见的功能——listview下拉刷新:
首先下拉未松手时候手机显示这样的界面:
下面的代码是自定的扎样的控件:...
这篇博客为大家介绍一个android常见的功能——listview下拉刷新:
首先下拉未松手时候手机显示这样的界面:
下面的代码是自定的扎样的控件:
<span style="font-family: comic sans ms,sans-serif; font-size: 16px;">package com.dhsr.smartid.view; import android.content.context; import android.util.attributeset; import android.view.gravity; import android.view.layoutinflater; import android.view.view; import android.view.animation.animation; import android.view.animation.rotateanimation; import android.widget.imageview; import android.widget.linearlayout; import android.widget.progressbar; import android.widget.textview; import com.example.sirelinkscanapp.r; /** * 自定义控件,完成下拉时候listview显示“图标及提示正在刷新……”的布局 * * @author cyf * */ public class xlistviewheader extends linearlayout { private linearlayout mcontainer; // 图片 private imageview marrowimageview; // 圆形进度条 private progressbar mprogressbar; private textview mhinttextview; // 状态 private int mstate = state_normal; // 动画 private animation mrotateupanim; private animation mrotatedownanim; private final int rotate_anim_duration = 180; // 正常 public final static int state_normal = 0; // 准备刷新 public final static int state_ready = 1; // 刷新中 public final static int state_refreshing = 2; public xlistviewheader(context context) { super(context); initview(context); } /** * @param context * @param attrs */ public xlistviewheader(context context, attributeset attrs) { super(context, attrs); initview(context); } /** * “ 松开即可刷新……正在刷新……”的布局 * * @param context */ private void initview(context context) { // 初始情况,设置下拉刷新view高度为0 linearlayout.layoutparams lp = new linearlayout.layoutparams( layoutparams.match_parent, 0); mcontainer = (linearlayout) layoutinflater.from(context).inflate( r.layout.xlistview_header, null); // 加载视图 addview(mcontainer, lp); // 居中方式 setgravity(gravity.bottom); // 初始化控件 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); } /** * 根据下拉状态执行相应的功能,开始以及停止相应的动画 * * @param state */ 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(r.string.xlistview_header_hint_normal); break; case state_ready: if (mstate != state_ready) { marrowimageview.clearanimation(); marrowimageview.startanimation(mrotateupanim); mhinttextview.settext(r.string.xlistview_header_hint_ready); } break; case state_refreshing: mhinttextview.settext(r.string.xlistview_header_hint_loading); break; default: } mstate = state; } public void setvisiableheight(int height) { if (height < 0) height = 0; linearlayout.layoutparams lp = (linearlayout.layoutparams) mcontainer .getlayoutparams(); lp.height = height; mcontainer.setlayoutparams(lp); } public int getvisiableheight() { return mcontainer.getheight(); } } </span>
接下来需要自定义自己的listview继承与android本身的listview,方便自己添加新的方法。
<span style="font-family: comic sans ms,sans-serif; font-size: 16px;">package com.dhsr.smartid.view; import android.content.context; import android.util.attributeset; import android.view.motionevent; import android.view.view; import android.view.viewtreeobserver.ongloballayoutlistener; import android.view.animation.decelerateinterpolator; import android.widget.abslistview; import android.widget.abslistview.onscrolllistener; import android.widget.listadapter; import android.widget.listview; import android.widget.relativelayout; import android.widget.scroller; import android.widget.textview; import com.example.sirelinkscanapp.r; /** * 自定义listview * * @author cyf * */ public class xlistview extends listview implements onscrolllistener { private final string tag = "xlistview"; private float mlasty = -1; private scroller mscroller; // 滑动 private onscrolllistener mscrolllistener; // 为外界创建监听 private ixlistviewlistener mlistviewlistener; // 刚才定义的自定义控件 private xlistviewheader mheaderview; private relativelayout mheaderviewcontent; private textview mheadertimeview; private int mheaderviewheight; // header view's height private boolean menablepullrefresh = true; private boolean mpullrefreshing = false; // is refreashing. private boolean menablepullload; private boolean mpullloading; private boolean misfooterready = false; // total list items, used to detect is at the bottom of listview. private int mtotalitemcount; // for mscroller, scroll back from header or footer. private int mscrollback; private final static int scrollback_header = 0; private final static int scrollback_footer = 1; private final static int scroll_duration = 400; // scroll back duration private final static int pull_load_more_delta = 50; // when pull up >= 50px // at bottom, trigger // load more. private final static float offset_radio = 1.8f; // support ios like pull // feature. /** * @param context */ public xlistview(context context) { super(context); initwithcontext(context); } public xlistview(context context, attributeset attrs) { super(context, attrs); initwithcontext(context); } public xlistview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); initwithcontext(context); } /** * 初始化控件 * * @param context */ private void initwithcontext(context context) { mscroller = new scroller(context, new decelerateinterpolator()); super.setonscrolllistener(this); mheaderview = new xlistviewheader(context); mheaderviewcontent = (relativelayout) mheaderview .findviewbyid(r.id.xlistview_header_content); mheadertimeview = (textview) mheaderview .findviewbyid(r.id.xlistview_header_time); addheaderview(mheaderview, null, false); mheaderview.getviewtreeobserver().addongloballayoutlistener( new ongloballayoutlistener() { @override public void ongloballayout() { mheaderviewheight = mheaderviewcontent.getheight(); getviewtreeobserver() .removeglobalonlayoutlistener(this); } }); } @override public void setadapter(listadapter adapter) { // make sure xlistviewfooter is the last footer view, and only add once. if (misfooterready == false) { if (menablepullload) { misfooterready = true; // addfooterview(mfooterview); } } super.setadapter(adapter); } public void setpullrefreshenable(boolean enable) { menablepullrefresh = enable; if (!menablepullrefresh) { // disable, hide the content mheaderviewcontent.setvisibility(view.invisible); } else { mheaderviewcontent.setvisibility(view.visible); } } /** * 停止刷新 */ public void stoprefresh() { if (mpullrefreshing == true) { mpullrefreshing = false; resetheaderheight(); } } // 可使进入activity时便执行下拉刷新 public void startrefresh() { if (mpullrefreshing == false) { mpullrefreshing = true; mheaderview.setstate(xlistviewheader.state_refreshing); mheaderview.setvisiableheight(90); if (mlistviewlistener != null) { mlistviewlistener.onrefresh(); } } } /** * stop load more, reset footer view. */ public void stoploadmore() { if (mpullloading == true) { mpullloading = false; } } /** * set last refresh time * * @param time */ public void setrefreshtime(string time) { mheadertimeview.settext(time); } private void invokeonscrolling() { if (mscrolllistener instanceof onxscrolllistener) { onxscrolllistener l = (onxscrolllistener) mscrolllistener; l.onxscrolling(this); } } private void updateheaderheight(float delta) { mheaderview.setvisiableheight((int) delta + mheaderview.getvisiableheight()); if (menablepullrefresh && !mpullrefreshing) { // 未处于刷新状态,更新箭头 if (mheaderview.getvisiableheight() > mheaderviewheight) { mheaderview.setstate(xlistviewheader.state_ready); } else { mheaderview.setstate(xlistviewheader.state_normal); } if (mpullloading) { // disable, hide the content mheaderviewcontent.setvisibility(view.invisible); } else { mheaderviewcontent.setvisibility(view.visible); } } setselection(0); // scroll to top each time } /** * reset header view's height. */ private void resetheaderheight() { int height = mheaderview.getvisiableheight(); if (height == 0) // not visible. return; // refreshing and header isn't shown fully. do nothing. if (mpullrefreshing && height <= mheaderviewheight) { return; } int finalheight = 0; // default: scroll back to dismiss header. // is refreshing, just scroll back to show all the header. if (mpullrefreshing && height > mheaderviewheight) { finalheight = mheaderviewheight; } mscrollback = scrollback_header; mscroller.startscroll(0, height, 0, finalheight - height, scroll_duration); // trigger computescroll invalidate(); } /** * 触屏监听 */ @override public boolean ontouchevent(motionevent ev) { if (mlasty == -1) { mlasty = ev.getrawy(); } switch (ev.getaction()) { case motionevent.action_down: mlasty = ev.getrawy(); break; case motionevent.action_move: final float deltay = ev.getrawy() - mlasty; // dlog.i(tag, "deltay is " + deltay); mlasty = ev.getrawy(); if (getfirstvisibleposition() == 0 && (mheaderview.getvisiableheight() > 0 || deltay > 0)) { // the first item is showing, header has shown or pull down. updateheaderheight(deltay / offset_radio); invokeonscrolling(); } else if (getlastvisibleposition() == mtotalitemcount - 1) { // last item, already pulled up or want to pull up. } break; default: mlasty = -1; // reset if (getfirstvisibleposition() == 0) { // invoke refresh if (!mpullrefreshing && menablepullrefresh && !mpullloading && mheaderview.getvisiableheight() > mheaderviewheight) { mpullrefreshing = true; mheaderview.setstate(xlistviewheader.state_refreshing); // dlog.i(tag, "invoke refresh"); if (mlistviewlistener != null) { // 此时执行刷刷新的方法 mlistviewlistener.onrefresh(); } } resetheaderheight(); } else if (getlastvisibleposition() == mtotalitemcount - 1) { // invoke load more. if (!mpullloading && menablepullload && !mpullrefreshing) { // dlog.i(tag, "invoke load more"); } } break; } return super.ontouchevent(ev); } @override public void computescroll() { if (mscroller.computescrolloffset()) { if (mscrollback == scrollback_header) { mheaderview.setvisiableheight(mscroller.getcurry()); } else { } postinvalidate(); invokeonscrolling(); } super.computescroll(); } @override public void setonscrolllistener(onscrolllistener l) { mscrolllistener = l; } /** * 在滚动时回调,回调2-3次,手指没抛开则回调2次,scrollstate = 2的这次不回调 第1次:scrollstate = * scroll_state_touch_scroll(1) 正在滚动 第2次:scrollstate = * scroll_state_fling(2)手指做了抛的动作(手指离开屏幕前,用力滑了一下) 第3次:scrollstate = * scroll_state_idle(0) 停止滚动 */ @override public void onscrollstatechanged(abslistview view, int scrollstate) { if (mscrolllistener != null) { mscrolllistener.onscrollstatechanged(view, scrollstate); } // 滑到底部时,自动加载更多。 也可以禁用此逻辑 if (getlastvisibleposition() == mtotalitemcount - 1 && scrollstate == scroll_state_idle) { if (!mpullloading && menablepullload && !mpullrefreshing) { // dlog.i(tag, "invoke load more"); } } } /** * 正在滚动的时候回调,停止滚动时才停止回调,单击时回调一次 */ @override public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) { // send to user's listener mtotalitemcount = totalitemcount; if (mscrolllistener != null) { mscrolllistener.onscroll(view, firstvisibleitem, visibleitemcount, totalitemcount); } } public void setxlistviewlistener(ixlistviewlistener l) { mlistviewlistener = l; } /** * 监听listview的滑动事件 * * @author cyf * */ public interface onxscrolllistener extends onscrolllistener { public void onxscrolling(view view); } /** * 自定义接口 */ public interface ixlistviewlistener { // 下拉刷新时候执行的方法 public void onrefresh(); // 上拉加载时候执行的方法 public void onloadmore(); } } </span>
mainactivity的xml文件中的listview就不要用android的listview了,用上面自定义的listview(加包名)
<span style="font-family: comic sans ms,sans-serif; font-size: 16px;">package com.test.andy; import java.util.arraylist; import com.test.andy.view.xlistview; import com.test.andy.view.xlistview.ixlistviewlistener; import com.test.andy.r; import android.app.activity; import android.os.bundle; import android.os.handler; import android.widget.arrayadapter; /** * xlistviewactivity * 实现ixlistviewlistener接口是为了实现其中的下拉刷新等方法 * @author cyf * */ public class xlistviewactivity extends activity implements ixlistviewlistener { //自定义的listview private xlistview mlistview; private arrayadapter<string> madapter; private arraylist<string> items = new arraylist<string>(); private handler mhandler; private int start = 0; private int refreshcnt = 0; /** called when the activity is first created. */ @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); //初始化的时候加载20条数据 geneitems(); mlistview = (xlistview) findviewbyid(r.id.xlistview); mlistview.setpullloadenable(true); madapter = new arrayadapter<string>(this, r.layout.list_item, items); mlistview.setadapter(madapter); // mlistview.setpullrefreshenable(false); mlistview.setxlistviewlistener(this); mhandler = new handler(); mlistview.startrefresh(); } private void geneitems() { for (int i = 0; i != 20; ++i) { items.add("refresh cnt " + (++start)); } } private void onload() { mlistview.stoprefresh(); mlistview.stoploadmore(); mlistview.setrefreshtime("刚刚"); } @override public void onrefresh() { mhandler.postdelayed(new runnable() { @override public void run() { start = ++refreshcnt; items.clear(); geneitems(); // madapter.notifydatasetchanged(); madapter = new arrayadapter<string>(xlistviewactivity.this, r.layout.list_item, items); mlistview.setadapter(madapter); onload(); } }, 2000); } @override public void onloadmore() { mhandler.postdelayed(new runnable() { @override public void run() { geneitems(); madapter.notifydatasetchanged(); onload(); } }, 2000); } } </span>
重要的代码都总结在这了,希望给大家带来帮助,谢谢。
上一篇: Linux 软连接
推荐阅读