最近项目中刚好有下拉刷新的需求,于是找了一些开源的资料研究了一下,看到网上有很多实现方式,有的继承Linearlayout,有的重写ScrollView,还有的重写Gridview,看到最多的还是重写listview,这里采用哪一种方式的实现没有对错之分,关键要看需求的设计。
这里还是采用大家用的最多的listview实现下拉刷新的操作。
效果如下图:
下面我们来看下代码:
其主要代码是listview实现:
001 | public class PullToRefreshListView extends ListView implements OnScrollListener { |
003 |
private final static String TAG = "PullToRefreshListView" ; |
006 |
private final static int PULL_To_REFRESH = 0 ; |
008 |
private final static int RELEASE_To_REFRESH = 1 ; |
010 |
private final static int REFRESHING = 2 ; |
012 |
private final static int DONE = 3 ; |
014 |
private LayoutInflater inflater; |
016 |
private LinearLayout headView; |
017 |
private TextView tipsTextview; |
018 |
private TextView lastUpdatedTextView; |
019 |
private ImageView arrowImageView; |
020 |
private ProgressBar progressBar; |
022 |
private RotateAnimation animation; |
023 |
private RotateAnimation reverseAnimation; |
026 |
private boolean isRecored; |
028 |
private int headContentWidth; |
029 |
private int headContentHeight; |
030 |
private int headContentOriginalTopPadding; |
033 |
private int firstItemIndex; |
034 |
private int currentScrollState; |
038 |
private boolean isBack; |
040 |
public OnRefreshListener refreshListener; |
042 |
public PullToRefreshListView(Context context, AttributeSet attrs) { |
043 |
super (context, attrs); |
047 |
public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) { |
048 |
super (context, attrs, defStyle); |
052 |
private void init(Context context) { |
054 |
animation = new RotateAnimation( 0 , - 180 , |
055 |
RotateAnimation.RELATIVE_TO_SELF, 0 .5f, |
056 |
RotateAnimation.RELATIVE_TO_SELF, 0 .5f); |
057 |
animation.setInterpolator( new LinearInterpolator()); |
058 |
animation.setDuration( 100 ); |
059 |
animation.setFillAfter( true ); |
061 |
reverseAnimation = new RotateAnimation(- 180 , 0 , |
062 |
RotateAnimation.RELATIVE_TO_SELF, 0 .5f, |
063 |
RotateAnimation.RELATIVE_TO_SELF, 0 .5f); |
064 |
reverseAnimation.setInterpolator( new LinearInterpolator()); |
065 |
reverseAnimation.setDuration( 100 ); |
066 |
reverseAnimation.setFillAfter( true ); |
068 |
inflater = LayoutInflater.from(context); |
069 |
headView = (LinearLayout) inflater.inflate(R.layout.pull_to_refresh_head, null ); |
071 |
arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView); |
072 |
arrowImageView.setMinimumWidth( 50 ); |
073 |
arrowImageView.setMinimumHeight( 50 ); |
074 |
progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar); |
075 |
tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView); |
076 |
lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView); |
078 |
headContentOriginalTopPadding = headView.getPaddingTop(); |
080 |
measureView(headView); |
081 |
headContentHeight = headView.getMeasuredHeight(); |
082 |
headContentWidth = headView.getMeasuredWidth(); |
084 |
headView.setPadding(headView.getPaddingLeft(), - 1 * headContentHeight, headView.getPaddingRight(), headView.getPaddingBottom()); |
085 |
headView.invalidate(); |
090 |
addHeaderView(headView); |
091 |
setOnScrollListener( this ); |
094 |
public void onScroll(AbsListView view, int firstVisiableItem, int visibleItemCount, int totalItemCount) { |
095 |
firstItemIndex = firstVisiableItem; |
098 |
public void onScrollStateChanged(AbsListView view, int scrollState) { |
099 |
currentScrollState = scrollState; |
102 |
public boolean onTouchEvent(MotionEvent event) { |
103 |
switch (event.getAction()) { |
104 |
case MotionEvent.ACTION_DOWN: |
105 |
if (firstItemIndex == 0 && !isRecored) { |
106 |
startY = ( int ) event.getY(); |
112 |
case MotionEvent.ACTION_CANCEL: |
113 |
case MotionEvent.ACTION_UP: |
115 |
if (state != REFRESHING) { |
119 |
else if (state == PULL_To_REFRESH) { |
121 |
changeHeaderViewByState(); |
124 |
else if (state == RELEASE_To_REFRESH) { |
126 |
changeHeaderViewByState(); |
137 |
case MotionEvent.ACTION_MOVE: |
138 |
int tempY = ( int ) event.getY(); |
140 |
if (!isRecored && firstItemIndex == 0 ) { |
145 |
if (state != REFRESHING && isRecored) { |
147 |
if (state == RELEASE_To_REFRESH) { |
149 |
if ((tempY - startY < headContentHeight+ 20 ) |
150 |
&& (tempY - startY) > 0 ) { |
151 |
state = PULL_To_REFRESH; |
152 |
changeHeaderViewByState(); |
156 |
else if (tempY - startY <= 0 ) { |
158 |
changeHeaderViewByState(); |
167 |
else if (state == PULL_To_REFRESH) { |
169 |
if (tempY - startY >= headContentHeight+ 20 && currentScrollState == SCROLL_STATE_TOUCH_SCROLL) { |
170 |
state = RELEASE_To_REFRESH; |
172 |
changeHeaderViewByState(); |
176 |
else if (tempY - startY <= 0 ) { |
178 |
changeHeaderViewByState(); |
183 |
else if (state == DONE) { |
184 |
if (tempY - startY > 0 ) { |
185 |
state = PULL_To_REFRESH; |
186 |
changeHeaderViewByState(); |
192 |
if (state == PULL_To_REFRESH) { |
193 |
int topPadding = ( int )((- 1 * headContentHeight + (tempY - startY))); |
194 |
headView.setPadding(headView.getPaddingLeft(), topPadding, headView.getPaddingRight(), headView.getPaddingBottom()); |
195 |
headView.invalidate(); |
200 |
if (state == RELEASE_To_REFRESH) { |
201 |
int topPadding = ( int )((tempY - startY - headContentHeight)); |
202 |
headView.setPadding(headView.getPaddingLeft(), topPadding, headView.getPaddingRight(), headView.getPaddingBottom()); |
203 |
headView.invalidate(); |
209 |
return super .onTouchEvent(event); |
213 |
private void changeHeaderViewByState() { |
215 |
case RELEASE_To_REFRESH: |
217 |
arrowImageView.setVisibility(View.VISIBLE); |
218 |
progressBar.setVisibility(View.GONE); |
219 |
tipsTextview.setVisibility(View.VISIBLE); |
220 |
lastUpdatedTextView.setVisibility(View.VISIBLE); |
222 |
arrowImageView.clearAnimation(); |
223 |
arrowImageView.startAnimation(animation); |
225 |
tipsTextview.setText(R.string.pull_to_refresh_release_label); |
229 |
case PULL_To_REFRESH: |
231 |
progressBar.setVisibility(View.GONE); |
232 |
tipsTextview.setVisibility(View.VISIBLE); |
233 |
lastUpdatedTextView.setVisibility(View.VISIBLE); |
234 |
arrowImageView.clearAnimation(); |
235 |
arrowImageView.setVisibility(View.VISIBLE); |
238 |
arrowImageView.clearAnimation(); |
239 |
arrowImageView.startAnimation(reverseAnimation); |
241 |
tipsTextview.setText(R.string.pull_to_refresh_pull_label); |
248 |
headView.setPadding(headView.getPaddingLeft(), headContentOriginalTopPadding, headView.getPaddingRight(), headView.getPaddingBottom()); |
249 |
headView.invalidate(); |
251 |
progressBar.setVisibility(View.VISIBLE); |
252 |
arrowImageView.clearAnimation(); |
253 |
arrowImageView.setVisibility(View.GONE); |
254 |
tipsTextview.setText(R.string.pull_to_refresh_refreshing_label); |
255 |
lastUpdatedTextView.setVisibility(View.GONE); |
261 |
headView.setPadding(headView.getPaddingLeft(), - 1 * headContentHeight, headView.getPaddingRight(), headView.getPaddingBottom()); |
262 |
headView.invalidate(); |
264 |
progressBar.setVisibility(View.GONE); |
265 |
arrowImageView.clearAnimation(); |
267 |
arrowImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow); |
269 |
tipsTextview.setText(R.string.pull_to_refresh_pull_label); |
270 |
lastUpdatedTextView.setVisibility(View.VISIBLE); |
278 |
public void clickRefresh() { |
281 |
changeHeaderViewByState(); |
285 |
public void setOnRefreshListener(OnRefreshListener refreshListener) { |
286 |
this .refreshListener = refreshListener; |
289 |
public interface OnRefreshListener { |
290 |
public void onRefresh(); |
293 |
public void onRefreshComplete(String update) { |
294 |
lastUpdatedTextView.setText(update); |
298 |
public void onRefreshComplete() { |
300 |
changeHeaderViewByState(); |
303 |
private void onRefresh() { |
304 |
if (refreshListener != null ) { |
305 |
refreshListener.onRefresh(); |
310 |
private void measureView(View child) { |
311 |
ViewGroup.LayoutParams p = child.getLayoutParams(); |
313 |
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, |
314 |
ViewGroup.LayoutParams.WRAP_CONTENT); |
316 |
int childWidthSpec = ViewGroup.getChildMeasureSpec( 0 , 0 + 0 , p.width); |
317 |
int lpHeight = p.height; |
320 |
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, |
321 |
MeasureSpec.EXACTLY); |
323 |
childHeightSpec = MeasureSpec.makeMeasureSpec( 0 , |
324 |
MeasureSpec.UNSPECIFIED); |
326 |
child.measure(childWidthSpec, childHeightSpec); |
里面的注释可以说写的相当的详细,我这里就不详细解释了,大家可以直接复制到项目中去看,下面我们来看看,如何使用它:
01 | pullrefresh_lv = (PullToRefreshListView) findViewById(R.id.pullrefresh_lv); |
02 |
testArr = new LinkedList<String>(); |
03 |
testArr.add( "↓↓请下拉我看效果↓↓" ); |
04 |
testAdapter = new TestAdapter(testArr, this ); |
05 |
pullrefresh_lv.setAdapter(testAdapter); |
08 |
.setOnRefreshListener( new PullToRefreshListView.OnRefreshListener() { |
09 |
public void onRefresh() { |
11 |
handler.sendEmptyMessageDelayed( 0 , 2000 ); |
使用起来也非常方便,和原生态listview无太多差别,只是多了一个 setOnRefreshListener方法,对的,这里就是处理刷新动作的地方,我这里没有进行网络操作,只是做了一个假数据演示(延迟刷新),下面来看看Handler里面做的处理:
01 | Handler handler = new Handler() { |
02 |
public void handleMessage(android.os.Message msg) { |
06 |
testArr.addFirst( "刷新第" + i + "条" ); |
08 |
.onRefreshComplete(getString(R.string.pull_to_refresh_update) |
09 |
+ new Date().toLocaleString()); |
10 |
pullrefresh_lv.onRefreshComplete(); |
11 |
testAdapter.notifyDataSetChanged(); |
这里当我们刷新动作完成后,调用onRefreshComplete()结束该动作,到这里,整个下来刷新的操作流程就讲完了。
因本站存储空间有限,稍后将源码上传其它社区,有问题请留言,感谢大家。
代码已上传,附上链接:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=304996&page=1&extra=#pid3147702