使用Ultra-Pull-To-Refresh实现上拉加载和下拉刷新


分享自:http://blog.csdn.net/lisdye2/article/details/51449716

Ultra-Pull-To-Refresh是一个功能非常强大的类库,通过他,我们可以实现非常丰富的下拉刷新视图,并且他支持几乎所有的控件的下拉刷新(不仅仅是ListView),但该视图不支持上拉加载,作者可能在考虑此库设计时的想法与Google官方的SwipeRefreshLayout的理念符合。即刷新可能是许多控件都需要,而上拉加载只有列表视图需要。

那么我们就开始尝试使用他吧。

Android Studio 导入Ultra

  • 在工程的build.gradle中,加上如下代码
  • 1 allprojects {
    2     repositories {
    3         jcenter()
    4         mavenCentral();
    5         maven {
    6             url 'https://oss.sonatype.org/content/repositories/snapshots'
    7         }
    8     }
    9 }
  • 添加依赖包

        1 compile 'in.srain.cube:ultra-ptr:1.0.11' 

  • Clean一下工程即可

Eclipse 导入 Ultra

因为作者提供的只有AndroidStudio版本,所以,我把它代码导出到了一个Eclipse工程中,直接添加依赖即可。

Ultra-Pull-To-Refresh Eclipse 版本下载地址

简单使用

添加xml文件:activity_listview_ultra_refresh

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:orientation="vertical" android:layout_width="match_parent"
 3     android:layout_height="match_parent">
 4 
 5     <in.srain.cube.views.ptr.PtrClassicFrameLayout
 6         android:id="@+id/ultra_ptr_frame"
 7         xmlns:cube_ptr="http://schemas.android.com/apk/res-auto"
 8         android:layout_width="match_parent"
 9         android:layout_height="match_parent"
10         >
11 
12         <FrameLayout
13             android:id="@+id/ultra_refresh_frame"
14             android:layout_width="match_parent"
15             android:layout_height="match_parent"
16             android:background="#f00"
17             android:paddingTop="100dp">
18         </FrameLayout>
19     </in.srain.cube.views.ptr.PtrClassicFrameLayout>
20 
21 
22 </LinearLayout> 

添加了一个PtrClassicFrameLayout包含了一个FrameLayout.

看一下Java代码

 1 public class UltraRefreshActivity extends Activity {
 2 
 3 
 4     private PtrClassicFrameLayout ptrFrame;
 5 
 6     @Override
 7     protected void onCreate(Bundle savedInstanceState) {
 8         super.onCreate(savedInstanceState);
 9 
10         setContentView(R.layout.activity_listview_ultra_refresh);
11 
12         ptrFrame = ((PtrClassicFrameLayout) findViewById(R.id.ultra_ptr_frame));
13 
14         ptrFrame.setLastUpdateTimeRelateObject(this);
15 
16         //下拉刷新的阻力,下拉时,下拉距离和显示头部的距离比例,值越大,则越不容易滑动
17         ptrFrame.setRatioOfHeaderHeightToRefresh(1.2f);
18 
19         ptrFrame.setDurationToClose(200);//返回到刷新的位置(暂未找到)
20 
21         ptrFrame.setDurationToCloseHeader(1000);//关闭头部的时间 // default is false
22 
23         ptrFrame.setPullToRefresh(false);//当下拉到一定距离时,自动刷新(true),显示释放以刷新(false)
24 
25         ptrFrame.setKeepHeaderWhenRefresh(true);//见名只意
26 
27         ptrFrame.setPtrHandler(new PtrHandler() {
28             @Override
29             public boolean checkCanDoRefresh(PtrFrameLayout frame,
30                                              View content, View header) {
31                 return PtrDefaultHandler.checkContentCanBePulledDown(frame,
32                         content, header);
33             }
34 
35             @Override
36             public void onRefreshBegin(PtrFrameLayout frame) {
37 
38                 //数据刷新的回调
39 
40                 ptrFrame.postDelayed(new Runnable() {
41                     @Override
42                     public void run() {
43                         ptrFrame.refreshComplete();
44                     }
45                 }, 1500);
46             }
47         });
48     }
49 }

看一下效果

通过实现过程,我们可以发现,其下拉刷新和其包含的控件没有之间联系:也就是说我们可以在PtrClassicFrameLayout包含任何子控件。

