ListView下拉刷新一般要注意以下几点:
1. listview的头布局
2. 注意标志的应用,即刷新的几个状态,分别是下拉刷新,松开刷新和正在刷新
3. 注意几个动画效果,即箭头旋转,刷新图标旋转等
先上效果图:
下面,我们来分别介绍。
先来说说这个demo里用到的几个方法。
(1)public boolean onTouchEvent(MotionEvent ev):这个方法是手势触摸事件。这里主要用到了MotionEvent.ACTION_DOWN(手势按下)、MotionEvent.ACTION_MOVE(手势滑动)和MotionEvent.ACTION_UP(手指抬起手势)。
下拉刷新,是先将listview的头部隐藏,然后用手势拖动屏幕刷新。那么,listview的头部如何隐藏呢?这里用的是header.setPadding(0,-headerViewHeight,0,0)来隐藏头部,这里不细说,等下介绍。如图1所示,要让listview的头部跟着手势一起向下滑动,则需要记录手指移动的距离。所以,当MotionEvent.ACTION_DOWN(手势按下)触发时,需要一个downY记录手势按下时y的偏移值,然后随着手势的滑动即MotionEvent.ACTION_MOVE(手势滑动)触发时,又需要一个moveY记录移动的偏移值,然后paddingTop=moveY-downY得到的就是手指滑动的距离。然后用paddingTop-headerViewHeight(相减的值设为x)得到头部的顶部到屏幕的顶部距离。如果想x>0则说明头部完全显示,如果x<0则头部没有完全显示。
这里要设置几个标志位
PULL_DOWN_REFRESH(下拉刷新):当头部未完全显示时为下拉刷新状态。
RELEASE_REFRESH(释放刷新):当头部完全显示时为释放刷新状态。
REFRESHING(正在刷新):当手势抬起,并头部完全显示时为正在刷新。
其他的都比较简单,在程序里都有注释,这里就不再赘述了。上代码:
RefreshListView.java
package com.example.yds.pulldowntest;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.AbsListView;
import android.widget.TextView;
import org.w3c.dom.Text;
/**
* Created by yds on 2017/1/10.
*/
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
//头布局视图
private View header;
//头布局高度
private int headerViewHeight;
//按下时y的偏移量
private int downY;
//移动时y的偏移量
private int moveY;
//距离顶部的距离
private int paddingTop;
//listview第一个可见的item项
private int firstVisibleItemPosition;
//下拉刷新
private final int PULL_DOWN_REFRESH = 0;
//释放刷新
private final int RELEASE_REFRESH = 1;
//正在刷新
private final int REFRESHING = 2;
//当前状态,默认为下拉刷新
private int currentState = PULL_DOWN_REFRESH;
//刷新列表构造函数
private Context context;
//刷新监听
private OnRefreshListener mOnRefreshListener;
private ImageView refresh;
private RotateAnimation animation;
//图片向上旋转
private Animation upAnimation;
//图片向下旋转
private Animation downAnimation;
//箭头
private ImageView row;
//提示文本
private TextView header_tv;
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
//初始化头布局
initHeaderView();
//滑动监听
this.setOnScrollListener(this);
this.context = context;
}
/**滚动状态改变时调用,一般用于列表视图和网格视图
*
* @param view
* @param scrollState 有三种值,分别是SCROLL_STATE_IDLE,SCROLL_STATE_TOUCH_SCROLL,SCROLL_STATE_FLING
* SCROLL_STATE_IDLE:当屏幕停止滚动时
* SCROLL_STATE_TOUCH_SCROLL:当屏幕以触屏方式滚动并且手指还在屏幕上时
* SCROLL_STATE_FLING:当用户之前滑动屏幕并抬起手指,屏幕以惯性滚动
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState==SCROLL_STATE_IDLE||scrollState == SCROLL_STATE_FLING){
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
firstVisibleItemPosition = firstVisibleItem;
}
// public void setOnRefreshListener(OnRefreshListener listener){
//
// }
public void setOnRefreshListener(OnRefreshListener listener){
mOnRefreshListener = listener;
}
private void initHeaderView(){
//头布局文件
header = LayoutInflater.from(getContext()).inflate(R.layout.header,null);
//测量头布局,绘制一个视图一般经过measure,layout,draw
header.measure(0,0);
//头布局高度
headerViewHeight = header.getMeasuredHeight();
//设置间隔
header.setPadding(0,-headerViewHeight,0,0);
//加载头布局
this.addHeaderView(header);
refresh = (ImageView) header.findViewById(R.id.refresh);
row = (ImageView) header.findViewById(R.id.row);
header_tv = (TextView) header.findViewById(R.id.header_tv);
initAnimation();
}
private void initAnimation(){
animation = new RotateAnimation(0f,360f, Animation.RELATIVE_TO_SELF,0.48f,Animation.RELATIVE_TO_SELF,0.47f);
animation.setDuration(500);
animation.setRepeatCount(5);
upAnimation = new RotateAnimation(0f,180f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
upAnimation.setDuration(300);
upAnimation.setFillAfter(true);
downAnimation = new RotateAnimation(180f,360f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
downAnimation.setDuration(300);
downAnimation.setFillAfter(true);
// refresh.setAnimation(animation);
}
/**
* 触摸事件
* @param ev
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
//手指按下
case MotionEvent.ACTION_DOWN:
//记录按下时y的偏移量
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE://手指滑动
//记录移动时的y值偏移
moveY = (int) ev.getY();
//可以看成头布局距离屏幕顶部的距离,这里除以2是控制手指滑动
paddingTop = (moveY - downY)/2-headerViewHeight;
if(firstVisibleItemPosition==0&&-headerViewHeight<paddingTop){//必须的条件
//如果paddingTop>0就说明完全显示了,但还要判断当前状态是否是下拉刷新状态,因为正在刷新状态也是完全显示
if(paddingTop>0&¤tState == PULL_DOWN_REFRESH){//完全显示
header_tv.setText("松开刷新");
//将当前状态置为释放刷新
currentState = RELEASE_REFRESH;
changeHeaderByViewState();
}else if(paddingTop<0&¤tState == RELEASE_REFRESH){//没有完全显示,currentState=RELEASE_REFRESH原因是可以先滑到完全显示后再往上滑到不完全显示
currentState = PULL_DOWN_REFRESH;
header_tv.setText("下拉刷新");
changeHeaderByViewState();
}
header.setPadding(0,paddingTop,0,0);
}
break;
case MotionEvent.ACTION_UP:
if(currentState == RELEASE_REFRESH){//完全显示
header.setPadding(0,0,0,0);
currentState = REFRESHING;
changeHeaderByViewState();
if (mOnRefreshListener!=null){
mOnRefreshListener.downPullRefresh();
}
}else if(currentState == PULL_DOWN_REFRESH){//未完全显示
//当手指松开时,若头部未完全显示则隐藏头部
header.setPadding(0,-headerViewHeight,0,0);
}
break;
}
return super.onTouchEvent(ev);
}
private void changeHeaderByViewState(){
switch (currentState){
case PULL_DOWN_REFRESH:
row.startAnimation(downAnimation);
break;
case RELEASE_REFRESH:
row.startAnimation(upAnimation);
break;
case REFRESHING:
row.clearAnimation();
row.setVisibility(View.GONE);
header_tv.setText("正在刷新");
refresh.clearAnimation();
refresh.setVisibility(View.VISIBLE);
refresh.startAnimation(animation);
break;
}
}
public void hideHeaderView(){
header.setPadding(0,-headerViewHeight,0,0);
refresh.setVisibility(View.GONE);
currentState = PULL_DOWN_REFRESH;
header_tv.setText("下拉刷新");
row.setVisibility(View.VISIBLE);
}
}
MainActivity.java
package com.example.yds.pulldowntest;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends Activity implements OnRefreshListener{
private TextView log_tv;
private RefreshListView listView;
private PullDownAdapter adapter;
private Map<String,Object>map;
List<Map<String,Object>>list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
log_tv = (TextView) findViewById(R.id.log_tv);
listView = (RefreshListView) findViewById(R.id.listview);
for (int i = 0 ;i < 5 ; i++){
map = new HashMap<>();
map.put("name","this "+i);
list.add(map);
}
adapter = new PullDownAdapter(this,list);
listView.setOnRefreshListener(this);
listView.setAdapter(adapter);
log_tv.setText(list.get(0).get("name").toString()+"this is null?"+list.get(1).get("name").toString());
}
@Override
public void downPullRefresh() {
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(2800);
map = list.get(0);
map.put("name","这是刷新后的数据");
list.add(map);
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
adapter.notifyDataSetChanged();
listView.hideHeaderView();
}
}.execute(new Void[]{});
}
}