Android下拉刷新上拉加载控件,对所有View通用!

这个是我目前用的比较不错的Veiw刷新的Demo 推荐给大家,这里索性把原文评论一起粘贴过来方便大家学习 下面有原文链接


  转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38868463

     前面写过一篇关于下拉刷新控件的博客下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能。不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~

    我做了一个大集合的demo,实现了ListView、GridView、ExpandableListView、ScrollView、WebView、ImageView、TextView的下拉刷新和上拉加载。后面会提供demo的下载地址。(csdn上的demo有小bug,最新代码已上传到github:https://github.com/jingchenUSTC/PullToRefreshAndLoad)

    依照惯例,下面将会是一大波效果图:

demo首页也是可下拉的ListView,在底下可以加入table:

                                            

ListView:

                

GridView:

                

ExpandableListView:

                

ScrollView:

                

WebView:

                

ImageView:

                

TextView:

                

    很不错吧?最后的ImageView和TextView是最简单的,直接在下面的接口方法里返回true。

    增加上拉加载很简单,和管理下拉头一样,再多管理一个上拉头,也不费事;至于把它改成通用的就需要统一一下View的行为了,为此,我定义了这样一个接口:

[java]  view plain  copy
  1. package com.jingchen.pulltorefresh.pullableview;  
  2.   
  3. public interface Pullable  
  4. {  
  5.     /** 
  6.      * 判断是否可以下拉,如果不需要下拉功能可以直接return false 
  7.      *  
  8.      * @return true如果可以下拉否则返回false 
  9.      */  
  10.     boolean canPullDown();  
  11.   
  12.     /** 
  13.      * 判断是否可以上拉,如果不需要上拉功能可以直接return false 
  14.      *  
  15.      * @return true如果可以上拉否则返回false 
  16.      */  
  17.     boolean canPullUp();  
  18. }  
从接口名就可以看出它是一个提供判断是否可拉的方法的接口。这个接口的两个方法,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的代码,注释写了好多:

[java]  view plain  copy
  1. package com.jingchen.pulltorefresh;  
  2.   
  3. import java.util.Timer;  
  4. import java.util.TimerTask;  
  5.   
  6. import android.content.Context;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.LinearGradient;  
  9. import android.graphics.Paint;  
  10. import android.graphics.Paint.Style;  
  11. import android.graphics.RectF;  
  12. import android.graphics.Shader.TileMode;  
  13. import android.os.Handler;  
  14. import android.os.Message;  
  15. import android.util.AttributeSet;  
  16. import android.util.Log;  
  17. import android.view.MotionEvent;  
  18. import android.view.View;  
  19. import android.view.ViewGroup;  
  20. import android.view.animation.AnimationUtils;  
  21. import android.view.animation.LinearInterpolator;  
  22. import android.view.animation.RotateAnimation;  
  23. import android.widget.RelativeLayout;  
  24. import android.widget.TextView;  
  25.   
  26. import com.jingchen.pulltorefresh.pullableview.Pullable;  
  27.   
  28. /** 
  29.  * 自定义的布局,用来管理三个子控件,其中一个是下拉头,一个是包含内容的pullableView(可以是实现Pullable接口的的任何View), 
  30.  * 还有一个上拉头 
  31.  *  
  32.  * @author 陈靖 
  33.  */  
  34. public class PullToRefreshLayout extends RelativeLayout  
  35. {  
  36.     public static final String TAG = "PullToRefreshLayout";  
  37.     // 初始状态  
  38.     public static final int INIT = 0;  
  39.     // 释放刷新  
  40.     public static final int RELEASE_TO_REFRESH = 1;  
  41.     // 正在刷新  
  42.     public static final int REFRESHING = 2;  
  43.     // 释放加载  
  44.     public static final int RELEASE_TO_LOAD = 3;  
  45.     // 正在加载  
  46.     public static final int LOADING = 4;  
  47.     // 操作完毕  
  48.     public static final int DONE = 5;  
  49.     // 当前状态  
  50.     private int state = INIT;  
  51.     // 刷新回调接口  
  52.     private OnRefreshListener mListener;  
  53.     // 刷新成功  
  54.     public static final int SUCCEED = 0;  
  55.     // 刷新失败  
  56.     public static final int FAIL = 1;  
  57.     // 按下Y坐标,上一个事件点Y坐标  
  58.     private float downY, lastY;  
  59.   
  60.     // 下拉的距离。注意:pullDownY和pullUpY不可能同时不为0  
  61.     public float pullDownY = 0;  
  62.     // 上拉的距离  
  63.     private float pullUpY = 0;  
  64.   
  65.     // 释放刷新的距离  
  66.     private float refreshDist = 200;  
  67.     // 释放加载的距离  
  68.     private float loadmoreDist = 200;  
  69.   
  70.     private MyTimer timer;  
  71.     // 回滚速度  
  72.     public float MOVE_SPEED = 8;  
  73.     // 第一次执行布局  
  74.     private boolean isLayout = false;  
  75.     // 在刷新过程中滑动操作  
  76.     private boolean isTouch = false;  
  77.     // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化  
  78.     private float radio = 2;  
  79.   
  80.     // 下拉箭头的转180°动画  
  81.     private RotateAnimation rotateAnimation;  
  82.     // 均匀旋转动画  
  83.     private RotateAnimation refreshingAnimation;  
  84.   
  85.     // 下拉头  
  86.     private View refreshView;  
  87.     // 下拉的箭头  
  88.     private View pullView;  
  89.     // 正在刷新的图标  
  90.     private View refreshingView;  
  91.     // 刷新结果图标  
  92.     private View refreshStateImageView;  
  93.     // 刷新结果:成功或失败  
  94.     private TextView refreshStateTextView;  
  95.   
  96.     // 上拉头  
  97.     private View loadmoreView;  
  98.     // 上拉的箭头  
  99.     private View pullUpView;  
  100.     // 正在加载的图标  
  101.     private View loadingView;  
  102.     // 加载结果图标  
  103.     private View loadStateImageView;  
  104.     // 加载结果:成功或失败  
  105.     private TextView loadStateTextView;  
  106.   
  107.     // 实现了Pullable接口的View  
  108.     private View pullableView;  
  109.     // 过滤多点触碰  
  110.     private int mEvents;  
  111.     // 这两个变量用来控制pull的方向,如果不加控制,当情况满足可上拉又可下拉时没法下拉  
  112.     private boolean canPullDown = true;  
  113.     private boolean canPullUp = true;  
  114.   
  115.     /** 
  116.      * 执行自动回滚的handler 
  117.      */  
  118.     Handler updateHandler = new Handler()  
  119.     {  
  120.   
  121.         @Override  
  122.         public void handleMessage(Message msg)  
  123.         {  
  124.             // 回弹速度随下拉距离moveDeltaY增大而增大  
  125.             MOVE_SPEED = (float) (8 + 5 * Math.tan(Math.PI / 2  
  126.                     / getMeasuredHeight() * (pullDownY + Math.abs(pullUpY))));  
  127.             if (!isTouch)  
  128.             {  
  129.                 // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..."  
  130.                 if (state == REFRESHING && pullDownY <= refreshDist)  
  131.                 {  
  132.                     pullDownY = refreshDist;  
  133.                     timer.cancel();  
  134.                 } else if (state == LOADING && -pullUpY <= loadmoreDist)  
  135.                 {  
  136.                     pullUpY = -loadmoreDist;  
  137.                     timer.cancel();  
  138.                 }  
  139.   
  140.             }  
  141.             if (pullDownY > 0)  
  142.                 pullDownY -= MOVE_SPEED;  
  143.             else if (pullUpY < 0)  
  144.                 pullUpY += MOVE_SPEED;  
  145.             if (pullDownY < 0)  
  146.             {  
  147.                 // 已完成回弹  
  148.                 pullDownY = 0;  
  149.                 pullView.clearAnimation();  
  150.                 // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态  
  151.                 if (state != REFRESHING && state != LOADING)  
  152.                     changeState(INIT);  
  153.                 timer.cancel();  
  154.             }  
  155.             if (pullUpY > 0)  
  156.             {  
  157.                 // 已完成回弹  
  158.                 pullUpY = 0;  
  159.                 pullUpView.clearAnimation();  
  160.                 // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态  
  161.                 if (state != REFRESHING && state != LOADING)  
  162.                     changeState(INIT);  
  163.                 timer.cancel();  
  164.             }  
  165.             // 刷新布局,会自动调用onLayout  
  166.             requestLayout();  
  167.         }  
  168.   
  169.     };  
  170.   
  171.     public void setOnRefreshListener(OnRefreshListener listener)  
  172.     {  
  173.         mListener = listener;  
  174.     }  
  175.   
  176.     public PullToRefreshLayout(Context context)  
  177.     {  
  178.         super(context);  
  179.         initView(context);  
  180.     }  
  181.   
  182.     public PullToRefreshLayout(Context context, AttributeSet attrs)  
  183.     {  
  184.         super(context, attrs);  
  185.         initView(context);  
  186.     }  
  187.   
  188.     public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle)  
  189.     {  
  190.         super(context, attrs, defStyle);  
  191.         initView(context);  
  192.     }  
  193.   
  194.     private void initView(Context context)  
  195.     {  
  196.         timer = new MyTimer(updateHandler);  
  197.         rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(  
  198.                 context, R.anim.reverse_anim);  
  199.         refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation(  
  200.                 context, R.anim.rotating);  
  201.         // 添加匀速转动动画  
  202.         LinearInterpolator lir = new LinearInterpolator();  
  203.         rotateAnimation.setInterpolator(lir);  
  204.         refreshingAnimation.setInterpolator(lir);  
  205.     }  
  206.   
  207.     private void hide()  
  208.     {  
  209.         timer.schedule(5);  
  210.     }  
  211.   
  212.     /** 
  213.      * 完成刷新操作,显示刷新结果。注意:刷新完成后一定要调用这个方法 
  214.      */  
  215.     /** 
  216.      * @param refreshResult 
  217.      *            PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败 
  218.      */  
  219.     public void refreshFinish(int refreshResult)  
  220.     {  
  221.         refreshingView.clearAnimation();  
  222.         refreshingView.setVisibility(View.GONE);  
  223.         switch (refreshResult)  
  224.         {  
  225.         case SUCCEED:  
  226.             // 刷新成功  
  227.             refreshStateImageView.setVisibility(View.VISIBLE);  
  228.             refreshStateTextView.setText(R.string.refresh_succeed);  
  229.             refreshStateImageView  
  230.                     .setBackgroundResource(R.drawable.refresh_succeed);  
  231.             break;  
  232.         case FAIL:  
  233.         default:  
  234.             // 刷新失败  
  235.             refreshStateImageView.setVisibility(View.VISIBLE);  
  236.             refreshStateTextView.setText(R.string.refresh_fail);  
  237.             refreshStateImageView  
  238.                     .setBackgroundResource(R.drawable.refresh_failed);  
  239.             break;  
  240.         }  
  241.         // 刷新结果停留1秒  
  242.         new Handler()  
  243.         {  
  244.             @Override  
  245.             public void handleMessage(Message msg)  
  246.             {  
  247.                 changeState(DONE);  
  248.                 hide();  
  249.             }  
  250.         }.sendEmptyMessageDelayed(01000);  
  251.     }  
  252.   
  253.     /** 
  254.      * 加载完毕,显示加载结果。注意:加载完成后一定要调用这个方法 
  255.      *  
  256.      * @param refreshResult 
  257.      *            PullToRefreshLayout.SUCCEED代表成功,PullToRefreshLayout.FAIL代表失败 
  258.      */  
  259.     public void loadmoreFinish(int refreshResult)  
  260.     {  
  261.         loadingView.clearAnimation();  
  262.         loadingView.setVisibility(View.GONE);  
  263.         switch (refreshResult)  
  264.         {  
  265.         case SUCCEED:  
  266.             // 加载成功  
  267.             loadStateImageView.setVisibility(View.VISIBLE);  
  268.             loadStateTextView.setText(R.string.load_succeed);  
  269.             loadStateImageView.setBackgroundResource(R.drawable.load_succeed);  
  270.             break;  
  271.         case FAIL:  
  272.         default:  
  273.             // 加载失败  
  274.             loadStateImageView.setVisibility(View.VISIBLE);  
  275.             loadStateTextView.setText(R.string.load_fail);  
  276.             loadStateImageView.setBackgroundResource(R.drawable.load_failed);  
  277.             break;  
  278.         }  
  279.         // 刷新结果停留1秒  
  280.         new Handler()  
  281.         {  
  282.             @Override  
  283.             public void handleMessage(Message msg)  
  284.             {  
  285.                 changeState(DONE);  
  286.                 hide();  
  287.             }  
  288.         }.sendEmptyMessageDelayed(01000);  
  289.     }  
  290.   
  291.     private void changeState(int to)  
  292.     {  
  293.         state = to;  
  294.         switch (state)  
  295.         {  
  296.         case INIT:  
  297.             // 下拉布局初始状态  
  298.             refreshStateImageView.setVisibility(View.GONE);  
  299.             refreshStateTextView.setText(R.string.pull_to_refresh);  
  300.             pullView.clearAnimation();  
  301.             pullView.setVisibility(View.VISIBLE);  
  302.             // 上拉布局初始状态  
  303.             loadStateImageView.setVisibility(View.GONE);  
  304.             loadStateTextView.setText(R.string.pullup_to_load);  
  305.             pullUpView.clearAnimation();  
  306.             pullUpView.setVisibility(View.VISIBLE);  
  307.             break;  
  308.         case RELEASE_TO_REFRESH:  
  309.             // 释放刷新状态  
  310.             refreshStateTextView.setText(R.string.release_to_refresh);  
  311.             pullView.startAnimation(rotateAnimation);  
  312.             break;  
  313.         case REFRESHING:  
  314.             // 正在刷新状态  
  315.             pullView.clearAnimation();  
  316.             refreshingView.setVisibility(View.VISIBLE);  
  317.             pullView.setVisibility(View.INVISIBLE);  
  318.             refreshingView.startAnimation(refreshingAnimation);  
  319.             refreshStateTextView.setText(R.string.refreshing);  
  320.             break;  
  321.         case RELEASE_TO_LOAD:  
  322.             // 释放加载状态  
  323.             loadStateTextView.setText(R.string.release_to_load);  
  324.             pullUpView.startAnimation(rotateAnimation);  
  325.             break;  
  326.         case LOADING:  
  327.             // 正在加载状态  
  328.             pullUpView.clearAnimation();  
  329.             loadingView.setVisibility(View.VISIBLE);  
  330.             pullUpView.setVisibility(View.INVISIBLE);  
  331.             loadingView.startAnimation(refreshingAnimation);  
  332.             loadStateTextView.setText(R.string.loading);  
  333.             break;  
  334.         case DONE:  
  335.             // 刷新或加载完毕,啥都不做  
  336.             break;  
  337.         }  
  338.     }  
  339.   
  340.     /** 
  341.      * 不限制上拉或下拉 
  342.      */  
  343.     private void releasePull()  
  344.     {  
  345.         canPullDown = true;  
  346.         canPullUp = true;  
  347.     }  
  348.   
  349.     /* 
  350.      * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突 
  351.      *  
  352.      * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent) 
  353.      */  
  354.     @Override  
  355.     public boolean dispatchTouchEvent(MotionEvent ev)  
  356.     {  
  357.         switch (ev.getActionMasked())  
  358.         {  
  359.         case MotionEvent.ACTION_DOWN:  
  360.             downY = ev.getY();  
  361.             lastY = downY;  
  362.             timer.cancel();  
  363.             mEvents = 0;  
  364.             releasePull();  
  365.             break;  
  366.         case MotionEvent.ACTION_POINTER_DOWN:  
  367.         case MotionEvent.ACTION_POINTER_UP:  
  368.             // 过滤多点触碰  
  369.             mEvents = -1;  
  370.             break;  
  371.         case MotionEvent.ACTION_MOVE:  
  372.             if (mEvents == 0)  
  373.             {  
  374.                 if (((Pullable) pullableView).canPullDown() && canPullDown  
  375.                         && state != LOADING)  
  376.                 {  
  377.                     // 可以下拉,正在加载时不能下拉  
  378.                     // 对实际滑动距离做缩小,造成用力拉的感觉  
  379.                     pullDownY = pullDownY + (ev.getY() - lastY) / radio;  
  380.                     if (pullDownY < 0)  
  381.                     {  
  382.                         pullDownY = 0;  
  383.                         canPullDown = false;  
  384.                         canPullUp = true;  
  385.                     }  
  386.                     if (pullDownY > getMeasuredHeight())  
  387.                         pullDownY = getMeasuredHeight();  
  388.                     if (state == REFRESHING)  
  389.                     {  
  390.                         // 正在刷新的时候触摸移动  
  391.                         isTouch = true;  
  392.                     }  
  393.                 } else if (((Pullable) pullableView).canPullUp() && canPullUp  
  394.                         && state != REFRESHING)  
  395.                 {  
  396.                     // 可以上拉,正在刷新时不能上拉  
  397.                     pullUpY = pullUpY + (ev.getY() - lastY) / radio;  
  398.                     if (pullUpY > 0)  
  399.                     {  
  400.                         pullUpY = 0;  
  401.                         canPullDown = true;  
  402.                         canPullUp = false;  
  403.                     }  
  404.                     if (pullUpY < -getMeasuredHeight())  
  405.                         pullUpY = -getMeasuredHeight();  
  406.                     if (state == LOADING)  
  407.                     {  
  408.                         // 正在加载的时候触摸移动  
  409.                         isTouch = true;  
  410.                     }  
  411.                 } else  
  412.                     releasePull();  
  413.             } else  
  414.                 mEvents = 0;  
  415.             lastY = ev.getY();  
  416.             // 根据下拉距离改变比例  
  417.             radio = (float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight()  
  418.                     * (pullDownY + Math.abs(pullUpY))));  
  419.             requestLayout();  
  420.             if (pullDownY <= refreshDist  
  421. <span style="white-space:pre">                    </span>&& (state == RELEASE_TO_REFRESH || state == DONE)) {  
  422. <span style="white-space:pre">                </span>// 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新  
  423. <span style="white-space:pre">                </span>changeState(INIT);  
  424. <span style="white-space:pre">            </span>}  
  425. <span style="white-space:pre">            </span>if (pullDownY >= refreshDist && (state == INIT || state == DONE)) {  
  426. <span style="white-space:pre">                </span>// 如果下拉距离达到刷新的距离且当前状态是初始状态刷新,改变状态为释放刷新  
  427. <span style="white-space:pre">                </span>changeState(RELEASE_TO_REFRESH);  
  428. <span style="white-space:pre">            </span>}  
  429. <span style="white-space:pre">            </span>// 下面是判断上拉加载的,同上,注意pullUpY是负值  
  430. <span style="white-space:pre">            </span>if (-pullUpY <= loadmoreDist  
  431. <span style="white-space:pre">                    </span>&& (state == RELEASE_TO_LOAD || state == DONE)) {  
  432. <span style="white-space:pre">                </span>changeState(INIT);  
  433. <span style="white-space:pre">            </span>}  
  434. <span style="white-space:pre">            </span>if (-pullUpY >= loadmoreDist && (state == INIT || state == DONE)) {  
  435. <span style="white-space:pre">                </span>changeState(RELEASE_TO_LOAD);  
  436. <span style="white-space:pre">            </span>}  
  437.             // 因为刷新和加载操作不能同时进行,所以pullDownY和pullUpY不会同时不为0,因此这里用(pullDownY +  
  438.             // Math.abs(pullUpY))就可以不对当前状态作区分了  
  439.             if ((pullDownY + Math.abs(pullUpY)) > 8)  
  440.             {  
  441.                 // 防止下拉过程中误触发长按事件和点击事件  
  442.                 ev.setAction(MotionEvent.ACTION_CANCEL);  
  443.             }  
  444.             break;  
  445.         case MotionEvent.ACTION_UP:  
  446.             if (pullDownY > refreshDist || -pullUpY > loadmoreDist)  
  447.                 // 正在刷新时往下拉(正在加载时往上拉),释放后下拉头(上拉头)不隐藏  
  448.                 isTouch = false;  
  449.             if (state == RELEASE_TO_REFRESH)  
  450.             {  
  451.                 changeState(REFRESHING);  
  452.                 // 刷新操作  
  453.                 if (mListener != null)  
  454.                     mListener.onRefresh(this);  
  455.             } else if (state == RELEASE_TO_LOAD)  
  456.             {  
  457.                 changeState(LOADING);  
  458.                 // 加载操作  
  459.                 if (mListener != null)  
  460.                     mListener.onLoadMore(this);  
  461.             }  
  462.             hide();  
  463.         default:  
  464.             break;  
  465.         }  
  466.         // 事件分发交给父类  
  467.         super.dispatchTouchEvent(ev);  
  468.         return true;  
  469.     }  
  470.   
  471.     private void initView()  
  472.     {  
  473.         // 初始化下拉布局  
  474.         pullView = refreshView.findViewById(R.id.pull_icon);  
  475.         refreshStateTextView = (TextView) refreshView  
  476.                 .findViewById(R.id.state_tv);  
  477.         refreshingView = refreshView.findViewById(R.id.refreshing_icon);  
  478.         refreshStateImageView = refreshView.findViewById(R.id.state_iv);  
  479.         // 初始化上拉布局  
  480.         pullUpView = loadmoreView.findViewById(R.id.pullup_icon);  
  481.         loadStateTextView = (TextView) loadmoreView  
  482.                 .findViewById(R.id.loadstate_tv);  
  483.         loadingView = loadmoreView.findViewById(R.id.loading_icon);  
  484.         loadStateImageView = loadmoreView.findViewById(R.id.loadstate_iv);  
  485.     }  
  486.   
  487.     @Override  
  488.     protected void onLayout(boolean changed, int l, int t, int r, int b)  
  489.     {  
  490.         if (!isLayout)  
  491.         {  
  492.             // 这里是第一次进来的时候做一些初始化  
  493.             refreshView = getChildAt(0);  
  494.             pullableView = getChildAt(1);  
  495.             loadmoreView = getChildAt(2);  
  496.             isLayout = true;  
  497.             initView();  
  498.             refreshDist = ((ViewGroup) refreshView).getChildAt(0)  
  499.                     .getMeasuredHeight();  
  500.             loadmoreDist = ((ViewGroup) loadmoreView).getChildAt(0)  
  501.                     .getMeasuredHeight();  
  502.         }  
  503.         // 改变子控件的布局,这里直接用(pullDownY + pullUpY)作为偏移量,这样就可以不对当前状态作区分  
  504.         refreshView.layout(0,  
  505.                 (int) (pullDownY + pullUpY) - refreshView.getMeasuredHeight(),  
  506.                 refreshView.getMeasuredWidth(), (int) (pullDownY + pullUpY));  
  507.         pullableView.layout(0, (int) (pullDownY + pullUpY),  
  508.                 pullableView.getMeasuredWidth(), (int) (pullDownY + pullUpY)  
  509.                         + pullableView.getMeasuredHeight());  
  510.         loadmoreView.layout(0,  
  511.                 (int) (pullDownY + pullUpY) + pullableView.getMeasuredHeight(),  
  512.                 loadmoreView.getMeasuredWidth(),  
  513.                 (int) (pullDownY + pullUpY) + pullableView.getMeasuredHeight()  
  514.                         + loadmoreView.getMeasuredHeight());  
  515.     }  
  516.   
  517.     class MyTimer  
  518.     {  
  519.         private Handler handler;  
  520.         private Timer timer;  
  521.         private MyTask mTask;  
  522.   
  523.         public MyTimer(Handler handler)  
  524.         {  
  525.             this.handler = handler;  
  526.             timer = new Timer();  
  527.         }  
  528.   
  529.         public void schedule(long period)  
  530.         {  
  531.             if (mTask != null)  
  532.             {  
  533.                 mTask.cancel();  
  534.                 mTask = null;  
  535.             }  
  536.             mTask = new MyTask(handler);  
  537.             timer.schedule(mTask, 0, period);  
  538.         }  
  539.   
  540.         public void cancel()  
  541.         {  
  542.             if (mTask != null)  
  543.             {  
  544.                 mTask.cancel();  
  545.                 mTask = null;  
  546.             }  
  547.         }  
  548.   
  549.         class MyTask extends TimerTask  
  550.         {  
  551.             private Handler handler;  
  552.   
  553.             public MyTask(Handler handler)  
  554.             {  
  555.                 this.handler = handler;  
  556.             }  
  557.   
  558.             @Override  
  559.             public void run()  
  560.             {  
  561.                 handler.obtainMessage().sendToTarget();  
  562.             }  
  563.   
  564.         }  
  565.     }  
  566.   
  567.     /** 
  568.      * 刷新加载回调接口 
  569.      *  
  570.      * @author chenjing 
  571.      *  
  572.      */  
  573.     public interface OnRefreshListener  
  574.     {  
  575.         /** 
  576.          * 刷新操作 
  577.          */  
  578.         void onRefresh(PullToRefreshLayout pullToRefreshLayout);  
  579.   
  580.         /** 
  581.          * 加载操作 
  582.          */  
  583.         void onLoadMore(PullToRefreshLayout pullToRefreshLayout);  
  584.     }  
  585.   
  586. }  