他可以设置的一些常量,代码注释中都有体现。而他有两个关键性的方法:

  • ptrFrame.setPtrHandler(new PtrHandler()):数据刷新的接口回调。包含两个回调方法
  1. checkCanDoRefresh:是否能够刷新。
  2. onRefreshBegin:开始刷新的回调。
  • ptrFrame.addPtrUIHandler(new PtrUIHandler()):UI更新接口的回调。其中PtrUIHandler为一个接口,其定义如下
  •  1 public interface PtrUIHandler {
     2 
     3     /**
     4      * When the content view has reached top and refresh has been completed, view will be reset.
     5      *
     6      * @param frame
     7      */
     8     public void onUIReset(PtrFrameLayout frame);
     9 
    10     /**
    11      * prepare for loading
    12      *
    13      * @param frame
    14      */
    15     public void onUIRefreshPrepare(PtrFrameLayout frame);
    16 
    17     /**
    18      * perform refreshing UI
    19      */
    20     public void onUIRefreshBegin(PtrFrameLayout frame);
    21 
    22     /**
    23      * perform UI after refresh
    24      */
    25     public void onUIRefreshComplete(PtrFrameLayout frame);
    26 
    27     public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator);
    28 }

     含义如下:

    • onUIRefreshPrepare:开始下拉之前的接口回调。
    • onUIRefreshBegin:开始刷新的接口回调。
    • onUIRefreshComplete:刷新完成的接口回调。
    • onUIReset:刷新完成之后,UI消失之后的接口回调。
    • onUIPositionChange:下拉滑动的接口回调,多次调用。 
      • isUnderTouch :手指是否触摸
      • status:状态值
      • ptrIndicator:滑动偏移量等值的封装

那么对于onUIPositionChange中,都是返回了那些值呢,我写了一个自定义的HeadView,并设置高位100,同时打印相应的Log。在这只贴onUIPositionChange中的Log代码。

 1 @Override
 2     public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
 3         int headerHeight = ptrIndicator.getHeaderHeight();//头部的高度
 4         int lastPosY = ptrIndicator.getLastPosY();//上一次滑动的Y偏移值
 5         int offsetToRefresh = ptrIndicator.getOffsetToRefresh();//刷新需要滑动的偏移量
 6         float offsetY = ptrIndicator.getOffsetY();//当前与上一次滑动处理的偏移值
 7         int currentPosY = ptrIndicator.getCurrentPosY();//当前系统偏移值
 8 
 9 
10         Log.i("info","isUnderTouch"+isUnderTouch+"headHeight: "+headerHeight+" lastPosY "+lastPosY+" offsetToRefresh "+offsetToRefresh+" offsetY "+offsetY+" currentPosY "+currentPosY);
11     }

 

看一下打印信息

 1 onUIRefreshPrepare
 2 isUnderTouchtrueheadHeight: 100 lastPosY 0 offsetToRefresh 120 offsetY 23.025852 currentPosY 23
 3 ............................................................
 4 isUnderTouchtrueheadHeight: 100 lastPosY 299 offsetToRefresh 120 offsetY 2.1575928 currentPosY 301
 5 onUIRefreshBegin
 6 isUnderTouchfalseheadHeight: 100 lastPosY 301 offsetToRefresh 120 offsetY 2.1575928 currentPosY 278
 7 ............................................................
 8 isUnderTouchfalseheadHeight: 100 lastPosY 101 offsetToRefresh 120 offsetY 2.1575928 currentPosY 100
 9 onUIRefreshComplete
10 isUnderTouchfalseheadHeight: 100 lastPosY 100 offsetToRefresh 120 offsetY 2.1575928 currentPosY 96
11 ............................................................
12 isUnderTouchtrueheadHeight: 100 lastPosY 239 offsetToRefresh 120 offsetY 1.324391 currentPosY 240
13 onUIRefreshComplete
14 isUnderTouchfalseheadHeight: 100 lastPosY 240 offsetToRefresh 120 offsetY 1.324391 currentPosY 223
15 ............................................................
16 isUnderTouchfalseheadHeight: 100 lastPosY 2 offsetToRefresh 120 offsetY 1.324391 currentPosY 1
17 onUIReset
18 isUnderTouchfalseheadHeight: 100 lastPosY 1 offsetToRefresh 120 offsetY 1.324391 currentPosY 0

 

Ultra自定义刷新

看一下我们将要实现的效果:

