使用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