上面就是整个布局的代码,并不是很难。

下面看各个View对Pullable接口的实现,ListView和GridView还有ExpandableListView的判断方法是一样的:

PullableListView:

[java]  view plain  copy
  1. package com.jingchen.pulltorefresh.pullableview;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.widget.ListView;  
  7.   
  8. public class PullableListView extends ListView implements Pullable  
  9. {  
  10.   
  11.     public PullableListView(Context context)  
  12.     {  
  13.         super(context);  
  14.     }  
  15.   
  16.     public PullableListView(Context context, AttributeSet attrs)  
  17.     {  
  18.         super(context, attrs);  
  19.     }  
  20.   
  21.     public PullableListView(Context context, AttributeSet attrs, int defStyle)  
  22.     {  
  23.         super(context, attrs, defStyle);  
  24.     }  
  25.   
  26.     @Override  
  27.     public boolean canPullDown()  
  28.     {  
  29.         if (getCount() == 0)  
  30.         {  
  31.             // 没有item的时候也可以下拉刷新  
  32.             return true;  
  33.         } else if (getFirstVisiblePosition() == 0  
  34.                 && getChildAt(0).getTop() >= 0)  
  35.         {  
  36.             // 滑到ListView的顶部了  
  37.             return true;  
  38.         } else  
  39.             return false;  
  40.     }  
  41.   
  42.     @Override  
  43.     public boolean canPullUp()  
  44.     {  
  45.         if (getCount() == 0)  
  46.         {  
  47.             // 没有item的时候也可以上拉加载  
  48.             return true;  
  49.         } else if (getLastVisiblePosition() == (getCount() - 1))  
  50.         {  
  51.             // 滑到底部了  
  52.             if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null  
  53.                     && getChildAt(  
  54.                             getLastVisiblePosition()  
  55.                                     - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight())  
  56.                 return true;  
  57.         }  
  58.         return false;  
  59.     }  
  60. }  