自定义头部的代码实现

  1 public class CustomUltraRefreshHeader extends RelativeLayout implements PtrUIHandler {
  2 
  3     CircleView mCircleView;
  4 
  5     TextView mDescText;
  6 
  7     private ObjectAnimator anim;
  8 
  9     public CustomUltraRefreshHeader(Context context) {
 10         this(context,null);
 11     }
 12 
 13     public CustomUltraRefreshHeader(Context context, AttributeSet attrs) {
 14         this(context, attrs,0);
 15     }
 16 
 17     public CustomUltraRefreshHeader(Context context, AttributeSet attrs, int defStyleAttr) {
 18         super(context, attrs, defStyleAttr);
 19 
 20         initView();
 21     }
 22 
 23     @Override
 24     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 25         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 26         setMeasuredDimension(widthMeasureSpec,100);
 27     }
 28 
 29     /**
 30      * 初始化布局
 31      */
 32     private void initView() {
 33 
 34         int circlewidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics());
 35 
 36         mCircleView = new CircleView(getContext());
 37 
 38         LinearLayout.LayoutParams circleParams = new LinearLayout.LayoutParams(circlewidth,circlewidth);
 39 
 40         mCircleView.setLayoutParams(circleParams);
 41 
 42         mDescText = new TextView(getContext());
 43 
 44         LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(circlewidth*3, ViewGroup.LayoutParams.WRAP_CONTENT);
 45 
 46         descParams.gravity = Gravity.CENTER;
 47         descParams.setMargins(circlewidth/2,0,0,0);
 48         mDescText.setLayoutParams(descParams);
 49         mDescText.setTextSize(12);
 50         mDescText.setTextColor(Color.GRAY);
 51         mDescText.setText("下拉刷新");
 52 
 53         //添加线性的父布局
 54         LinearLayout ll = new LinearLayout(getContext());
 55         RelativeLayout.LayoutParams llParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 56         llParams.addRule(CENTER_IN_PARENT);
 57         ll.setLayoutParams(llParams);
 58         ll.setPadding(10,10,10,10);
 59 
 60         ll.addView(mCircleView);
 61         ll.addView(mDescText);
 62 
 63         addView(ll);
 64     }
 65 
 66     @Override
 67     public void onUIReset(PtrFrameLayout frame) {
 68         //重置时,将动画置为初始状态
 69         mCircleView.setRotation(0f);
 70         Log.i("info","onUIReset");
 71     }
 72 
 73     @Override
 74     public void onUIRefreshPrepare(PtrFrameLayout frame) {
 75         mDescText.setText("下拉加载数据");
 76         Log.i("info","onUIRefreshPrepare");
 77     }
 78 
 79     @Override
 80     public void onUIRefreshBegin(PtrFrameLayout frame) {
 81 
 82         //开始刷新,启动动画
 83         anim = ObjectAnimator.ofFloat(mCircleView, "rotation", mCircleView.getRotation(), mCircleView.getRotation()+360f)
 84                 .setDuration(500);
 85         anim.setRepeatCount(ValueAnimator.INFINITE);
 86         anim.setRepeatMode(ValueAnimator.RESTART);
 87         anim.start();
 88 
 89         mDescText.setText("正在加载数据");
 90         Log.i("info","onUIRefreshBegin");
 91     }
 92 
 93     @Override
 94     public void onUIRefreshComplete(PtrFrameLayout frame) {
 95         anim.cancel();
 96         mDescText.setText("加载完成");
 97         Log.i("info","onUIRefreshComplete");
 98     }
 99 
100     @Override
101     public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
102         int headerHeight = ptrIndicator.getHeaderHeight();//头部的高度
103         int lastPosY = ptrIndicator.getLastPosY();//上一次滑动的Y偏移值
104         int offsetToRefresh = ptrIndicator.getOffsetToRefresh();//舒心需要滑动的偏移量
105         float offsetY = ptrIndicator.getOffsetY();//当前与上一次滑动处理的偏移值
106         int currentPosY = ptrIndicator.getCurrentPosY();//当前系统偏移值
107 
108         if (isUnderTouch&&status== PtrFrameLayout.PTR_STATUS_PREPARE) {
109 
110             mCircleView.setRotation(currentPosY);
111             if(currentPosY<offsetToRefresh&&lastPosY >= offsetToRefresh){
112                 //表示不刷新了
113                 mDescText.setText("下拉加载数据");
114             }else if(currentPosY>offsetToRefresh&&lastPosY<=offsetToRefresh){
115                 mDescText.setText("松开加载更多");
116             }
117         }
118 
119        /* if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {
120             if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {*//*
121         Log.i("info","isUnderTouch"+isUnderTouch+"headHeight: "+headerHeight+" lastPosY "+lastPosY+" offsetToRefresh "+offsetToRefresh+" offsetY "+offsetY+" currentPosY "+currentPosY);*/
122     }
123 }

 

