有很多控件都可以下拉刷新如,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中使用的控件。
示例:
代码:
抽象基类: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> -->