有很多控件都可以下拉刷新如,ListView,ExpandableListView,GridView,ScrollView,ViewPager,WebView等。

自定义一个通用的下拉刷新控件


 主要思路:

  一个ViewGroup,最上面是刷新控件,下面是显示正文的控件,刷新控件只在产生下拉事件时才显示出来。其它时候一直是隐藏的。

 具体步骤:

  a,定义一个ViewGroup或者Layout 的抽象模板类,模板参数为T(T是View的子类)

  b,布局,顶部是一个LinearLayout,它里面是一个ImageView(刷新时旋转图片)和一个TextView(文字提示)。在LinearLayout下是显示正文的T

  c,重写onInterceptTouchEvent,当满足下拉条件时,拦截下面正文T的事件。其它时候不拦截。

  d,定义一个AsyncTask,定义接口,注意回调。

  e,定义模板类的子类。参数可为ListView,ScrollView等。这些子类就是要在layout.xml中使用的控件。

 示例:

ios 下拉刷新 实现 下拉刷新控件_ui

 代码:

抽象基类:PRVBase.java



1 import android.content.Context;
  2 import android.graphics.Canvas;
  3 import android.graphics.Color;
  4 import android.os.AsyncTask;
  5 import android.util.AttributeSet;
  6 import android.view.Gravity;
  7 import android.view.MotionEvent;
  8 import android.view.View;
  9 import android.view.ViewGroup;
 10 import android.view.animation.Animation;
 11 import android.view.animation.RotateAnimation;
 12 import android.widget.ImageView;
 13 import android.widget.LinearLayout;
 14 import android.widget.TextView;
 15 
 16 import com.e.weixin.R;
 17 
 18 public abstract class PRVBase<T extends View> extends LinearLayout {
 19     
 20     protected abstract T createContentView(Context context, AttributeSet attrs);
 21     protected abstract void backgroundRefreshing();
 22     protected abstract void refreshed();
 23     protected abstract boolean canPull();
 24     
 25     Context context;
 26     
 27     LinearLayout refreshView;
 28     ImageView refreshIcon;
 29     TextView refreshText;
 30     float preDegrees = 0.0f;
 31     
 32     T content;
 33     
 34     public void setContentView(T v){
 35         content = v;
 36     }
 37     void addRefreshView(){
 38         refreshView = new LinearLayout(context);
 39         refreshView.setOrientation(HORIZONTAL);
 40         refreshView.setBackgroundColor(Color.parseColor("#f8910a"));
 41         refreshView.setGravity(Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL);
 42         addRefreshIcon();
 43         addRefreshText();
 44         ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(-1,-2);
 45         this.addView(refreshView,params);
 46     }
 47     void addRefreshText(){
 48         refreshText = new TextView(context);
 49         refreshText.setText("pull");
 50         ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(240,-2);
 51         refreshView.addView(refreshText,params);
 52     }
 53     void addRefreshIcon(){
 54         refreshIcon = new ImageView(context);
 55         refreshIcon.setImageDrawable(context.getResources().getDrawable(R.drawable.refreshview_icon));
 56         ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(-2,-2);
 57         refreshView.addView(refreshIcon,params);
 58     }
 59     
 60     void addContent(){
 61         content = createContentView(context, null);
 62         //MATCH_PARENT = -1 , WRAP_CONTENT = -2
 63         ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(-1,-1);
 64         addView(content,params);
 65     }
 66     
 67     public PRVBase(Context context, AttributeSet attrs) {
 68         super(context, attrs);
 69         this.context = context;
 70         this.setOrientation(VERTICAL);
 71         addRefreshView();
 72         addContent();
 73     }
 74     @Override
 75     protected void onLayout(boolean changed, int l, int t, int r, int b) {
 76         System.out.println(String.format("l=%d,t=%d,r=%d,b=%d", l,t,r,b));
 77         
 78         int h = refreshView.getMeasuredHeight();
 79         refreshView.layout(0, -h, r, 0);
 80         
 81         r = getMeasuredWidth();
 82         r = this.getWidth();
 83         b = this.getHeight();
 84         content.layout(0, 0, r, b);
 85     }
 86     protected void refreshViewReleased(MotionEvent event){
 87         int scrollY = getScrollY();
 88         if (scrollY <= -refreshView.getHeight()) {
 89             scrollTo(0, -refreshView.getHeight());
 90             refreshViewRefreshing();
 91         }else{
 92             scrollTo(0, 0);
 93         }
 94     }
 95     protected void refreshViewPulling(MotionEvent event){
 96         int scrollY = getScrollY();
 97         int sz = event.getHistorySize();
 98         if (sz > 0) {
 99             //相对于自身的中心点旋转.  
100             float previousY = event.getHistoricalY(0);//找到本组事件的第一个 ACTION_MOVE 的Y
101             int scrollOffset =  (int) (previousY - event.getY());
102             System.out.println(String.format("%f - %f = %d",previousY,event.getY(),scrollOffset));
103             if (-scrollOffset < scrollY) {
104                 //如果本次移动的距离(previousY - event.getY())大于scrollY到0的距离,头部会移出它正常的显示区域。
105                 scrollOffset = scrollY;
106             }                
107             scrollBy(0,scrollOffset);
108             if (scrollY <= -refreshView.getHeight()) {
109                 refreshText.setText("松手刷新");
110             }else{
111                 refreshText.setText("下拉刷新");
112             }
113             //每次旋转的角度为前一个的角度到本次的角度。
114             //event.getY()是在屏幕上的y,getY是本view在屏幕上的y,
115             //event.getY-getY就是手指到本view顶部的距离。也就是0到getHeigh之间的某个值。
116             //手指从0滑到getHeight是就是从0度到360度
117             float currentDegrees = (event.getY() - getY()) * 360 / getHeight();
118             RotateAnimation ra = new RotateAnimation(preDegrees,currentDegrees,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
119             preDegrees = currentDegrees;
120             ra.setDuration(500);//一个动画的持续时间
121             refreshIcon.startAnimation(ra);
122         }
123     }
124     protected void refreshViewRefreshing(){
125         
126         RotateAnimation iconAnim = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
127         iconAnim.setDuration(400);//一个动画的持续时间
128         iconAnim.setRepeatCount(-1);//动画的次数 小于0 表示无限循环
129         iconAnim.setStartOffset(0);//设置动画时间间隔。
130         iconAnim.setRepeatMode(Animation.RESTART);//动画重复后,下一次开始时的行为,有反向,有继续无限。
131         refreshIcon.startAnimation(iconAnim);
132         
133         AsyncTask<String, String, Void> t = new AsyncTask<String, String, Void>(){
134             @Override
135             protected void onProgressUpdate(String... values) {
136                 
137             }
138             @Override
139             protected Void doInBackground(String... params) {
140                 backgroundRefreshing();
141                 return null;
142             }
143             @Override
144             protected void onPostExecute(Void result) {
145                 refreshed();
146                 scrollTo(0, 0);
147                 refreshIcon.clearAnimation();
148             }
149             
150         };
151         t.execute("refreshing");
152     }
153     public boolean refreViewOnTouchEvent(MotionEvent event){
154         
155         switch (event.getActionMasked()) {
156         case MotionEvent.ACTION_MOVE:
157             refreshViewPulling(event);
158             break;
159         case MotionEvent.ACTION_UP:
160             refreshViewReleased(event);
161             break;
162         default:
163             break;
164         }
165         return true;
166     }
167     @Override
168     public boolean onInterceptTouchEvent(MotionEvent ev) {
169         switch (ev.getAction()) {
170         case MotionEvent.ACTION_DOWN:
171             break;
172         case MotionEvent.ACTION_MOVE:
173             if (ev.getHistorySize() > 0) {
174                 //得到上一个y,上一个y-当前y就可得出是否向下拉动。
175                 float previousY = ev.getHistoricalY(0);
176                 //下面假设content是listview
177                 //当满足canPull条件,并且是向下拉时,拦截后续的move事件,不让listview处理,
178                 //listview收到了down和cancel事件,move,up被拦截,所以它不响应完整的事件。
179                 //显示下拉刷新控件只要move 和 up事件就可以,
180                 if (canPull() && ev.getY() - previousY > 0) {
181                     return true;
182                 }
183             }
184             break;
185         default:
186             break;
187         }
188         return false;
189     }
190     @Override
191     public boolean onTouchEvent(MotionEvent event) {
192         return refreViewOnTouchEvent(event);
193     }
194     @Override
195     protected void onDraw(Canvas canvas) {
196         super.onDraw(canvas);
197     }
198 }



 

子类 PRVListView.java 它是下拉刷新List



1 import java.util.ArrayList;
 2 
 3 import android.content.Context;
 4 import android.graphics.Color;
 5 import android.util.AttributeSet;
 6 import android.view.View;
 7 import android.widget.AbsListView;
 8 import android.widget.ArrayAdapter;
 9 import android.widget.ListView;
10 
11 public class PRVListView extends PRVBase<AbsListView> {
12 
13     ListView listView;
14     ArrayList<String> datas;
15     ArrayAdapter<String> adapter;
16     public PRVListView(Context context, AttributeSet attrs) {
17         super(context, attrs);
18         
19     }
20 
21     @Override
22     protected AbsListView createContentView(Context context,AttributeSet attrs) {
23         listView = new ListView(context,attrs);
24         listView.setBackgroundColor(Color.parseColor("#8ae21f"));
25         
26         datas = new ArrayList<String>();
27         for (int i = 0; i < 30; i++) {
28             datas.add("item"+i);
29         }
30         adapter = new ArrayAdapter<String>(context,
31                 android.R.layout.simple_list_item_1, android.R.id.text1, datas);
32         listView.setAdapter(adapter);
33         return listView;
34     }
35 
36     @Override
37     protected boolean canPull() {
38         View v = listView.getChildAt(0);
39         return v.getTop() == 0 && listView.getFirstVisiblePosition() == 0;
40     }
41 
42     @Override
43     protected void backgroundRefreshing() {
44         try {
45             Thread.sleep(3000);
46         } catch (InterruptedException e) {
47             e.printStackTrace();
48         }
49         int i = 0;
50         while (i < 10) {
51             datas.add("New item" + i);
52             ++i;
53         }
54     }
55     @Override
56     protected void refreshed() {
57         adapter.notifyDataSetChanged();
58     }
59 }



子类 PRVScrollView.java  ,它是ScrollView



1 import android.content.Context;
 2 import android.graphics.Color;
 3 import android.util.AttributeSet;
 4 import android.widget.LinearLayout;
 5 import android.widget.ScrollView;
 6 
 7 public class PRVScrollView extends PRVBase<ScrollView> {
 8     ScrollView scroll;
 9     LinearLayout layout;
10     
11     public PRVScrollView(Context context, AttributeSet attrs) {
12         super(context, attrs);
13     }
14 
15     @Override
16     protected ScrollView createContentView(Context context,
17             AttributeSet attrs) {
18         scroll = new ScrollView(context, attrs);
19         scroll.setBackgroundColor(Color.parseColor("#f891a2"));
20         layout = new LinearLayout(context);
21         scroll.addView(layout);
22         return scroll;
23     }
24 
25     @Override
26     protected boolean canPull() {
27         return true;
28     }
29 
30     @Override
31     protected void backgroundRefreshing() {
32         try {
33             Thread.sleep(1000);
34         } catch (InterruptedException e) {
35             e.printStackTrace();
36         }
37     }
38 
39     @Override
40     protected void refreshed() {
41         
42     }
43 }



在layout.xml中使用



1 <?xml version="1.0" encoding="utf-8"?>
 2 <!-- <merge xmlns:android="http://schemas.android.com/apk/res/android" > -->
 3 
 4 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 5     android:id="@+id/tab_weixin"
 6     android:layout_width="match_parent"
 7     android:layout_height="match_parent"
 8     android:orientation="vertical" >
 9 
10     <TextView
11         android:id="@+id/tab_weixin_notice_layout"
12         android:layout_width="match_parent"
13         android:layout_height="wrap_content"
14         android:background="#BFBFBF"
15         android:gravity="center_horizontal|center_vertical"
16         android:text="@string/find_qq_friends"
17         android:textSize="20sp" />
18 
19     <!-- android:overScrollMode=”never” -->
20 
21     <com.e.weixin.ui.PRVListView android:id="@+id/PRVList"
22         android:layout_width="match_parent"
23         android:layout_height="match_parent" >
24 
25     </com.e.weixin.ui.PRVListView>
26      
27    <!--  <com.e.weixin.ui.PRVScrollView
28         android:id="@+id/PRVScrollView"
29         android:layout_width="match_parent"
30         android:layout_height="match_parent" >
31 
32     </com.e.weixin.ui.PRVScrollView> -->
33 
34 </LinearLayout> <!-- </merge> -->