PullableExpandableListView:

[java]  view plain  copy
  1. package com.jingchen.pulltorefresh.pullableview;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.widget.ExpandableListView;  
  6.   
  7. public class PullableExpandableListView extends ExpandableListView implements  
  8.         Pullable  
  9. {  
  10.   
  11.     public PullableExpandableListView(Context context)  
  12.     {  
  13.         super(context);  
  14.     }  
  15.   
  16.     public PullableExpandableListView(Context context, AttributeSet attrs)  
  17.     {  
  18.         super(context, attrs);  
  19.     }  
  20.   
  21.     public PullableExpandableListView(Context context, AttributeSet attrs,  
  22.             int defStyle)  
  23.     {  
  24.         super(context, attrs, defStyle);  
  25.     }  
  26.   
  27.     @Override  
  28.     public boolean canPullDown()  
  29.     {  
  30.         if (getCount() == 0)  
  31.         {  
  32.             // 没有item的时候也可以下拉刷新  
  33.             return true;  
  34.         } else if (getFirstVisiblePosition() == 0  
  35.                 && getChildAt(0).getTop() >= 0)  
  36.         {  
  37.             // 滑到顶部了  
  38.             return true;  
  39.         } else  
  40.             return false;  
  41.     }  
  42.   
  43.     @Override  
  44.     public boolean canPullUp()  
  45.     {  
  46.         if (getCount() == 0)  
  47.         {  
  48.             // 没有item的时候也可以上拉加载  
  49.             return true;  
  50.         } else if (getLastVisiblePosition() == (getCount() - 1))  
  51.         {  
  52.             // 滑到底部了  
  53.             if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null  
  54.                     && getChildAt(  
  55.                             getLastVisiblePosition()  
  56.                                     - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight())  
  57.                 return true;  
  58.         }  
  59.         return false;  
  60.     }  
  61.   
  62. }  