在这里我们实现了PtrUIHandler接口,用于下拉刷新时的接口会调,有两点说明:

  • 使用的CircleView为自定义控件,就是图中的转盘,在我以前的博客中有相应的实现。RecyclerView 下拉刷新和上拉加载

  • 在动态改变现实视图时,需要注意的便是onUIPositionChange,我们只有在手指触摸且statusPtrFrameLayout.PTR_STATUS_PREPARE才修改我们的属性,其他时候不能修改。因为该方法会一直回调。

封装自定义的UltraRefreshListView继承ListView,并实现了PtrHandler接口,用以数据更新回调。

  1 public class UltraRefreshListView extends ListView implements PtrHandler,AbsListView.OnScrollListener {
  2 
  3     private UltraRefreshListener mUltraRefreshListener;
  4 
  5     /**
  6      * 根布局
  7      */
  8     private View footView;
  9 
 10 
 11     /**
 12      * 是否正在加载数据
 13      */
 14     private boolean isLoadData = false;
 15 
 16     /**
 17      * 是否是下拉刷新,主要在处理结果时使用
 18      */
 19     private boolean isRefresh = false;
 20 
 21     public UltraRefreshListView(Context context) {
 22         this(context,null);
 23     }
 24 
 25     public UltraRefreshListView(Context context, AttributeSet attrs) {
 26         this(context, attrs,0);
 27     }
 28 
 29     public UltraRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
 30         super(context, attrs, defStyleAttr);
 31         //初始化布局,及添加一个跟布局
 32         initView();
 33     }
 34 
 35     private void initView() {
 36         footView = LayoutInflater.from(getContext()).inflate(R.layout.foot_ultra_refresh_listview, null);
 37 
 38         setOnScrollListener(this);
 39     }
 40 
 41 
 42 
 43     @Override
 44     public void onRefreshBegin(PtrFrameLayout frame) {
 45 
 46         isLoadData  =true;
 47         isRefresh =true;
 48         //下拉刷新的回调
 49         if(mUltraRefreshListener!=null){
 50 
 51             mUltraRefreshListener.onRefresh();
 52         }
 53     }
 54 
 55 
 56     @Override
 57     public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
 58         //  PtrHandler 的接口回调,是否能够加载数据的判断
 59         return !isLoadData&&checkContentCanBePulledDown(frame, content, header);
 60     }
 61 
 62     // 从PtrHandler的默认实现类 PtrDefaultHandler中找到的,用以判断是否可以下拉刷新
 63     public static boolean checkContentCanBePulledDown(PtrFrameLayout frame, View content, View header) {
 64         return !canChildScrollUp(content);
 65 
 66     }
 67 
 68     // 从PtrHandler的默认实现类 PtrDefaultHandler中找到的,用以判断是否可以下拉刷新
 69     public static boolean canChildScrollUp(View view) {
 70         if (android.os.Build.VERSION.SDK_INT < 14) {
 71             if (view instanceof AbsListView) {
 72                 final AbsListView absListView = (AbsListView) view;
 73                 return absListView.getChildCount() > 0
 74                         && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
 75                         .getTop() < absListView.getPaddingTop());
 76             } else {
 77                 return view.getScrollY() > 0;
 78             }
 79         } else {
 80             return view.canScrollVertically(-1);
 81         }
 82     }
 83 
 84 
 85     /**
 86      * 设置ListView的监听回调
 87      */
 88     public void setUltraRefreshListener(UltraRefreshListener mUltraRefreshListener) {
 89         this.mUltraRefreshListener = mUltraRefreshListener;
 90     }
 91 
 92     @Override
 93     public void onScrollStateChanged(AbsListView view, int scrollState) {
 94 
 95     }
 96 
 97     @Override
 98     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 99         /*Log.i("info","isLoadData:"+isLoadData+" totalItemCount "+totalItemCount+" firstVisibleItem "+
100                 firstVisibleItem+" visibleItemCount "+ visibleItemCount);
101 */
102         //加载更多的判断
103         if(totalItemCount>1&&!isLoadData&&totalItemCount==firstVisibleItem+visibleItemCount){
104             isRefresh =false;
105             isLoadData = true;
106             addFooterView(footView);
107             mUltraRefreshListener.addMore();
108         }
109 }
110 
111 
112 
113     //刷新完成的后调用此方法还原布局
114     public void refreshComplete(){
115         isLoadData = false;
116         if(isRefresh){
117             //获取其父控件,刷新
118             ViewParent parent = getParent();
119             if(parent instanceof PtrClassicFrameLayout){
120                 ((PtrClassicFrameLayout) parent).refreshComplete();
121             }
122         }else{
123             removeFooterView(footView);
124         }
125     }
126 }

 

