当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义控件开发实战之实现ListView下拉刷新实例代码

Android自定义控件开发实战之实现ListView下拉刷新实例代码

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

这篇博客为大家介绍一个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> 

重要的代码都总结在这了,希望给大家带来帮助,谢谢。

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

相关文章:

验证码:
移动技术网