前面写过一篇关于下拉刷新控件的文章下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能。不仅如此,我已经把它改成了对所有VIEw都通用!可以随心所欲使用这两个功能~~
我做了一个大集合的demo,实现了ListVIEw、GrIDVIEw、ExpandableListVIEw、ScrollVIEw、WebVIEw、ImageVIEw、TextVIEw的下拉刷新和上拉加载。后面会提供demo的下载地址,最新代码已上传到github:https://github.com/jingchenUSTC/PullToRefreshAndLoad
依照惯例,下面将会是一大波效果图:
demo首页也是可下拉的ListVIEw,在底下可以加入table:
ListVIEw:
GrIDVIEw:
ExpandableListVIEw:
ScrollVIEw:
WebVIEw:
ImageVIEw:
TextVIEw:
很不错吧?最后的ImageVIEw和TextVIEw是最简单的,直接在下面的接口方法里返回true。
增加上拉加载很简单,和管理下拉头一样,再多管理一个上拉头,也不费事;至于把它改成通用的就需要统一一下VIEw的行为了,为此,我定义了这样一个接口:
package com.jingchen.pulltorefresh.pullablevIEw;
public interface Pullable
{
/**
* 判断是否可以下拉,如果不需要下拉功能可以直接return false
*
* @return true如果可以下拉否则返回false
*/
boolean canPullDown();
/**
* 判断是否可以上拉,如果不需要上拉功能可以直接return false
*
* @return true如果可以上拉否则返回false
*/
boolean canPullUp();
}
从接口名就可以看出它是一个提供判断是否可拉的方法的接口。这个接口的两个方法,canPullDown()是判断何时可以下拉的方法,canPullUp()则是判断何时可以上拉,我在demo中的判断是滑到顶部的时候可以下拉,滑到底部的时候可以上拉。所有需要上拉和下拉的VIEw都需要实现这个接口。后面会给出一些VIEw的实现。先来看看改进后的自定义的布局PullToRefreshLayout,增加了一个上拉头,下拉头和上拉头之间的VIEw是实现了Pullable接口的pullableVIEw。相比前面的版本,这里有改动的需要注意的地方如下:
1、增加了上拉头,相应的也增加了控制变量。
2、拉动时消除content_vIEw事件防止误触发不再使用反射,直接设置 event.setAction(MotionEvent.ACTION_CANCEL)。
3、消除了拉动过程中的多点触碰导致的剧变。
4、不再设置content_vIEw的onTouListener,让使用者可以更加自由的设置监听器。
这个PullToRefreshLayout只负责管理三个控件,如果一个VIEw需要有上拉下拉功能则只需实现接口就行了。下面看PullToRefreshLayout的代码,注释写了好多:
package com.jingchen.pulltorefresh; import java.util.Timer; import java.util.TimerTask; import androID.content.Context; import androID.graphics.Canvas; import androID.graphics.linearGradIEnt; import androID.graphics.Paint; import androID.graphics.Paint.Style; import androID.graphics.RectF; import androID.graphics.Shader.TileMode; import androID.os.Handler; import androID.os.Message; import androID.util.AttributeSet; import androID.util.Log; import androID.vIEw.MotionEvent; import androID.vIEw.VIEw; import androID.vIEw.VIEwGroup; import androID.vIEw.animation.AnimationUtils; import androID.vIEw.animation.linearInterpolator; import androID.vIEw.animation.RotateAnimation; import androID.Widget.relativeLayout; import androID.Widget.TextVIEw; import com.jingchen.pulltorefresh.pullablevIEw.Pullable; /** * 自定义的布局,用来管理三个子控件,其中一个是下拉头,一个是包含内容的pullableVIEw(可以是实现Pullable接口的的任何VIEw), * 还有一个上拉头 * * @author 陈靖 */ public class PullToRefreshLayout extends relativeLayout { public static final String TAG = "PullToRefreshLayout"; // 初始状态 public static final int INIT = 0; // 释放刷新 public static final int RELEASE_TO_REFRESH = 1; // 正在刷新 public static final int REFRESHING = 2; // 释放加载 public static final int RELEASE_TO_LOAD = 3; // 正在加载 public static final int LOADING = 4; // *** 作完毕 public static final int DONE = 5; // 当前状态 private int state = INIT; // 刷新回调接口 private OnRefreshListener mListener; // 刷新成功 public static final int SUCCEED = 0; // 刷新失败 public static final int FAIL = 1; // 按下Y坐标,上一个事件点Y坐标 private float downY,lastY; // 下拉的距离。注意:pullDownY和pullUpY不可能同时不为0 public float pullDownY = 0; // 上拉的距离 private float pullUpY = 0; // 释放刷新的距离 private float refreshdist = 200; // 释放加载的距离 private float loadmoredist = 200; private MyTimer timer; // 回滚速度 public float MOVE_SPEED = 8; // 第一次执行布局 private boolean isLayout = false; // 在刷新过程中滑动 *** 作 private boolean istouch = false; // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化 private float radio = 2; // 下拉箭头的转180°动画 private RotateAnimation rotateAnimation; // 均匀旋转动画 private RotateAnimation refreshingAnimation; // 下拉头 private VIEw refreshVIEw; // 下拉的箭头 private VIEw pullVIEw; // 正在刷新的图标 private VIEw refreshingVIEw; // 刷新结果图标 private VIEw refreshStateImageVIEw; // 刷新结果:成功或失败 private TextVIEw refreshStateTextVIEw; // 上拉头 private VIEw loadmoreVIEw; // 上拉的箭头 private VIEw pullUpVIEw; // 正在加载的图标 private VIEw loadingVIEw; // 加载结果图标 private VIEw loadStateImageVIEw; // 加载结果:成功或失败 private TextVIEw loadStateTextVIEw; // 实现了Pullable接口的VIEw private VIEw pullableVIEw; // 过滤多点触碰 private int mEvents; // 这两个变量用来控制pull的方向,如果不加控制,当情况满足可上拉又可下拉时没法下拉 private boolean canPullDown = true; private boolean canPullUp = true; /** * 执行自动回滚的handler */ Handler updateHandler = new Handler() { @OverrIDe public voID handleMessage(Message msg) { // 回d速度随下拉距离moveDeltaY增大而增大 MOVE_SPEED = (float) (8 + 5 * Math.tan(Math.PI / 2 / getMeasuredHeight() * (pullDownY + Math.abs(pullUpY)))); if (!istouch) { // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..." if (state == REFRESHING && pullDownY <= refreshdist) { pullDownY = refreshdist; timer.cancel(); } else if (state == LOADING && -pullUpY <= loadmoredist) { pullUpY = -loadmoredist; timer.cancel(); } } if (pullDownY > 0) pullDownY -= MOVE_SPEED; else if (pullUpY < 0) pullUpY += MOVE_SPEED; if (pullDownY < 0) { // 已完成回d pullDownY = 0; pullVIEw.clearanimation(); // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态 if (state != REFRESHING && state != LOADING) changeState(INIT); timer.cancel(); } if (pullUpY > 0) { // 已完成回d pullUpY = 0; pullUpVIEw.clearanimation(); // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态 if (state != REFRESHING && state != LOADING) changeState(INIT); timer.cancel(); } // 刷新布局,会自动调用onLayout requestLayout(); } }; public voID setonRefreshListener(OnRefreshListener Listener) { mListener = Listener; } public PullToRefreshLayout(Context context) { super(context); initVIEw(context); } public PullToRefreshLayout(Context context,AttributeSet attrs) { super(context,attrs); initVIEw(context); } public PullToRefreshLayout(Context context,AttributeSet attrs,int defStyle) { super(context,attrs,defStyle); initVIEw(context); } private voID initVIEw(Context context) { timer = new MyTimer(updateHandler); rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation( context,R.anim.reverse_anim); refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation( context,R.anim.rotating); // 添加匀速转动动画 linearInterpolator lir = new linearInterpolator(); rotateAnimation.setInterpolator(lir); refreshingAnimation.setInterpolator(lir); } private voID hIDe() { timer.schedule(5); } /** * 完成刷新 *** 作,显示刷新结果。注意:刷新完成后一定要调用这个方法 */ /** * @param refreshResult * PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败 */ public voID refreshFinish(int refreshResult) { refreshingVIEw.clearanimation(); refreshingVIEw.setVisibility(VIEw.GONE); switch (refreshResult) { case SUCCEED: // 刷新成功 refreshStateImageVIEw.setVisibility(VIEw.VISIBLE); refreshStateTextVIEw.setText(R.string.refresh_succeed); refreshStateImageVIEw .setBackgroundResource(R.drawable.refresh_succeed); break; case FAIL: default: // 刷新失败 refreshStateImageVIEw.setVisibility(VIEw.VISIBLE); refreshStateTextVIEw.setText(R.string.refresh_fail); refreshStateImageVIEw .setBackgroundResource(R.drawable.refresh_Failed); break; } // 刷新结果停留1秒 new Handler() { @OverrIDe public voID handleMessage(Message msg) { changeState(DONE); hIDe(); } }.sendEmptyMessageDelayed(0,1000); } /** * 加载完毕,显示加载结果。注意:加载完成后一定要调用这个方法 * * @param refreshResult * PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败 */ public voID loadmoreFinish(int refreshResult) { loadingVIEw.clearanimation(); loadingVIEw.setVisibility(VIEw.GONE); switch (refreshResult) { case SUCCEED: // 加载成功 loadStateImageVIEw.setVisibility(VIEw.VISIBLE); loadStateTextVIEw.setText(R.string.load_succeed); loadStateImageVIEw.setBackgroundResource(R.drawable.load_succeed); break; case FAIL: default: // 加载失败 loadStateImageVIEw.setVisibility(VIEw.VISIBLE); loadStateTextVIEw.setText(R.string.load_fail); loadStateImageVIEw.setBackgroundResource(R.drawable.load_Failed); break; } // 刷新结果停留1秒 new Handler() { @OverrIDe public voID handleMessage(Message msg) { changeState(DONE); hIDe(); } }.sendEmptyMessageDelayed(0,1000); } private voID changeState(int to) { state = to; switch (state) { case INIT: // 下拉布局初始状态 refreshStateImageVIEw.setVisibility(VIEw.GONE); refreshStateTextVIEw.setText(R.string.pull_to_refresh); pullVIEw.clearanimation(); pullVIEw.setVisibility(VIEw.VISIBLE); // 上拉布局初始状态 loadStateImageVIEw.setVisibility(VIEw.GONE); loadStateTextVIEw.setText(R.string.pullup_to_load); pullUpVIEw.clearanimation(); pullUpVIEw.setVisibility(VIEw.VISIBLE); break; case RELEASE_TO_REFRESH: // 释放刷新状态 refreshStateTextVIEw.setText(R.string.release_to_refresh); pullVIEw.startAnimation(rotateAnimation); break; case REFRESHING: // 正在刷新状态 pullVIEw.clearanimation(); refreshingVIEw.setVisibility(VIEw.VISIBLE); pullVIEw.setVisibility(VIEw.INVISIBLE); refreshingVIEw.startAnimation(refreshingAnimation); refreshStateTextVIEw.setText(R.string.refreshing); break; case RELEASE_TO_LOAD: // 释放加载状态 loadStateTextVIEw.setText(R.string.release_to_load); pullUpVIEw.startAnimation(rotateAnimation); break; case LOADING: // 正在加载状态 pullUpVIEw.clearanimation(); loadingVIEw.setVisibility(VIEw.VISIBLE); pullUpVIEw.setVisibility(VIEw.INVISIBLE); loadingVIEw.startAnimation(refreshingAnimation); loadStateTextVIEw.setText(R.string.loading); break; case DONE: // 刷新或加载完毕,啥都不做 break; } } /** * 不限制上拉或下拉 */ private voID releasePull() { canPullDown = true; canPullUp = true; } /* * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突 * * @see androID.vIEw.VIEwGroup#dispatchtouchEvent(androID.vIEw.MotionEvent) */ @OverrIDe public boolean dispatchtouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: downY = ev.getY(); lastY = downY; timer.cancel(); mEvents = 0; releasePull(); break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: // 过滤多点触碰 mEvents = -1; break; case MotionEvent.ACTION_MOVE: if (mEvents == 0) { if (((Pullable) pullableVIEw).canPullDown() && canPullDown && state != LOADING) { // 可以下拉,正在加载时不能下拉 // 对实际滑动距离做缩小,造成用力拉的感觉 pullDownY = pullDownY + (ev.getY() - lastY) / radio; if (pullDownY < 0) { pullDownY = 0; canPullDown = false; canPullUp = true; } if (pullDownY > getMeasuredHeight()) pullDownY = getMeasuredHeight(); if (state == REFRESHING) { // 正在刷新的时候触摸移动 istouch = true; } } else if (((Pullable) pullableVIEw).canPullUp() && canPullUp && state != REFRESHING) { // 可以上拉,正在刷新时不能上拉 pullUpY = pullUpY + (ev.getY() - lastY) / radio; if (pullUpY > 0) { pullUpY = 0; canPullDown = true; canPullUp = false; } if (pullUpY < -getMeasuredHeight()) pullUpY = -getMeasuredHeight(); if (state == LOADING) { // 正在加载的时候触摸移动 istouch = true; } } else releasePull(); } else mEvents = 0; lastY = ev.getY(); // 根据下拉距离改变比例 radio = (float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight() * (pullDownY + Math.abs(pullUpY)))); requestLayout(); if (pullDownY <= refreshdist <span > </span>&& (state == RELEASE_TO_REFRESH || state == DONE)) { <span > </span>// 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新 <span > </span>changeState(INIT); <span > </span>} <span > </span>if (pullDownY >= refreshdist && (state == INIT || state == DONE)) { <span > </span>// 如果下拉距离达到刷新的距离且当前状态是初始状态刷新,改变状态为释放刷新 <span > </span>changeState(RELEASE_TO_REFRESH); <span > </span>} <span > </span>// 下面是判断上拉加载的,同上,注意pullUpY是负值 <span > </span>if (-pullUpY <= loadmoredist <span > </span>&& (state == RELEASE_TO_LOAD || state == DONE)) { <span > </span>changeState(INIT); <span > </span>} <span > </span>if (-pullUpY >= loadmoredist && (state == INIT || state == DONE)) { <span > </span>changeState(RELEASE_TO_LOAD); <span > </span>} // 因为刷新和加载 *** 作不能同时进行,所以pullDownY和pullUpY不会同时不为0,因此这里用(pullDownY + // Math.abs(pullUpY))就可以不对当前状态作区分了 if ((pullDownY + Math.abs(pullUpY)) > 8) { // 防止下拉过程中误触发长按事件和点击事件 ev.setAction(MotionEvent.ACTION_CANCEL); } break; case MotionEvent.ACTION_UP: if (pullDownY > refreshdist || -pullUpY > loadmoredist) // 正在刷新时往下拉(正在加载时往上拉),释放后下拉头(上拉头)不隐藏 istouch = false; if (state == RELEASE_TO_REFRESH) { changeState(REFRESHING); // 刷新 *** 作 if (mListener != null) mListener.onRefresh(this); } else if (state == RELEASE_TO_LOAD) { changeState(LOADING); // 加载 *** 作 if (mListener != null) mListener.onLoadMore(this); } hIDe(); default: break; } // 事件分发交给父类 super.dispatchtouchEvent(ev); return true; } private voID initVIEw() { // 初始化下拉布局 pullVIEw = refreshVIEw.findVIEwByID(R.ID.pull_icon); refreshStateTextVIEw = (TextVIEw) refreshVIEw .findVIEwByID(R.ID.state_tv); refreshingVIEw = refreshVIEw.findVIEwByID(R.ID.refreshing_icon); refreshStateImageVIEw = refreshVIEw.findVIEwByID(R.ID.state_iv); // 初始化上拉布局 pullUpVIEw = loadmoreVIEw.findVIEwByID(R.ID.pullup_icon); loadStateTextVIEw = (TextVIEw) loadmoreVIEw .findVIEwByID(R.ID.loadstate_tv); loadingVIEw = loadmoreVIEw.findVIEwByID(R.ID.loading_icon); loadStateImageVIEw = loadmoreVIEw.findVIEwByID(R.ID.loadstate_iv); } @OverrIDe protected voID onLayout(boolean changed,int l,int t,int r,int b) { if (!isLayout) { // 这里是第一次进来的时候做一些初始化 refreshVIEw = getChildAt(0); pullableVIEw = getChildAt(1); loadmoreVIEw = getChildAt(2); isLayout = true; initVIEw(); refreshdist = ((VIEwGroup) refreshVIEw).getChildAt(0) .getMeasuredHeight(); loadmoredist = ((VIEwGroup) loadmoreVIEw).getChildAt(0) .getMeasuredHeight(); } // 改变子控件的布局,这里直接用(pullDownY + pullUpY)作为偏移量,这样就可以不对当前状态作区分 refreshVIEw.layout(0,(int) (pullDownY + pullUpY) - refreshVIEw.getMeasuredHeight(),refreshVIEw.getMeasureDWIDth(),(int) (pullDownY + pullUpY)); pullableVIEw.layout(0,(int) (pullDownY + pullUpY),pullableVIEw.getMeasureDWIDth(),(int) (pullDownY + pullUpY) + pullableVIEw.getMeasuredHeight()); loadmoreVIEw.layout(0,(int) (pullDownY + pullUpY) + pullableVIEw.getMeasuredHeight(),loadmoreVIEw.getMeasureDWIDth(),(int) (pullDownY + pullUpY) + pullableVIEw.getMeasuredHeight() + loadmoreVIEw.getMeasuredHeight()); } class MyTimer { private Handler handler; private Timer timer; private MyTask mTask; public MyTimer(Handler handler) { this.handler = handler; timer = new Timer(); } public voID schedule(long period) { if (mTask != null) { mTask.cancel(); mTask = null; } mTask = new MyTask(handler); timer.schedule(mTask,period); } public voID cancel() { if (mTask != null) { mTask.cancel(); mTask = null; } } class MyTask extends TimerTask { private Handler handler; public MyTask(Handler handler) { this.handler = handler; } @OverrIDe public voID run() { handler.obtainMessage().sendToTarget(); } } } /** * 刷新加载回调接口 * * @author chenjing * */ public interface OnRefreshListener { /** * 刷新 *** 作 */ voID onRefresh(PullToRefreshLayout pullToRefreshLayout); /** * 加载 *** 作 */ voID onLoadMore(PullToRefreshLayout pullToRefreshLayout); } }
上面就是整个布局的代码,并不是很难。
下面看各个VIEw对Pullable接口的实现,ListVIEw和GrIDVIEw还有ExpandableListVIEw的判断方法是一样的:
PullableListVIEw:
package com.jingchen.pulltorefresh.pullablevIEw; import androID.content.Context; import androID.util.AttributeSet; import androID.util.Log; import androID.Widget.ListVIEw; public class PullableListVIEw extends ListVIEw implements Pullable { public PullableListVIEw(Context context) { super(context); } public PullableListVIEw(Context context,attrs); } public PullableListVIEw(Context context,defStyle); } @OverrIDe public boolean canPullDown() { if (getCount() == 0) { // 没有item的时候也可以下拉刷新 return true; } else if (getFirstVisibleposition() == 0 && getChildAt(0).gettop() >= 0) { // 滑到ListVIEw的顶部了 return true; } else return false; } @OverrIDe public boolean canPullUp() { if (getCount() == 0) { // 没有item的时候也可以上拉加载 return true; } else if (getLastVisibleposition() == (getCount() - 1)) { // 滑到底部了 if (getChildAt(getLastVisibleposition() - getFirstVisibleposition()) != null && getChildAt( getLastVisibleposition() - getFirstVisibleposition()).getBottom() <= getMeasuredHeight()) return true; } return false; } }
PullableExpandableListVIEw:
package com.jingchen.pulltorefresh.pullablevIEw; import androID.content.Context; import androID.util.AttributeSet; import androID.Widget.ExpandableListVIEw; public class PullableExpandableListVIEw extends ExpandableListVIEw implements Pullable { public PullableExpandableListVIEw(Context context) { super(context); } public PullableExpandableListVIEw(Context context,attrs); } public PullableExpandableListVIEw(Context context,defStyle); } @OverrIDe public boolean canPullDown() { if (getCount() == 0) { // 没有item的时候也可以下拉刷新 return true; } else if (getFirstVisibleposition() == 0 && getChildAt(0).gettop() >= 0) { // 滑到顶部了 return true; } else return false; } @OverrIDe public boolean canPullUp() { if (getCount() == 0) { // 没有item的时候也可以上拉加载 return true; } else if (getLastVisibleposition() == (getCount() - 1)) { // 滑到底部了 if (getChildAt(getLastVisibleposition() - getFirstVisibleposition()) != null && getChildAt( getLastVisibleposition() - getFirstVisibleposition()).getBottom() <= getMeasuredHeight()) return true; } return false; } }
PullableGrIDVIEw:
package com.jingchen.pulltorefresh.pullablevIEw; import androID.content.Context; import androID.util.AttributeSet; import androID.Widget.GrIDVIEw; public class PullableGrIDVIEw extends GrIDVIEw implements Pullable { public PullableGrIDVIEw(Context context) { super(context); } public PullableGrIDVIEw(Context context,attrs); } public PullableGrIDVIEw(Context context,defStyle); } @OverrIDe public boolean canPullDown() { if (getCount() == 0) { // 没有item的时候也可以下拉刷新 return true; } else if (getFirstVisibleposition() == 0 && getChildAt(0).gettop() >= 0) { // 滑到顶部了 return true; } else return false; } @OverrIDe public boolean canPullUp() { if (getCount() == 0) { // 没有item的时候也可以上拉加载 return true; } else if (getLastVisibleposition() == (getCount() - 1)) { // 滑到底部了 if (getChildAt(getLastVisibleposition() - getFirstVisibleposition()) != null && getChildAt( getLastVisibleposition() - getFirstVisibleposition()).getBottom() <= getMeasuredHeight()) return true; } return false; } }
PullableScrollVIEw:
package com.jingchen.pulltorefresh.pullablevIEw; import androID.content.Context; import androID.util.AttributeSet; import androID.Widget.ScrollVIEw; public class PullableScrollVIEw extends ScrollVIEw implements Pullable { public PullableScrollVIEw(Context context) { super(context); } public PullableScrollVIEw(Context context,attrs); } public PullableScrollVIEw(Context context,defStyle); } @OverrIDe public boolean canPullDown() { if (getScrollY() == 0) return true; else return false; } @OverrIDe public boolean canPullUp() { if (getScrollY() >= (getChildAt(0).getHeight() - getMeasuredHeight())) return true; else return false; } }
PullableWebVIEw:
package com.jingchen.pulltorefresh.pullablevIEw; import androID.content.Context; import androID.util.AttributeSet; import androID.webkit.WebVIEw; public class PullableWebVIEw extends WebVIEw implements Pullable { public PullableWebVIEw(Context context) { super(context); } public PullableWebVIEw(Context context,attrs); } public PullableWebVIEw(Context context,defStyle); } @OverrIDe public boolean canPullDown() { if (getScrollY() == 0) return true; else return false; } @OverrIDe public boolean canPullUp() { if (getScrollY() >= getContentHeight() * getScale() - getMeasuredHeight()) return true; else return false; } }
ImageVIEw和TextVIEw就不贴了,我直接在方法里返回了true。
源码下载:通用版下拉刷新上拉加载大合集demo
精彩专题分享:javascriptAndroid加载功能汇总
本文已经被整理到《Android下拉刷新上拉加载效果》,欢迎大家学习研究。
希望本文所述对大家学习AndroID下拉刷新上拉加载控件有所帮助。
总结以上是内存溢出为你收集整理的Android下拉刷新上拉加载控件(适用于所有View)全部内容,希望文章能够帮你解决Android下拉刷新上拉加载控件(适用于所有View)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)