PullableGridView:

[java]  view plain  copy
  1. package com.jingchen.pulltorefresh.pullableview;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.widget.GridView;  
  6.   
  7. public class PullableGridView extends GridView implements Pullable  
  8. {  
  9.   
  10.     public PullableGridView(Context context)  
  11.     {  
  12.         super(context);  
  13.     }  
  14.   
  15.     public PullableGridView(Context context, AttributeSet attrs)  
  16.     {  
  17.         super(context, attrs);  
  18.     }  
  19.   
  20.     public PullableGridView(Context context, AttributeSet attrs, int defStyle)  
  21.     {  
  22.         super(context, attrs, defStyle);  
  23.     }  
  24.   
  25.     @Override  
  26.     public boolean canPullDown()  
  27.     {  
  28.         if (getCount() == 0)  
  29.         {  
  30.             // 没有item的时候也可以下拉刷新  
  31.             return true;  
  32.         } else if (getFirstVisiblePosition() == 0  
  33.                 && getChildAt(0).getTop() >= 0)  
  34.         {  
  35.             // 滑到顶部了  
  36.             return true;  
  37.         } else  
  38.             return false;  
  39.     }  
  40.   
  41.     @Override  
  42.     public boolean canPullUp()  
  43.     {  
  44.         if (getCount() == 0)  
  45.         {  
  46.             // 没有item的时候也可以上拉加载  
  47.             return true;  
  48.         } else if (getLastVisiblePosition() == (getCount() - 1))  
  49.         {  
  50.             // 滑到底部了  
  51.             if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null  
  52.                     && getChildAt(  
  53.                             getLastVisiblePosition()  
  54.                                     - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight())  
  55.                 return true;  
  56.         }  
  57.         return false;  
  58.     }  
  59.   
  60. }  

PullableScrollView:

[java]  view plain  copy
  1. package com.jingchen.pulltorefresh.pullableview;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.widget.ScrollView;  
  6.   
  7. public class PullableScrollView extends ScrollView implements Pullable  
  8. {  
  9.   
  10.     public PullableScrollView(Context context)  
  11.     {  
  12.         super(context);  
  13.     }  
  14.   
  15.     public PullableScrollView(Context context, AttributeSet attrs)  
  16.     {  
  17.         super(context, attrs);  
  18.     }  
  19.   
  20.     public PullableScrollView(Context context, AttributeSet attrs, int defStyle)  
  21.     {  
  22.         super(context, attrs, defStyle);  
  23.     }  
  24.   
  25.     @Override  
  26.     public boolean canPullDown()  
  27.     {  
  28.         if (getScrollY() == 0)  
  29.             return true;  
  30.         else  
  31.             return false;  
  32.     }  
  33.   
  34.     @Override  
  35.     public boolean canPullUp()  
  36.     {  
  37.         if (getScrollY() >= (getChildAt(0).getHeight() - getMeasuredHeight()))  
  38.             return true;  
  39.         else  
  40.             return false;  
  41.     }  
  42.   
  43. }  

PullableWebView:

[java]  view plain  copy
  1. package com.jingchen.pulltorefresh.pullableview;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.webkit.WebView;  
  6.   
  7. public class PullableWebView extends WebView implements Pullable  
  8. {  
  9.   
  10.     public PullableWebView(Context context)  
  11.     {  
  12.         super(context);  
  13.     }  
  14.   
  15.     public PullableWebView(Context context, AttributeSet attrs)  
  16.     {  
  17.         super(context, attrs);  
  18.     }  
  19.   
  20.     public PullableWebView(Context context, AttributeSet attrs, int defStyle)  
  21.     {  
  22.         super(context, attrs, defStyle);  
  23.     }  
  24.   
  25.     @Override  
  26.     public boolean canPullDown()  
  27.     {  
  28.         if (getScrollY() == 0)  
  29.             return true;  
  30.         else  
  31.             return false;  
  32.     }  
  33.   
  34.     @Override  
  35.     public boolean canPullUp()  
  36.     {  
  37.         if (getScrollY() >= getContentHeight() * getScale()  
  38.                 - getMeasuredHeight())  
  39.             return true;  
  40.         else  
  41.             return false;  
  42.     }  
  43. }  

ImageView和TextView就不贴了,我直接在方法里返回了true。

OK了,整个demo的代码有点多,就不贴了。

源码下载


29
0
 
 
我的同类文章
猜你在找
Android高级界面控件难点精讲
Android《自定义控件》视频,震撼发布!
Android之动画全讲
【Android APP开发】Android高级商业布局快速实现
Android自定义控件系列之九宫格解锁
Android下拉刷新上拉加载控件对所有View通用
Android下拉刷新上拉加载控件对所有View通用
Android下拉刷新上拉加载控件对所有View通用
PullToRefresh 系列一基本使用方法 Android上拉加载下拉刷新控件详解
Android自定义控件ListView的下拉刷新与上拉加载
查看评论
127楼  gladiator0975 2016-08-12 21:20发表 [回复]
我项目里用了这个,但发现存在内存泄露的问题,没人遇到这个问题吗?
126楼  sp_wei 2016-08-11 09:22发表 [回复]
博主你好,我想问一下你这有没有android studio的代码呀,我把这代码引用到studio里每个类都报了错误,只引用了Listview的类
125楼  sgset 2016-07-21 16:41发表 [回复]
博主你好,我用了你这个代码之后,出现了一个bug。就是加载更多的时候,下滑到listview的最底部,然后再上拉加载更多,如果手指不松开的话,listview最后一个条目就显示的不全啊。这个bug该怎么解决?求大神求助。当listview下滑的速度慢的时候,条目就会展示不全;当listview下滑的速度快的时候,条目就会展示的全
124楼  大妖过境 2016-07-14 20:57发表 [回复]
引用“doraspenlows”的评论:我下载的是最早的版本,没有一开始进入页面就自动刷新的功能,我研究了好久还是没有结果,为什么刷新头是在...