其中有以下几个关键点需要注意:

  • isRefresh:标清当前是正在下拉刷新还是在正在加载更多,这样就不用其调用者在使用refreshComplete()时,传入相应参数。
  • checkCanDoRefresh()方法中,在判断是否可以刷新时,加入了!isLoadData,该目的是,当正在加载更多时,不允许下拉刷新。
  • checkContentCanBePulledDown()canChildScrollUp()方法的实现,是从其源码PtrDefaultHandler中找到的,作者已经给出了比较靠谱的实现方式。

该方法,将下拉刷新和上拉加载整合到了一起,定义一个共同的接口以便进行相应操作的回调

 1 /**
 2  * 数据接口的回调
 3  * Created by Alex_MaHao on 2016/5/18.
 4  */
 5 public interface UltraRefreshListener {
 6 
 7     //下拉刷新
 8     void onRefresh();
 9 
10     //上拉加载
11     void addMore();
12 }

 

最后我们看一下如何使用

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:orientation="vertical" android:layout_width="match_parent"
 3     android:layout_height="match_parent">
 4 
 5     <in.srain.cube.views.ptr.PtrClassicFrameLayout
 6         android:id="@+id/ultra_ptr"
 7         android:layout_width="match_parent"
 8         android:layout_height="match_parent">
 9 
10         <com.mahao.alex.systemwidgetdemo.listView.ultra_refresh.UltraRefreshListView
11             android:id="@+id/ultra_lv"
12             android:layout_width="match_parent"
13             android:layout_height="match_parent"/>
14 
15     </in.srain.cube.views.ptr.PtrClassicFrameLayout>
16 
17 </LinearLayout>

 

 1 /**
 2  * Created by Alex_MaHao on 2016/5/18.
 3  */
 4 public class UltraRefreshListActivity extends AppCompatActivity implements UltraRefreshListener {
 5 
 6     private PtrClassicFrameLayout mPtrFrame;
 7 
 8     private List<String> datas = new ArrayList<>();
 9 
10     private SimpleBaseAdapter mAdapter;
11 
12     private UltraRefreshListView mLv;
13 
14     @Override
15     protected void onCreate(@Nullable Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17 
18         setContentView(R.layout.activity_listview_ultra);
19 
20         //查找控件
21         mPtrFrame = ((PtrClassicFrameLayout) findViewById(R.id.ultra_ptr));
22 
23         mLv = ((UltraRefreshListView) findViewById(R.id.ultra_lv));
24 
25         //创建我们的自定义头部视图
26         CustomUltraRefreshHeader header = new CustomUltraRefreshHeader(this);
27 
28         //设置头部视图
29         mPtrFrame.setHeaderView(header);
30 
31         //设置视图修改的回调,因为我们的CustomUltraRefreshHeader实现了PtrUIHandler
32         mPtrFrame.addPtrUIHandler(header);
33 
34         //设置数据刷新的会回调,因为UltraRefreshListView实现了PtrHandler
35         mPtrFrame.setPtrHandler(mLv);
36 
37         mAdapter = new SimpleBaseAdapter(datas);
38 
39         mLv.setAdapter(mAdapter);
40 
41         //设置数据刷新回调接口
42         mLv.setUltraRefreshListener(this);
43     }
44 
45     @Override
46     public void onRefresh() {
47         mPtrFrame.postDelayed(new Runnable() {
48             @Override
49             public void run() {
50                 datas.clear();
51                 for(int i = 0;i<20;i++){
52                     datas.add("添加了数据~~"+i);
53                 }
54                 //刷新完成
55                 mLv.refreshComplete();
56                 mAdapter.notifyDataSetChanged();
57             }
58         },1000);
59 
60     }
61 
62     @Override
63     public void addMore() {
64         mPtrFrame.postDelayed(new Runnable() {
65             @Override
66             public void run() {
67 
68                 int count = mAdapter.getCount();
69                 for(int i = count; i< count +10; i++){
70                     datas.add("添加了数据~~"+i);
71                 }
72                 mAdapter.notifyDataSetChanged();
73                 //刷新完成
74                 mLv.refreshComplete();
75             }
76         },1000);
77 
78     }
79 }

 

OK,注释你要看不懂,那我也很纠结该怎么办。

该控件非常好用,推荐。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM