转自http://blog.csdn.net/allen315410/article/details/39965327
最近项目中要用到刷新和加载的功能,网上查了有好多关于刷新和加载的例子,挑了一个比较好的,有些地方更完善了些。例如下拉不完全时没有做处理,还有加载后滑动有些问题都一一解决了。话不多说,先看效果图,后直接上代码了。
RefreshListView:
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
private final static String TAG = "RefreshListView";
private View mHeaderView;//头布局对象
private ImageView mImageViewArror;//头布局的箭头
private ProgressBar mProgressBar;//进度条
private TextView mTextViewState;//头布局的状态
private TextView mTextViewTime;//头布局的时间
private int mHeaderViewHeight;//mHeaderView的高度
private Animation mUpAnimation;//顶部的动画
private Animation mDownAnimation;//底部的动画
private View mFooterView;//脚布局对象
private int mFooterViewHeight;//mFooterView的高度
private boolean mIsScrollToBottom;//是否滑动到底部
private boolean mIsLoadingMore = false;//是否正在加载更多中
private OnRefreshListener mOnRefreshListener;
private int mDownY;//按下时y轴的偏移量
private final static int DOWM_PULL_REFRESH = 0;//下拉刷新状态
private final static int RELEASE_REFRESH = 1;//松开刷新
private final static int REFRESHING = 2;//正在刷新
private int currentState = DOWM_PULL_REFRESH;//当前布局的状态,默认为下拉刷新状态
private int mFirstVisibleItemPosition;//屏幕显示在第一个的item的索引
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initFooterView();
this.setOnScrollListener(this);
}
/**
* 初始化头布局
*/
private void initHeaderView() {
mHeaderView = View.inflate(getContext(), R.layout.header, null);
mImageViewArror = (ImageView) mHeaderView.findViewById(R.id.iv_header);
mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.progressBar);
mTextViewState = (TextView) mHeaderView.findViewById(R.id.tv_header_state);
mTextViewTime = (TextView) mHeaderView.findViewById(R.id.tv_header_time);
//设置最后的刷新时间
mTextViewTime.setText("最后刷新时间:" + getLastUpdateTime());
mHeaderView.measure(0, 0);//系统会帮我们测量出mHeaderView的高度
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
this.addHeaderView(mHeaderView);//向ListView的顶部添加一个View对象
initAnimation();
}
/**
* 获取系统最新时间
*
* @return
*/
@TargetApi(Build.VERSION_CODES.N)
private String getLastUpdateTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
return format.format(System.currentTimeMillis());
}
/**
* 初始化动画
*/
private void initAnimation() {
mUpAnimation = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation
.RELATIVE_TO_SELF, 0.5f);
mUpAnimation.setDuration(500);
mUpAnimation.setFillAfter(true);//动画结束后,停留在结束的位置上
mDownAnimation = new RotateAnimation(-180f, -360f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mDownAnimation.setDuration(500);
mDownAnimation.setFillAfter(true);
}
/**
* 初始化脚布局
*/
private void initFooterView() {
mFooterView = View.inflate(getContext(), R.layout.loading, null);
mFooterView.measure(0, 0);
mFooterViewHeight = mFooterView.getMeasuredHeight();
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
this.addFooterView(mFooterView);
}
/**
* 当滚动状态改变时回调
*/
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) {
//判断当前是否已经加载到了底部
if (mIsScrollToBottom && !mIsLoadingMore) {
mIsLoadingMore = true;
//当前到底部
Log.i(TAG, "加载更多数据");
mFooterView.setPadding(0, 0, 0, 0);
this.setSelection(this.getCount());
if (mOnRefreshListener != null) {
mOnRefreshListener.onLoadingMore();
}
}
}
}
/**
* 当滚动时调用
*
* @param firstVisibleItem 当前屏幕显示在顶部的item的position
* @param visibleItemCount 当前屏幕显示了多少个条目的总数
* @param totalItemCount ListView的总条目数
*/
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int
totalItemCount) {
mFirstVisibleItemPosition = firstVisibleItem;
if (getLastVisiblePosition() == (totalItemCount - 1) && !mIsLoadingMore) {
mIsScrollToBottom = true;
} else {
mIsScrollToBottom = false;
}
}
public interface OnRefreshListener {
/**
* 下拉刷新
*/
void onDownPullToRefresh();
/**
* 上拉加载更多
*/
void onLoadingMore();
}
/**
* 设置刷新监听事件
*/
public void setmOnRefreshListener(OnRefreshListener listener) {
this.mOnRefreshListener = listener;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
//移动后的y-按下的y = 间距
int distanceY = (moveY - mDownY) / 2;
//-头布局的高度+间距=paddingTop
int paddingTop = -mHeaderViewHeight + distanceY;
//如果:- 头布局的高度>paddingTop的值,执行super.onTouchEvent(ev);
if (mFirstVisibleItemPosition == 0 && -mHeaderViewHeight < paddingTop) {
if (paddingTop > 0 && currentState == DOWM_PULL_REFRESH) {//完全显示了
Log.i(TAG, "松开刷新");
currentState = RELEASE_REFRESH;
refreshHeaderView();
} else if (paddingTop < 0 && currentState == RELEASE_REFRESH) {
Log.i(TAG, "下拉刷新");
currentState = DOWM_PULL_REFRESH;
refreshHeaderView();
}
//下拉头布局
mHeaderView.setPadding(0, paddingTop, 0, 0);
return true;
}
break;
case MotionEvent.ACTION_UP:
//判断当前状态是松开刷新还是下拉刷新
switch (currentState) {
case RELEASE_REFRESH:
Log.i(TAG, "刷新数据");
//把头布局设置为完全显示状态
mHeaderView.setPadding(0, 0, 0, 0);
//进入到正在刷新中状态
currentState = REFRESHING;
refreshHeaderView();
if (mOnRefreshListener != null) {
mOnRefreshListener.onDownPullToRefresh();//调用使用者的监听方法
}
}
break;
case DOWM_PULL_REFRESH:
//隐藏头布局
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
break;
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
/**
* 根据currentState刷新头布局
*/
private void refreshHeaderView() {
switch (currentState) {
case DOWM_PULL_REFRESH://下拉刷新
mTextViewState.setText("下拉刷新");
mImageViewArror.startAnimation(mDownAnimation);
break;
case RELEASE_REFRESH://松开刷新
mTextViewState.setText("松开刷新");
mImageViewArror.startAnimation(mUpAnimation);
break;
case REFRESHING://正在刷新
mImageViewArror.clearAnimation();
mImageViewArror.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
mTextViewState.setText("正在刷新中...");
break;
}
}
/**
* 隐藏头布局
*/
public void hideHeaderView() {
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
mImageViewArror.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
mTextViewState.setText("下拉刷新");
mTextViewTime.setText("最后刷新时间:" + getLastUpdateTime());
currentState = DOWM_PULL_REFRESH;
}
public void hideFooterView() {
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
mIsLoadingMore = false;
}
}
MainActivity:
public class MainActivity extends AppCompatActivity implements RefreshListView.OnRefreshListener {
private RefreshListView mRefreshListView;
private List<String> mData;
private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRefreshListView = (RefreshListView) findViewById(R.id.refresh_listview);
mData = new ArrayList<>();
for(int i = 0;i < 25 ; i++){
mData.add(i,"这是一条listview的数据"+i);
}
mAdapter = new MyAdapter(mData,this);
mRefreshListView.setAdapter(mAdapter);
mRefreshListView.setmOnRefreshListener(this);
}
@Override
public void onDownPullToRefresh() {
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(2000);
for (int i = 0; i < 2;i++) {
mData.add(0,"这是下拉刷新出来的数据"+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
mAdapter.notifyDataSetChanged();
mRefreshListView.hideHeaderView();
}
}.execute(new Void[]{});
}
@Override
public void onLoadingMore() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(3000);
mData.add("这是加载的数据1");
mData.add("这是加载的数据2");
mData.add("这是加载的数据3");
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
mAdapter.notifyDataSetChanged();
//隐藏脚布局
mRefreshListView.hideFooterView();
}
}.execute(new Void[]{});
}
}
MyAdapter:
public class MyAdapter extends BaseAdapter{
private List<String> mData;
private Context mContext;
private LayoutInflater mInflater;
public MyAdapter(List<String> mData, Context mContext) {
if(mData == null){
this.mData = mData;
}
this.mData = mData;
this.mContext = mContext;
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int i) {
return mData.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
ViewHolder holder;
if(convertView == null){
convertView = mInflater.inflate(R.layout.listview_item,viewGroup,false);
holder = new ViewHolder();
convertView.setTag(holder);
holder.tvItem = (TextView) convertView.findViewById(R.id.tv_item);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.tvItem.setText(mData.get(i));
return convertView;
}
private class ViewHolder {
private TextView tvItem;
}
}
activity_main的布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ihavau.www.pulltorefreshlistviewdemo.MainActivity">
<com.ihavau.www.pulltorefreshlistviewdemo.widget.RefreshListView
android:id="@+id/refresh_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
header.xml的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<ImageView
android:id="@+id/iv_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="30dp"
android:src="@drawable/ptr_pulltorefresh_arrow"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateDrawable="@anim/common_progressbar"
android:visibility="gone"/>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tv_header_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="#FF0000"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_header_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="最后刷新时间:2016-6-7 17:47:30"/>
</LinearLayout>
</LinearLayout>
footer的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:gravity="center"
android:orientation="horizontal">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateDrawable="@anim/common_progressbar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载更多..."
android:textColor="#FF0000"
android:textSize="18sp"/>
</LinearLayout>
</LinearLayout>
listview_item.xml的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:textSize="18sp"/>
</LinearLayout>
代码下载地址:http://download.csdn.net/detail/zuozuoshenghen/9593649