GridView基于pulltorefresh实现下拉刷新 上拉加载更多功能(推荐)
程序员文章站
2024-03-01 23:58:10
原理和listview一样 ,都是重写android原生控件
activity
package com.example.refreshgridview;
i...
原理和listview一样 ,都是重写android原生控件
activity
package com.example.refreshgridview; import java.util.arraylist; import java.util.list; import android.app.activity; import android.os.bundle; import android.widget.gridview; import android.widget.toast; import com.example.refreshgridview.pulltorefreshbase.onrefreshlistener; public class mainactivity extends activity { private pulltorefreshgridview mpullrefreshgridview; private gridview mgridview; private gridviewadapter adapter; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mpullrefreshgridview = (pulltorefreshgridview) findviewbyid(r.id.video_gridview); mpullrefreshgridview.init(pulltorefreshgridview.mode_both); mgridview = mpullrefreshgridview.getrefreshableview(); mpullrefreshgridview.setonrefreshlistener(refreshlistener); list<string> list = new arraylist<string>(); for (int i = 0; i < 40; i++) { list.add(i+""); } adapter = new gridviewadapter(mainactivity.this,list); mgridview.setadapter(adapter); } private onrefreshlistener refreshlistener = new onrefreshlistener() { @override public void onrefresh(int mode) { if (pulltorefreshgridview.mode_pull_down_to_refresh == mpullrefreshgridview.getcurrentmode()) { toast.maketext(mainactivity.this, "下拉刷新", toast.length_short).show(); mpullrefreshgridview.onrefreshcomplete(); } else if (mode == pulltorefreshgridview.mode_pull_up_to_refresh) { // 加载更多 toast.maketext(mainactivity.this, "上拉加载更多", toast.length_short).show(); mpullrefreshgridview.onrefreshcomplete(); } } }; }
adapter
package com.example.refreshgridview; import java.util.arraylist; import java.util.list; import android.content.context; import android.content.intent; import android.text.textutils; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import android.widget.textview; public class gridviewadapter extends baseadapter { private list<string> mlist = new arraylist<string>(); private context mcontext; public gridviewadapter(context context,list<string> list) { super(); this.mcontext = context; this.mlist = list; } @override public int getcount() { return mlist.size(); } @override public string getitem(int position) { return mlist.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { childholderone holder; if (convertview == null) { convertview = layoutinflater.from(mcontext).inflate(r.layout.item_grid_live_show, parent, false); holder = new childholderone(); holder.tvtitle = (textview)convertview.findviewbyid(r.id.title_tv); convertview.settag(holder); } else { holder = (childholderone) convertview.gettag(); } return convertview; } class childholderone { textview tvtitle; } }
pulltorefreshgridview
package com.example.refreshgridview; import android.content.context; import android.util.attributeset; import android.view.contextmenu.contextmenuinfo; import android.view.view; import android.widget.gridview; public class pulltorefreshgridview extends pulltorefreshadapterviewbase<gridview> { class internalgridview extends gridview implements emptyviewmethodaccessor { public internalgridview(context context, attributeset attrs) { super(context, attrs); } @override public void setemptyview(view emptyview) { pulltorefreshgridview.this.setemptyview(emptyview); } @override public void setemptyviewinternal(view emptyview) { super.setemptyview(emptyview); } @override public contextmenuinfo getcontextmenuinfo() { return super.getcontextmenuinfo(); } } public pulltorefreshgridview(context context) { super(context); } public pulltorefreshgridview(context context, int mode) { super(context, mode); } public pulltorefreshgridview(context context, attributeset attrs) { super(context, attrs); } @override protected final gridview createrefreshableview(context context, attributeset attrs) { gridview gv = new internalgridview(context, attrs); // use generated id (from res/values/ids.xml) gv.setid(r.id.gridview); return gv; } @override public contextmenuinfo getcontextmenuinfo() { return ((internalgridview) getrefreshableview()).getcontextmenuinfo(); } }
pulltorefreshbase
package com.example.refreshgridview; import android.content.context; import android.os.handler; import android.util.attributeset; import android.view.motionevent; import android.view.view; import android.view.viewconfiguration; import android.view.viewgroup; import android.view.animation.acceleratedecelerateinterpolator; import android.view.animation.interpolator; import android.widget.linearlayout; import android.widget.textview; /** * * @author zlw */ public abstract class pulltorefreshbase<t extends view> extends linearlayout { final class smoothscrollrunnable implements runnable { static final int animation_duration_ms = 190; static final int animation_fps = 1000 / 60; private final interpolator interpolator; private final int scrolltoy; private final int scrollfromy; private final handler handler; private boolean continuerunning = true; private long starttime = -1; private int currenty = -1; public smoothscrollrunnable(handler handler, int fromy, int toy) { this.handler = handler; this.scrollfromy = fromy; this.scrolltoy = toy; this.interpolator = new acceleratedecelerateinterpolator(); } @override public void run() { /** * only set starttime if this is the first time we're starting, else * actually calculate the y delta */ if (starttime == -1) { starttime = system.currenttimemillis(); } else { /** * we do do all calculations in long to reduce software float * calculations. we use 1000 as it gives us good accuracy and * small rounding errors */ long normalizedtime = (1000 * (system.currenttimemillis() - starttime)) / animation_duration_ms; normalizedtime = math.max(math.min(normalizedtime, 1000), 0); final int deltay = math .round((scrollfromy - scrolltoy) * interpolator .getinterpolation(normalizedtime / 1000f)); this.currenty = scrollfromy - deltay; setheaderscroll(currenty); } // if we're not at the target y, keep going... if (continuerunning && scrolltoy != currenty) { handler.postdelayed(this, animation_fps); } } public void stop() { this.continuerunning = false; this.handler.removecallbacks(this); } }; // =========================================================== // constants // =========================================================== static final float friction = 2.0f; static final int pull_to_refresh = 0x0; static final int release_to_refresh = 0x1; static final int refreshing = 0x2; static final int manual_refreshing = 0x3; public static final int mode_pull_down_to_refresh = 0x1; public static final int mode_pull_up_to_refresh = 0x2; public static final int mode_both = 0x3; // =========================================================== // fields // =========================================================== private int touchslop; private float initialmotiony; private float lastmotionx; private float lastmotiony; private boolean isbeingdragged = false; private int state = pull_to_refresh; private int mode = mode_pull_up_to_refresh; private int currentmode; private boolean disablescrollingwhilerefreshing = true; t refreshableview; private boolean ispulltorefreshenabled = true; private loadinglayout headerlayout; private loadinglayout footerlayout; private int headerheight; private final handler handler = new handler(); private onrefreshlistener onrefreshlistener; private smoothscrollrunnable currentsmoothscrollrunnable; // =========================================================== // constructors // =========================================================== public pulltorefreshbase(context context) { super(context); init(context, null); } public pulltorefreshbase(context context, int mode) { super(context); this.mode = mode; init(context, null); } public pulltorefreshbase(context context, attributeset attrs) { super(context, attrs); init(context, attrs); } // =========================================================== // getter & setter // =========================================================== /** * deprecated. use {@link #getrefreshableview()} from now on. * * @deprecated * @return the refreshable view which is currently wrapped */ public final t getadapterview() { return refreshableview; } /** * get the wrapped refreshable view. anything returned here has already been * added to the content view. * * @return the view which is currently wrapped */ public final t getrefreshableview() { return refreshableview; } /** * whether pull-to-refresh is enabled * * @return enabled */ public final boolean ispulltorefreshenabled() { return ispulltorefreshenabled; } /** * returns whether the widget has disabled scrolling on the refreshable view * while refreshing. * * @return true if the widget has disabled scrolling while refreshing */ public final boolean isdisablescrollingwhilerefreshing() { return disablescrollingwhilerefreshing; } /** * returns whether the widget is currently in the refreshing state * * @return true if the widget is currently refreshing */ public final boolean isrefreshing() { return state == refreshing || state == manual_refreshing; } /** * by default the widget disabled scrolling on the refreshable view while * refreshing. this method can change this behaviour. * * @param disablescrollingwhilerefreshing * - true if you want to disable scrolling while refreshing */ public final void setdisablescrollingwhilerefreshing( boolean disablescrollingwhilerefreshing) { this.disablescrollingwhilerefreshing = disablescrollingwhilerefreshing; } /** * mark the current refresh as complete. will reset the ui and hide the * refreshing view */ public final void onrefreshcomplete() { if (state != pull_to_refresh) { resetheader(); if (onshowlayoutlistener != null) { onshowlayoutlistener.ondismiss(); } } } /** * set onrefreshlistener for the widget * * @param listener * - listener to be used when the widget is set to refresh */ public final void setonrefreshlistener(onrefreshlistener listener) { onrefreshlistener = listener; } /** * auto load headerlayout to refresh * * @param listener */ public final void setfirstautopulluptorefresh(onrefreshlistener listener) { setrefreshinginternal(true, mode_pull_down_to_refresh); listener.onrefresh(mode_pull_down_to_refresh); } /** * set refreshlable , default use null * * @param pulllabel * @param releaselabel * @param refreshinglabel */ public void setrefreshlabel(string pulllabel, string releaselabel, string refreshinglabel) { if (pulllabel != null) { setpulllabel(pulllabel); } if (releaselabel != null) { setreleaselabel(releaselabel); } if (refreshinglabel != null) { setrefreshinglabel(refreshinglabel); } } /** * a mutator to enable/disable pull-to-refresh for the current view * * @param enable * whether pull-to-refresh should be used */ public final void setpulltorefreshenabled(boolean enable) { this.ispulltorefreshenabled = enable; } /** * set text to show when the widget is being pulled, and will refresh when * released * * @param releaselabel * - string to display */ private void setreleaselabel(string releaselabel) { if (null != headerlayout) { headerlayout.setreleaselabel(releaselabel); } if (null != footerlayout) { footerlayout.setreleaselabel(releaselabel); } } /** * set text to show when the widget is being pulled * * @param pulllabel * - string to display */ private void setpulllabel(string pulllabel) { if (null != headerlayout) { headerlayout.setpulllabel(pulllabel); } if (null != footerlayout) { footerlayout.setpulllabel(pulllabel); } } /** * set text to show when the widget is refreshing * * @param refreshinglabel * - string to display */ private void setrefreshinglabel(string refreshinglabel) { if (null != headerlayout) { headerlayout.setrefreshinglabel(refreshinglabel); } if (null != footerlayout) { footerlayout.setrefreshinglabel(refreshinglabel); } } public final void setrefreshing() { this.setrefreshing(true); } /** * sets the widget to be in the refresh state. the ui will be updated to * show the 'refreshing' view. * * @param doscroll * - true if you want to force a scroll to the refreshing view. */ public final void setrefreshing(boolean doscroll) { if (!isrefreshing()) { setrefreshinginternal(doscroll); state = manual_refreshing; } } public final boolean haspullfromtop() { return currentmode != mode_pull_up_to_refresh; } // =========================================================== // methods for/from superclass/interfaces // =========================================================== @override public final boolean ontouchevent(motionevent event) { if (!ispulltorefreshenabled) { return false; } if (isrefreshing() && disablescrollingwhilerefreshing) { return true; } if (event.getaction() == motionevent.action_down && event.getedgeflags() != 0) { return false; } switch (event.getaction()) { case motionevent.action_move: { if (isbeingdragged) { if (math.abs(event.gety() - downlocation) > 5 && onshowlayoutlistener != null) { onshowlayoutlistener.onshow(); } lastmotiony = event.gety(); this.pullevent(); return true; } break; } case motionevent.action_down: { if (isreadyforpull()) { downlocation = event.gety(); lastmotiony = initialmotiony = event.gety(); return true; } break; } case motionevent.action_cancel: case motionevent.action_up: { if (isbeingdragged) { isbeingdragged = false; if (state == release_to_refresh && null != onrefreshlistener) { setrefreshinginternal(true); onrefreshlistener.onrefresh(currentmode); } else { smoothscrollto(0); if (onshowlayoutlistener != null) { onshowlayoutlistener.ondismiss(); } } return true; } break; } } return false; } // remeber to down location private float downlocation = 0; @override public final boolean onintercepttouchevent(motionevent event) { if (!ispulltorefreshenabled) { return false; } if (isrefreshing() && disablescrollingwhilerefreshing) { return true; } final int action = event.getaction(); if (action == motionevent.action_cancel || action == motionevent.action_up) { isbeingdragged = false; return false; } if (action != motionevent.action_down && isbeingdragged) { return true; } switch (action) { case motionevent.action_move: { if (isreadyforpull()) { final float y = event.gety(); final float dy = y - lastmotiony; final float ydiff = math.abs(dy); final float xdiff = math.abs(event.getx() - lastmotionx); if (ydiff > touchslop && ydiff > xdiff) { if ((mode == mode_pull_down_to_refresh || mode == mode_both) && dy >= 0.0001f && isreadyforpulldown()) { lastmotiony = y; isbeingdragged = true; if (mode == mode_both) { currentmode = mode_pull_down_to_refresh; } } else if ((mode == mode_pull_up_to_refresh || mode == mode_both) && dy <= 0.0001f && isreadyforpullup()) { lastmotiony = y; isbeingdragged = true; if (mode == mode_both) { currentmode = mode_pull_up_to_refresh; } } } } break; } case motionevent.action_down: { if (isreadyforpull()) { lastmotiony = initialmotiony = event.gety(); lastmotionx = event.getx(); isbeingdragged = false; } break; } case motionevent.action_up: break; } setrefreshlabel(currentmode); return isbeingdragged; } protected void addrefreshableview(context context, t refreshableview) { addview(refreshableview, new linearlayout.layoutparams( layoutparams.fill_parent, 0, 1.0f)); } /** * this is implemented by derived classes to return the created view. if you * need to use a custom view (such as a custom listview), override this * method and return an instance of your custom class. * * be sure to set the id of the view in this method, especially if you're * using a listactivity or listfragment. * * @param context * @param attrs * attributeset from wrapped class. means that anything you * include in the xml layout declaration will be routed to the * created view * @return new instance of the refreshable view */ protected abstract t createrefreshableview(context context, attributeset attrs); public final int getcurrentmode() { return currentmode; } protected final loadinglayout getfooterlayout() { return footerlayout; } protected final loadinglayout getheaderlayout() { return headerlayout; } protected final int getheaderheight() { return headerheight; } protected final int getmode() { return mode; } /** * implemented by derived class to return whether the view is in a state * where the user can pull to refresh by scrolling down. * * @return true if the view is currently the correct state (for example, top * of a listview) */ protected abstract boolean isreadyforpulldown(); /** * implemented by derived class to return whether the view is in a state * where the user can pull to refresh by scrolling up. * * @return true if the view is currently in the correct state (for example, * bottom of a listview) */ protected abstract boolean isreadyforpullup(); // =========================================================== // methods // =========================================================== protected void resetheader() { state = pull_to_refresh; isbeingdragged = false; if (null != headerlayout) { headerlayout.reset(); } if (null != footerlayout) { footerlayout.reset(); } smoothscrollto(0); } /** * unless special requirements to call the method ,default call the method * {@link #setrefreshinginternal(boolean doscroll)} * * @param doscroll * @param mode */ protected void setrefreshinginternal(boolean doscroll, int mode) { state = refreshing; setrefreshlabel(mode); if (null != headerlayout) { headerlayout.refreshing(); } if (doscroll) { smoothscrollto(mode == mode_pull_down_to_refresh ? -headerheight : headerheight); } } /** * set last refresh time * * @param time */ public void setrefreshtime(string time) { textview mheadertimeview = (textview) headerlayout .findviewbyid(r.id.xlistview_header_time); mheadertimeview.settext(time); } public void setrefreshtime(long time){ textview mheadertimeview = (textview) headerlayout .findviewbyid(r.id.xlistview_header_time); mheadertimeview.settext(timeutil.getchattime(time)); } protected void setrefreshinginternal(boolean doscroll) { state = refreshing; setrefreshlabel(currentmode); if (null != footerlayout) { footerlayout.refreshing(); } if (null != headerlayout) { headerlayout.refreshing(); } if (doscroll) { smoothscrollto(currentmode == mode_pull_down_to_refresh ? -headerheight : headerheight); } } private void setrefreshlabel(int mode) { if (mode == mode_pull_down_to_refresh) { setrefreshlabel("涓嬫媺鍒锋柊", "閲婃斁绔嬪嵆鍒锋柊", "姝e湪鍒锋柊"); } if (mode == mode_pull_up_to_refresh) { setrefreshlabel("涓婃媺鑾峰彇鏇村", "鏉惧紑鏄剧ず鏇村", "姝e湪鍔犺浇"); } } protected final void setheaderscroll(int y) { scrollto(0, y); } protected final void smoothscrollto(int y) { if (null != currentsmoothscrollrunnable) { currentsmoothscrollrunnable.stop(); } if (this.getscrolly() != y) { this.currentsmoothscrollrunnable = new smoothscrollrunnable( handler, getscrolly(), y); handler.post(currentsmoothscrollrunnable); } } public void init(int mode) { // loading view strings string pulllabel = context .getstring(r.string.pull_to_refresh_pull_label); string refreshinglabel = context .getstring(r.string.pull_to_refresh_refreshing_label); string releaselabel = context .getstring(r.string.pull_to_refresh_release_label); // add loading views if (mode == mode_pull_down_to_refresh || mode == mode_both) { headerlayout = new loadinglayout(context,mode_pull_down_to_refresh, releaselabel, pulllabel,refreshinglabel); addview(headerlayout, 0, new linearlayout.layoutparams(viewgroup.layoutparams.fill_parent,viewgroup.layoutparams.wrap_content)); measureview(headerlayout); headerheight = headerlayout.getmeasuredheight(); } if (mode == mode_pull_up_to_refresh || mode == mode_both) { footerlayout = new loadinglayout(context, mode_pull_up_to_refresh,releaselabel, pulllabel, refreshinglabel); addview(footerlayout, new linearlayout.layoutparams( viewgroup.layoutparams.fill_parent, viewgroup.layoutparams.wrap_content)); measureview(footerlayout); headerheight = footerlayout.getmeasuredheight(); } // styleables from xml if (null != headerlayout) { // headerlayout.settextcolor(color.black); } if (null != footerlayout) { // footerlayout.settextcolor(color.black); } // hide loading views switch (mode) { case mode_both: setpadding(0, -headerheight, 0, -headerheight); break; case mode_pull_up_to_refresh: setpadding(0, 0, 0, -headerheight); break; case mode_pull_down_to_refresh: default: setpadding(0, -headerheight, 0, 0); break; } // if we're not using mode_both, then just set currentmode to current // mode if (mode != mode_both) { currentmode = mode; } this.mode = mode; } private void init(context context, attributeset attrs) { this.context = context; setorientation(linearlayout.vertical); touchslop = viewconfiguration.gettouchslop(); // refreshable view // by passing the attrs, we can add listview/gridview params via xml refreshableview = this.createrefreshableview(context, attrs); this.addrefreshableview(context, refreshableview); } private context context; private void measureview(view child) { viewgroup.layoutparams p = child.getlayoutparams(); if (p == null) { p = new viewgroup.layoutparams(viewgroup.layoutparams.fill_parent, viewgroup.layoutparams.wrap_content); } int childwidthspec = viewgroup.getchildmeasurespec(0, 0 + 0, p.width); int lpheight = p.height; int childheightspec; if (lpheight > 0) { childheightspec = measurespec.makemeasurespec(lpheight, measurespec.exactly); } else { childheightspec = measurespec.makemeasurespec(0, measurespec.unspecified); } child.measure(childwidthspec, childheightspec); } /** * actions a pull event * * @return true if the event has been handled, false if there has been no * change */ private boolean pullevent() { final int newheight; final int oldheight = this.getscrolly(); switch (currentmode) { case mode_pull_up_to_refresh: newheight = math.round(math.max(initialmotiony - lastmotiony, 0) / friction); // newheight = math.round((initialmotiony - lastmotiony) / // friction); break; case mode_pull_down_to_refresh: default: newheight = math.round(math.min(initialmotiony - lastmotiony, 0) / friction); // newheight = math.round((initialmotiony - lastmotiony) / // friction); break; } setheaderscroll(newheight); if (newheight != 0) { if (state == pull_to_refresh && headerheight < math.abs(newheight)) { state = release_to_refresh; switch (currentmode) { case mode_pull_up_to_refresh: footerlayout.releasetorefresh(); break; case mode_pull_down_to_refresh: headerlayout.releasetorefresh(); break; } return true; } else if (state == release_to_refresh && headerheight >= math.abs(newheight)) { state = pull_to_refresh; switch (currentmode) { case mode_pull_up_to_refresh: footerlayout.pulltorefresh(); break; case mode_pull_down_to_refresh: headerlayout.pulltorefresh(); break; } return true; } } return oldheight != newheight; } private boolean isreadyforpull() { switch (mode) { case mode_pull_down_to_refresh: return isreadyforpulldown(); case mode_pull_up_to_refresh: return isreadyforpullup(); case mode_both: return isreadyforpullup() || isreadyforpulldown(); } return false; } // =========================================================== // inner and anonymous classes // =========================================================== public static interface onrefreshlistener { public void onrefresh(int mode); } private onshowlayoutlistener onshowlayoutlistener; public void setonshowlayoutlistener(onshowlayoutlistener listener) { this.onshowlayoutlistener = listener; } public static interface onshowlayoutlistener { /** * 鏄惁姝e湪鏄剧ず搴曢儴甯冨眬 */ public void onshow(); /** * 鏄惁娑堝け */ public void ondismiss(); } public static interface onlastitemvisiblelistener { public void onlastitemvisible(); } @override public void setlongclickable(boolean longclickable) { getrefreshableview().setlongclickable(longclickable); } }
主要代码都在上面贴出来了,当然还是一定要有demo
demo源码下载
这个源码里面 我把 pulltorefreshexpandablelistview 和 pulltorefreshlistview 也都放进来了,一样的逻辑。希望对大家有用。如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
推荐阅读
-
GridView基于pulltorefresh实现下拉刷新 上拉加载更多功能(推荐)
-
Android ListView实现上拉加载更多和下拉刷新功能
-
GridView基于pulltorefresh实现下拉刷新 上拉加载更多功能(推荐)
-
Android自定义listview布局实现上拉加载下拉刷新功能
-
Android ListView实现上拉加载下拉刷新和滑动删除功能
-
android使用PullToRefresh实现下拉刷新和上拉加载
-
Android中Listview下拉刷新和上拉加载更多的多种实现方案
-
Android ListView实现上拉加载下拉刷新和滑动删除功能
-
android使用PullToRefresh实现下拉刷新和上拉加载
-
Android中Listview下拉刷新和上拉加载更多的多种实现方案