同问,我也搞不清楚如何在onLayout()里面就加载了刷新头,pullableView,loadmoreView等控件,我注意到是用ViewGroup的getChild()函数获取的,但是ViewGroup并没有相关交代,到底那三个控件是怎么初始化的呢?
123楼  liyanfang_520 2016-07-06 13:16发表 [回复]
我想请问一下您这下拉刷新怎么解决和viewpage左右滑动的冲突的
Re:  江啸 2016-08-26 10:44发表 [回复]
回复liyanfang_520:在PullToRefreshLayout这个类里面的move手势监听中加一个判断条件pullDownY > 0 || (((Pullable) pullableView).canPullDown() 
&& canPullDown && state != LOADING&&Math.abs(moveX) < Math.abs(moveY))
Re:  江啸 2016-08-30 11:00发表 [回复]
回复qq_30299129:在手刚点击屏幕时获取坐标“X = getX();Y = getY();”
手滑动时计算活动距离“float moveX = getX() - X;float moveY = getY() - Y;”
Re:  zhangym90 2016-08-26 13:17发表 [回复]
回复qq_30299129:你好,能麻烦你详细说下这句话该加到哪个位置吗,还有moveX跟moveY的值该如何定义,本人小白一枚,感谢!
122楼  yqwyj123 2016-06-17 11:46发表 [回复]
请问博主,requestLayout()方法可以用invalidate()方法代替吗?
Re:  大家请叫我明哥 2016-06-17 12:47发表 [回复]
回复yqwyj123:肯定不能
Re:  大家请叫我明哥 2016-06-17 12:45发表 [回复]
回复yqwyj123:他这个是用requestLayout(),然后在onLayout计算出需要的大小,关键是测量;invalidate()只是重新绘制没有onLayout过程,
测试中refreshView.layout 然后 refreshView.invalidate()虽然会滑动,但是上拉头中的子view大小会出bug,估计是没有测量导致的,使用requestLayout()效率很低,每次都几乎把一个View的绘制流程都走完,并且觉得使用定时器有点奇葩(个人认为摆了),Ultra大神用的是Scroller+post
121楼  大家请叫我明哥 2016-06-16 15:06发表 [回复]
这个控件由于使用requestLayout();的方式进行更新,比较糟糕,会造成listview卡顿
120楼  ckyang 2016-05-31 09:30发表 [回复]
博主,这个控件非常棒,不过我在使用的过程中遇上一个问题,想请教一下。集成自ListView、GridView、ExpandableListView这些控件的地方,怎么使用setEmptyView?因为我在使用setEmptyView这个方法的时候,出现布局错误。
119楼  ckyang 2016-05-31 09:26发表 [回复]
博主,这个控件非常棒,不过我在使用的过程中遇上一个问题,想请教一下。集成自ListView、GridView、ExpandableListView这些控件的地方,怎么使用setEmptyView,因为我在使用setEmptyView这个方法的时候,出现布局错误。
118楼  lixianda000 2016-05-17 13:27发表 [回复]
有个bug 当网络情况差的时候ListView上拉加载中 如果正在加载中 用户又上拉了 报错java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. 
Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() 
when its content changes. [in ListView(2131427501, class com.aybc.view.PullableListView) with Adapter(class com.aybc.adapter.FoodIndexAdapter)]
117楼  lixianda000 2016-05-17 13:26发表 [回复]
楼主,有个bug, 当网络情况差的时候ListView上拉加载中 如果正在加载中 用户又上拉了 报错java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. 
Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() 
when its content changes. [in ListView(2131427501, class com.aybc.view.PullableListView) with Adapter(class com.aybc.adapter.FoodIndexAdapter)]
116楼  heartfly666 2016-05-16 09:56发表 [回复]
嵌套的viewpager自动轮播 手动切换被屏蔽了 请问楼主怎么解决
115楼  qq_527235890 2016-05-06 12:42发表 [回复]
楼主,请问你这个怎么实现加载或刷新添加新数据,重写那个方法。因为我看你不论怎么加载刷新都是那29个数据
114楼  Bye白 2016-04-09 19:29发表 [回复]
关于瀑布流的不能上拉的bug解决:
//因为StaggeredGridLayoutManager的特殊性可能导致最后显示的item存在多个,所以这里取到的是一个数组
//得到这个数组后再取到数组中position值最大的那个就是最后显示的position值了
int[] lastPositions = new int[((StaggeredGridLayoutManager) lm).getSpanCount()];
((StaggeredGridLayoutManager) lm).findLastVisibleItemPositions(lastPositions);
lastVisibleItemPosition = findMax(lastPositions);
//找到数组中的最大值
private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value > max) {
max = value;
}
}
return max;
}
Re:  qq_527235890 2016-05-08 23:17发表 [回复]
回复q365066360:你好,请问你知道这个怎么实现加载或刷新添加新数据吗,比如加载后在源数据项之后添加5组新数据,我是新手,求救啊。
113楼  友之游 2016-03-31 16:39发表 [回复]
学习了,感谢楼主大神的无私分享。
Re:  qq_527235890 2016-05-08 23:17发表 [回复]
回复qq_33867930:你好,请问你知道这个怎么实现加载或刷新添加新数据吗,比如加载后在源数据项之后添加5组新数据,我是新手,求救啊。
112楼  Garlic_You_Ruthless 2016-03-22 14:18发表 [回复]
pullablewebView.getscrollY()再某些网页中始终为0,比如淘宝首页,请问有人解决了这个问题没?
111楼  ylei127 2016-03-07 14:50发表 [回复]
使用下载的最新的版本 listview那个,当手指往上滑的时候 正在加载的状态经常会停顿,需要手再次往上滑动才会结束上拉加载
Re:  qq_527235890 2016-05-08 23:18发表 [回复]
回复u012963736:你好,请问你知道这个怎么实现加载或刷新添加新数据吗,比如加载后在源数据项之后添加5组新数据,我是新手,求救啊。
110楼  xxdxuxiangdong 2016-01-28 14:53发表 [回复]
楼主,能否实现阻尼效果的配置????
109楼  yongchao1210 2016-01-27 10:24发表 [回复]
楼主 我在只用的时候 在两个 Fragment 里面 分别放 一个 viewpager 时候 先打开任何一个 Fragment 则在打开另一个 Fragment 的时候 后打开的 那个里面的 拉动组件显示不出来,但是调试看了列表数据都已填充了, 检查一下PullToRefreshLayout的高度 为0,而且后打开的那个Fragment 中的PullToRefreshLayout的onLayout不会被调用 ,楼主帮忙给看看
Re:  yy_jsp 2016-05-10 13:12发表 [回复]
回复yongchao1210: 朋友怎么解决的呢?
Re:  qq_527235890 2016-05-08 23:20发表 [回复]
回复yongchao1210:你好,请问你知道这个怎么实现加载或刷新添加新数据吗,比如加载后在源数据项之后添加5组新数据,我是新手,求救啊。我也想在Fragment中使用,求指导
108楼  Xiaoxao璐 2016-01-24 11:37发表 [回复]
楼主 布局里面加了一个通过viewpager实现图片轮播的效果,但是滑动切换图片时 ,总是触发下拉刷新 ,这个可以解决吗
Re:  lindepeng318611 2016-01-29 09:49发表 [回复]
回复NatanLu:你这个问题解决了吗?
107楼  wly19871117 2016-01-21 17:19发表 [回复]
用了下,效果是不错,不过操作频繁会导致ANR,看了下日志,是使用timer出现了死锁,希望楼主重视下,及时优化
106楼  dong53821713 2016-01-18 19:12发表 [回复]
楼主,可以设置打开和关闭上拉加载下拉刷新呢?有的时候我只需要一个上拉或者下拉,求教!谢谢
105楼  qq_16598489 2016-01-14 16:58发表 [回复]
上拉刷新数据和下拉加载的数据往哪写啊?
104楼  lianzhen2011 2016-01-12 15:47发表 [回复]
楼主你好,为什么要把整个布局写成自定义控件呢?之自定义中间的listview控件不是更灵活吗
103楼  lianzhen2011 2016-01-12 15:39发表 [回复]
为什么要把整个布局文件写成自定义的,而不是单一的自定义Listview,导致如果listview上面还有控件(例如:textview)的话,下拉头不就在控件(例如:textview)的上面了吗?,感觉把中间的布局自定义比较灵活
102楼  qq_32164565 2016-01-11 11:24发表 [回复]
要在layout下面放多个控件, 是每一个都要实现pullable接口吗
101楼  lv_ws 2016-01-06 14:30发表 [回复]
哥们用timer更新head或foot会阻塞主线程的执行吧
100楼  淡然开怀 2015-12-28 16:42发表 [回复]
下拉刷新,完了就停在那,头部不会收回去。底部也一样。
99楼  远方夕阳 2015-12-22 11:12发表 [回复]
博主您好。
您的下拉刷新控件非常好,再有一个小改进就更好啦,就是上拉加载的时候,加载完毕后,加载动画View所占据的位置没有被新加载的item替换,而是直接返回到之前的list中最后一个item.
这样会感觉抖动了一下,能不能改成 像google play那样的刷新效果
98楼  pcdjimmy 2015-12-17 19:23发表 [回复]
楼主你好,由于您没有在代码中标明开源协议,我们将代码做了修改并运用在商业项目中且闭源发布了,但在代码中保留了您的署名,如果您对此有异议,请发邮件到taozui1在163.com,我们可以商讨合作。期盼您的回复。
97楼  pcdjimmy 2015-12-17 19:22发表 [回复]
楼主你好,由于您没有在代码中标明开源协议,我们将代码做了修改并运用在商业项目中且闭源发布了,但在代码中保留了您的署名,如果您对此有异议,请发邮件到taozui1在163.com,我们可以商讨合作。期盼您的回复。
96楼  方阳 2015-12-17 16:16发表 [回复]
想问怎么加横向删除?
95楼  lindepeng318611 2015-12-16 13:50发表 [回复]
楼主你好,我在使用PullableListView想用setEmptyView,但是没有效果,PullToRefreshLayout对PullableListView似乎有影响
Re:  qq_527235890 2016-05-08 23:23发表 [回复]
回复lindepeng318611:你好,请问你知道这个怎么实现加载或刷新添加新数据吗,比如加载后在源数据项之后添加5组新数据,我是新手,求救啊。
94楼  liyincheng 2015-12-07 15:01发表 [回复]
可以加个状态吗,类似淘宝拉到底部就自动加载,不需要释放加载,可以设置当前刷新类 是否有更多数据加载(调用者设置),然后当没有更多数据加载的时候,底部就一直显示没有更多数据加载,不管怎么滑动。
93楼  gsh4348548 2015-11-23 17:59发表 [回复]
我搬我项目中 PullToRefreshLayout类中 // 初始化上拉布局
pullUpView = loadmoreView.findViewById(R.id.pullup_icon);
这句话和下面的所有控件都报null错误,咋回事?
92楼  jixiedaxiakule 2015-11-19 11:44发表 [回复]
大神你好,,您这里实现的都是比较常见的下拉,,如果像瀑布流这样的样式你怎么去实现上拉下拉呢
91楼  Canra 2015-11-14 11:43发表 [回复]
你好,我想问下怎么处理分发事件。我的界面最外层是一个ScrollView,然后底部有个gridview,我用你的自动控件,但是滑动冲突不知道怎么解决
90楼  Canra 2015-11-14 11:42发表 [回复]
你好,我想问下怎么处理分发事件。我的界面最外层是一个ScrollView,然后底部有个gridview,我用你的自动控件,但是滑动冲突不知道怎么解决
89楼  小雪VS舞 2015-11-10 18:12发表 [回复]
佩服!
88楼  diguashishei 2015-11-05 10:17发表 [回复]
楼主你好,
我在项目使用了你的控件,很感谢。 有个问题,当向下加载结束后,列表还是保持在加载之前的最后一项的位置,如何可以显示一条或者一点新数据?请问修改哪里?
87楼  oqqXiXi 2015-10-31 00:36发表 [回复]
首先,非常感激楼主,他很热心,帮忙解决了无数据禁止上拉加载更多的问题,虽然很暴力的方式。

