但效果都不是太好,然后自己根据实例的思路,自己梳理了一下,自己也写了一把,感觉也不是太好,在此做下记录吧,希望对初入ANDROID开发的新手们有所帮助,直接上代码吧
package cn.zan.control.view; import java.util.Date; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import cn.zan.zan_society.R; /** * Gening 2013-10-31 **/ public class CustomListView extends ListView { private static final int DONE = 0; private static final int PULL_TO_REFRESH = 1; private static final int RELEASE_TO_REFRESH = 2; private static final int REFRESHING = 3; private static final float RATIO = 3; private int state; // 当前下拉刷新的状态 private int firstVisibleIndex; // ListView 中第一个可以看见的Item下标 /***************************** ListView 头部 View 控件 ***************************/ private View headView; // ListView 头部 View private ImageView headArrow; // ListView 头部 View的箭头 private ProgressBar progressBar; // ListView 头部 View的读取转圈 private TextView headTitle; // ListView 头部 View里的文字 private TextView headLastUpdate; // ListView 头部 View里的文字,最后更新时间 private int headContentHeight; // ListView 头部 View的高度 private Animation animation; private Animation reverseAnimation; private boolean isRefreshable; private boolean isRecored = false; // 用来记录第一次按下坐标点,在整个滑动的过程中 只记录一次 private float startY; private boolean isBack = false; private boolean isFootLoading = false; // 正在加载底部数据标识 private boolean hasFoot = false; // 是否有了底部 View(FootView) private int lastPos; // 最后一个可见的item的位置 private int count; // ListView Item总数,注意不是当前可见的item总数 public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); if (isInEditMode()) { return; } init(context); } private void init(Context context) { // listview 设置滑动时缓冲背景色 setCacheColorHint(0x00000000); headView = View.inflate(context, R.layout.head, null); headArrow = (ImageView) headView.findViewById(R.id.head_arrow); progressBar = (ProgressBar) headView.findViewById(R.id.progressbar); headTitle = (TextView) headView.findViewById(R.id.head_title); headLastUpdate = (TextView) headView.findViewById(R.id.head_last_update); headArrow.setMinimumWidth(50); headArrow.setMinimumHeight(70); MeasureView(headView); headContentHeight = headView.getMeasuredHeight(); headView.setPadding(0, -1 * headContentHeight, 0, 0); addHeaderView(headView); // 为 ListView加入顶部 View this.setOnScrollListener(custom_listview_onscroll_lis); animation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(250); animation.setFillAfter(true); // 设定动画结束时,停留在动画结束位置 (保留动画效果) animation.setInterpolator(new LinearInterpolator()); // 匀速变化 reverseAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); reverseAnimation.setDuration(200); reverseAnimation.setFillAfter(true);// 设定动画结束时,停留在动画结束位置 (保留动画效果) reverseAnimation.setInterpolator(new LinearInterpolator());// 匀速变化 // 设置当前headView的状态 state = DONE; // 设置当前下拉刷新是否可用 isRefreshable = false; } /** 测量headView的 宽高 **/ private void MeasureView(View child) { ViewGroup.LayoutParams lp = child.getLayoutParams(); if (null == lp) { lp = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); } int measureChildWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width); int measureChildHeight; if (lp.height > 0) { measureChildHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); } else { measureChildHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(measureChildWidth, measureChildHeight); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (isRefreshable) { if (firstVisibleIndex == 0 && !isRecored) { startY = event.getY(); isRecored = true; } } break; case MotionEvent.ACTION_MOVE: if (isRefreshable) { float tempY = event.getY(); if (firstVisibleIndex == 0 && !isRecored) { startY = tempY; isRecored = true; } if (isRecored) { if (tempY - startY < 0) { break; } if (state != REFRESHING) { if (state == PULL_TO_REFRESH) { if ((tempY - startY) / RATIO >= headContentHeight && (tempY - startY) > 0) { // 向下拉了 从下拉刷新的状态 来到 松开刷新的状态 state = RELEASE_TO_REFRESH; changeHeadViewOfState(); } else if ((tempY - startY) <= 0) { // 向上推了 从下拉刷新的状态 来到 刷新完成的状态 state = DONE; changeHeadViewOfState(); } } else if (state == RELEASE_TO_REFRESH) { // 向上推了 还没有完全将HEADVIEW 隐藏掉(可以看到一部分) if ((tempY - startY) / RATIO < headContentHeight && (tempY - startY) > 0) { // 从松开刷新的状态 来到 下拉刷新的状态 state = PULL_TO_REFRESH; changeHeadViewOfState(); isBack = true; } else if ((tempY - startY) <= 0) { // 向上推了 一下子推到了最上面 从松开刷新的状态 来到 刷新完成的状态 (数据不刷新的) state = DONE; changeHeadViewOfState(); } } else if (state == DONE) { // 刷新完成的状态 来到 下拉刷新的状态 if ((tempY - startY) > 0) { state = PULL_TO_REFRESH; changeHeadViewOfState(); } } if (state == PULL_TO_REFRESH) headView.setPadding(0, (int) ((tempY - startY) / RATIO - headContentHeight), 0, 0); if (state == RELEASE_TO_REFRESH) headView.setPadding(0, (int) ((tempY - startY) / RATIO - headContentHeight), 0, 0); } } } break; case MotionEvent.ACTION_UP: if (isRefreshable) { if (state != REFRESHING) { if (state == PULL_TO_REFRESH) { // 松手 state = DONE; changeHeadViewOfState(); } else if (state == RELEASE_TO_REFRESH) { // 松手 state = REFRESHING; changeHeadViewOfState(); // 执行数据刷新方法 onRefresh(); } } isRecored = false; isBack = false; } break; } return super.onTouchEvent(event); } /** 改变下拉刷新时,头部控件显示样式 **/ private void changeHeadViewOfState() { switch (state) { case PULL_TO_REFRESH: headArrow.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); headTitle.setVisibility(View.VISIBLE); headLastUpdate.setVisibility(View.VISIBLE); headArrow.clearAnimation(); headTitle.setText("下拉可以刷新"); // 由 松开刷新 到 下拉刷新 if (isBack) { headArrow.startAnimation(animation); isBack = false; } break; case RELEASE_TO_REFRESH: headArrow.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); headTitle.setVisibility(View.VISIBLE); headLastUpdate.setVisibility(View.VISIBLE); headArrow.clearAnimation(); headArrow.startAnimation(reverseAnimation); headTitle.setText("松开可以刷新"); break; case REFRESHING: headArrow.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); headTitle.setVisibility(View.VISIBLE); headLastUpdate.setVisibility(View.VISIBLE); headArrow.clearAnimation(); headTitle.setText("正在刷新..."); headView.setPadding(0, 0, 0, 0); break; case DONE: headArrow.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); headTitle.setVisibility(View.VISIBLE); headLastUpdate.setVisibility(View.VISIBLE); headArrow.clearAnimation(); headTitle.setText("下拉可以刷新"); headView.setPadding(0, -1 * headContentHeight, 0, 0); break; } } /** ListView 监听事件 **/ private OnScrollListener custom_listview_onscroll_lis = new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (!isFootLoading) { // 没有滚动 if (hasFoot && scrollState == SCROLL_STATE_IDLE) { isFootLoading = true; onFootLoading(); } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { firstVisibleIndex = firstVisibleItem; lastPos = getLastVisiblePosition(); count = totalItemCount; // 因为刚进入的时候,lastPos=-1,count=0,这个时候不能让它执行onAddFoot方法 if (lastPos == count - 1 && !hasFoot && lastPos != -1) { hasFoot = true; onAddFoot(); } } }; /** 下拉刷新监听 **/ private OnRefreshListner refreshListner; // 刷新监听器 /** 设置下拉刷新监听器 **/ public void setOnRefreshListner(OnRefreshListner listener) { isRefreshable = true; refreshListner = listener; } /** 执行下拉刷新操作 **/ private void onRefresh() { if (refreshListner != null) { refreshListner.onRefresh(); } } /** 添加底部View(FootView)监听器 **/ public OnAddFootListener onAddFootListener; /** 设置添加Foot监听器 **/ public void setOnAddFootListener(OnAddFootListener addFootListener) { onAddFootListener = addFootListener; } /** 执行添加Foot **/ public void onAddFoot() { if (onAddFootListener != null && hasFoot) { onAddFootListener.addFoot(); } } /** 上拉加载,监听器 **/ public OnFootLoadingListener footLoadingListener; public void setOnFootLoadingListener(OnFootLoadingListener footLoading) { footLoadingListener = footLoading; } /** 执行底部加载 **/ public void onFootLoading() { if (footLoadingListener != null && isFootLoading) { footLoadingListener.onFootLoading(); } } /********************************************************************** 监听器 **************************************************************************/ /** 下拉刷新监听器 **/ public interface OnRefreshListner { // 下拉刷新的时候,在这里执行获取数据的过程 void onRefresh(); } /** 添加Foot的监听器 **/ public interface OnAddFootListener { // 这里是用户addFootView的操作 void addFoot(); } /** 上拉加载监听器 **/ public interface OnFootLoadingListener { // 上拉加载的时候,在这里行后台获取数据的过程 void onFootLoading(); } /******************************************************************** 对外调用方法 *****************************************************************/ /** 底部数据加载完成,用户需要加入一个removeFootView的操作 **/ public void onFootLoadingComplete() { hasFoot = false; isFootLoading = false; } /** 上拉刷新完成时 所执行的操作,更改状态,隐藏head **/ public void onRefreshComplete() { state = DONE; changeHeadViewOfState(); headLastUpdate.setText("最后刷新时间: " + new Date().toLocaleString()); } @Override public void setAdapter(ListAdapter adapter) { headLastUpdate.setText("最后刷新时间: " + new Date().toLocaleString()); super.setAdapter(adapter); } }
在外部使用的时候,
需使用下拉刷新,直接实现CustomListView的OnRefreshListenner接口
需使用上拉加载,先实现Custom的setOnAddFootListener接口为ListView赋上想要显示的布局,然后再实现OnFootLoadingListener接口执行加载时需要做的请求、数据解析及ListView数据源刷新。最后需调用CustomListView.OnFootLoadingComplete()回复初始状态 和 CustomListView.removeFooterView()移除FootViewW布局。
大概实现就是这样,在此因为时间的原因没有写上实现的思路,有时间补上。但是此实例有明显的不足之处。
1. 下拉刷新与上拉加载的效果太突兀,还有待优化。
2. 下拉刷新与上拉加载的touch事件实现上区分的还不是很清晰明确,还有待优化。