另外,我说下该控件需要自己更改的地方。
比如,implements OnRefreshListener 实现该接口
回调了两个方法,很恶心!

第一,我往下没数据了,还可以上拉,而且没API。不符合真实项目需求,这里需要自己修改dispatchTouchEvent监听判断。

第二,也是恶心的地方。你既然定义了两个监听方法让我们写逻辑。那么问题来了,异步请求服务端Action接口拿到了数据。我想问问,你怎么知道我1秒还是20秒拿到数据?既然你不能决定我何时拿到数据。为何里面非得写:
// 千万别忘了告诉控件刷新完毕了哦! pullToRefreshLayout.refreshFinish(PullToRefreshLayout.SUCCEED);
pullToRefreshLayout.loadmoreFinish(PullToRefreshLayout.SUCCEED);
我有尝试过在Activity里面定义一个变量pull去接收它,用来在Handler接受服务端返回是否成功后再写这段代码,情况是所有控件空指针,同样如果直接用Activity中的
private PullToRefreshLayout ptrl; 去点出这两个方法一样是空指针。那么结果很显然了。鸡肋,只为效果……
经过一个小时我自己修改好了,虽然也有些暴力,总好过无限上拉刷新,哪怕无数据可拿。实现的两个接口简直没卵用,还刚刚请求服务端,这里必须就要给出刷新结果回弹。不过,楼主肯拿出来分享足以赢得我的尊重,所以我花费这么多时间去抠字说事,同样希望楼主有时间补缺下……
Re:  qq_527235890 2016-05-06 14:50发表 [回复]
回复oqqXiXi:你好,我看楼主这个无法刷新或加载到新数据,我想实现这个,不知道能否请教一下,该怎么做,是重写某个方法吗?还是楼主的代码中有,但我没找到,还是楼主的代码里面就没有?我是新手,求教
Re:  陈靖_ 2015-10-31 16:51发表 [回复]
回复oqqXiXi:嗯,您说的很对,确实有很多没有考虑到的地方。我并不是专业的Android开发者,业余时间写着玩的,没有考虑的很周到,共享出来希望能给有需要的人提供一个思路,也希望大家能一块去完善这个控件。如果给您带来了不必要的麻烦,还请见谅
86楼  oqqXiXi 2015-10-30 10:08发表 [回复]
我想问下,为什么没有禁止下拉刷,上拉加载的API呢。拿过来非得自己加或者改?
Re:  oqqXiXi 2015-10-31 00:37发表 [回复]
首先,非常感激楼主,他很热心,帮忙解决了无数据禁止上拉加载更多的问题,虽然很暴力的方式。

另外,我说下该控件需要自己更改的地方。
比如,implements OnRefreshListener 实现该接口
回调了两个方法,很恶心!

第一,我往下没数据了,还可以上拉,而且没API。不符合真实项目需求,这里需要自己修改dispatchTouchEvent监听判断。

第二,也是恶心的地方。你既然定义了两个监听方法让我们写逻辑。那么问题来了,异步请求服务端Action接口拿到了数据。我想问问,你怎么知道我1秒还是20秒拿到数据?既然你不能决定我何时拿到数据。为何里面非得写:
// 千万别忘了告诉控件刷新完毕了哦! pullToRefreshLayout.refreshFinish(PullToRefreshLayout.SUCCEED);
pullToRefreshLayout.loadmoreFinish(PullToRefreshLayout.SUCCEED);
我有尝试过在Activity里面定义一个变量pull去接收它,用来在Handler接受服务端返回是否成功后再写这段代码,情况是所有控件空指针,同样如果直接用Activity中的
private PullToRefreshLayout ptrl; 去点出这两个方法一样是空指针。那么结果很显然了。鸡肋,只为效果……
经过一个小时我自己修改好了,虽然也有些暴力,总好过无限上拉刷新,哪怕无数据可拿。实现的两个接口简直没卵用,还刚刚请求服务端,这里必须就要给出刷新结果回弹。不过,楼主肯拿出来分享足以赢得我的尊重,所以我花费这么多时间去抠字说事,同样希望楼主有时间补缺下……
Re:  dong53821713 2016-01-18 19:17发表 [回复]
回复oqqXiXi:请问怎么解决的 ,我也想要可以控制上啦和下拉的功能有时候只需要上啦或者下拉其中的一个就行,可以程序没有提供相关的方法
Re:  一心蝈蝈丶 2016-01-27 10:02发表 [回复]
回复dong53821713:在PullableListView 里面自己定义一个boolean变量去控制 是否需要上拉或者下拉就可以了
85楼  ityangjun 2015-10-16 11:59发表 [回复]
使用recyclerview继承pullable接口,recyclerview无法滑动,如何解决
84楼  CSDN_XSH 2015-10-11 12:54发表 [回复]
mark
83楼  Qi18501967134 2015-09-18 14:47发表 [回复]
新版本gif的是不没了
82楼  Qi18501967134 2015-09-18 14:40发表 [回复]
我只想说,你太jb牛b了,感谢,正是我需要的
81楼  z_z_Autum 2015-09-10 14:58发表 [回复]
为什么我刷新完后不能加载,或者加载完后不能刷新
80楼  niwanquba 2015-08-22 17:31发表 [回复]
如何控制页面加载的时候,隐藏include进来的下拉刷新和上拉加载的布局,在需要刷新的时候再显示出来。
79楼  迷影毅 2015-08-04 10:18发表 [回复]
楼主 我在加载完的时候如何改成没有更多信息了呢
Re:  迷影毅 2015-08-05 09:51发表 [回复]
回复u010437517:已经解决了 谢谢楼主
78楼  doraspenlows 2015-07-28 09:20发表 [回复]
我下载的是最早的版本,没有一开始进入页面就自动刷新的功能,我研究了好久还是没有结果,为什么刷新头是在onlayout里面通过refreshView = getChildAt(0)这种方式获取的,为什么不能在构造函数里使用findViewById()的方法获取,楼主大神能否解答一下,虽然我看了后面的跟帖,发现楼主有更新代码,我也下载了,我还是想知道为什么不能通过寻找id的方式对刷心头和下拉头进行初始化
77楼  qq_22081545 2015-07-27 17:31发表 [回复]
楼主啊 你这加什么才能把数据刷新啊 在哪儿加
76楼  灵雨飘零 2015-07-22 19:31发表 [回复]
你好,请问如何定义默认加载多少条数据?上拉加载多少条数据?
75楼  大浪滔滔 2015-07-09 16:20发表 [回复]
下拉正在刷新的时候,快速上拉的时候也会加载更多,这个好像没有控制?
74楼  D_Mouse 2015-07-02 15:01发表 [回复]
[java]  view plain  copy
  1. // 是否允许改变下拉的距离,如果是左右滑动则false。  
  2.     private boolean isChangeDist = false;  
  3. // 按下X坐标,上一个事件点Y坐标  
  4.     private float downX, lastX;  
  5.   
  6. if (Math.abs(ev.getX() - downX) > Math.abs(ev.getY()  
  7.                             - downY)) {  
  8.                         isChangeDist = false;  
  9.                     } else {  
  10.                         isChangeDist = true;  
  11.                     }  
  12.   
  13. if (isChangeDist == true) {  
  14.                         // 对实际滑动距离做缩小,造成用力拉的感觉  
  15.                         pullDownY = pullDownY + (ev.getY() - lastY) / radio;  
  16.                     }  
Re:  D_Mouse 2015-07-02 15:05发表 [回复]
回复D_Mouse:当listview addheadview viewpager 左右滑动的同时也会上下滑动
我修改了一下
顺便问下楼主qq
73楼  莫书 2015-07-01 19:05发表 [回复]
楼主你好首先非常感谢您的代码,然后有个问题想请教一下,在布局文件中,通常都是一个下拉刷新和上拉刷新中间夹着一个类似一个gridview控件,如果想要在中间添加一个或者几个新的自定义控件,能否添加,谢谢!
72楼  莫书 2015-07-01 19:02发表 [回复]
楼主你好首先非常感谢您的代码,然后有个问题想请教一下,在布局文件中,通常都是一个下拉刷新和上拉刷新中间夹着一个类似一个gridview控件,如果想要在中间添加一个或者几个新的自定义控件,能否添加,谢谢!
71楼  dreamsever 2015-06-12 17:05发表 [回复]
大神能不能提示下怎样改动实现只要上拉的弹性效果,不要下拉的刷新布局,也不要刷新功能
70楼  dreamsever 2015-06-12 15:25发表 [回复]
灰常感谢,找了很久这种通用的下拉布局,但是还是有点小bug,就是我里面包括的是滑动删除SwipeMenuListView,当滑动删除时有闪屏现象
69楼  zhangfei_jiayou 2015-06-11 13:27发表 [回复]
实现效果还不错,思路也挺好,就是代码写的比较乱,真想用的时候需要大幅重构一遍。
68楼  太多的人 2015-06-08 17:31发表 [回复]
跟我想象的不一样啊,我以为是可以包含其他布局
67楼  randystar 2015-05-18 16:38发表 [回复]
((PullToRefreshLayout) findViewById(R.id.refresh_view))
.setOnRefreshListener(new MyListener());


报空指针异常,是咋回事
66楼  qq_25446695 2015-05-14 17:31发表 [回复]
您好,我在一个viewpager中添加了几个fragment,第一个一面用的刷新,在正在刷新的时候,滑动切换页面app会崩坏,请问怎么解决,谢谢
Re:  qq_527235890 2016-05-08 23:24发表 [回复]
回复qq_25446695:你好,请问你知道这个怎么实现加载或刷新添加新数据吗,比如加载后在源数据项之后添加5组新数据,我是新手,求救啊。
Re:  baidu_33372335 2016-07-15 14:58发表 [回复]
回复qq_527235890:这个问题你解决了吗?
Re:  j610622106 2015-05-16 10:32发表 [回复]
回复qq_25446695:你的问题解决了吗?我也遇见同样问题,已经解决,QQ:872780008
65楼  xoxo4326 2015-05-14 15:25发表 [回复]
首先感谢提供楼主给力的源码,修修改改你的代码快一天了,学了不少,最后想问下loadmoreFinish 方法里面怎么取消回弹效果,footer布局想直接隐藏,谢谢
Re:  陈靖_ 2015-05-14 17:32发表 [回复]
回复xoxo4326:直接赋值上拉偏移量为0即可
64楼  IT屌丝_陈 2015-05-07 16:19发表 [回复]
大神你好 ,赞一个写的下拉上拉刷新。
我发现一个小BUG 自己试着改了下 但是没成功
就是下拉正在刷新时,快速的往上滑动,此时头部的布局不会完全隐藏。

我打印了日志 pullDownY=0,就不会执行updateHandler中隐藏的方法,如果我把updateHandler中隐藏的条件pullDownY>0修改成pullDownY>=0此时 能够解决下拉出现的问题。但如果这样做了上拉会出现问题,麻烦看下谢谢!
Re:  陈靖_ 2015-05-07 19:12发表 [回复]
回复chenxincsdn:我在handleMessage里最后加了这句代码:
// 没有拖拉或者回弹完成
if (pullDownY + Math.abs(pullUpY) == 0)
timer.cancel();
应该已经不会出现这个bug了。。代码已经更新到github了,你可以再测试一下,时间久了我对这代码也有点陌生(⊙o⊙)…
Re:  qq_25446695 2015-05-14 17:29发表 [回复]
回复zhongkejingwang:找到了,找到了
Re:  qq_25446695 2015-05-14 15:25发表 [回复]
回复zhongkejingwang:您的github怎么找
63楼  IT屌丝_陈 2015-05-07 16:16发表 [回复]
大神你好 ,赞一个写的下拉上拉刷新。
我发现一个小BUG 自己试着改了下 但是没成功
就是下拉正在刷新时,快速的往上滑动,此时头部的布局不会完全隐藏。

我打印了日志 pullDownY=0,就不会执行updateHandler中隐藏的方法,如果我把updateHandler中隐藏的条件pullDownY>0修改成pullDownY>=0此时 能够解决下拉出现的问题。但如果这样做了上拉会出现问题,麻烦看下谢谢!
62楼  joney_hy 2015-05-06 18:19发表 [回复] [引用] [举报]
请问gridview如何添加headerView

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值