到顶部时下拉刷新,到底部时上拉加载更多!具体如图
第1步,实现一个部分自定义控件,先继承一个ListView
public class RefreshListView extends ListView { //三种刷新的状态 private static final int STATE_PULL_TO_REFRESH=0;//下拉刷新状态 private static final int STATE_RELEASE_TO_REFRESH=1;//释放刷新状态 private static final int STATE_REFRESHING=2;//正在刷新状态
private int currentSate=STATE_PULL_TO_REFRESH;//缺省为下拉刷新状态
private boolean isPullToRrefresh=false;//是否 是下拉刷新 private boolean isLoadMore=false;//是否加载更多 private View headerView;//头部视图 private View footerView;//脚部视图 private ImageView arrowIv;//箭头控件 private ProgressBar progressBar;//进度控件 private TextView titleTv;//头部的标题 private TextView lastTimeTv;//最后刷新时间 private int headerViewHeight;//头部的高度 private int footerHeight;//尾部的高度 private RotateAnimation upAnimation;//向上的动画 private RotateAnimation downAnimation;//向下的动画 private float downY;//按下时的Y轴坐标
//构造方法 public RefreshListView(Context context) { super(context); init();//初使化数据 } public RefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } //1. 初始化方法 private void init() { //1.1 初始化刷新头部 initHeaderView(); //1.2 初始化加载更多底部(脚部) initFooterView(); } // 2. 初始化刷新头 private void initHeaderView() { //2.1 把头布局的xml转化为View对象 headerView = View.inflate(getContext(), R.layout.head_view, null); //2.2 把头布局的xml的根节点视图添加到ListView中,作为listview的头部 this.addHeaderView(headerView); arrowIv=(ImageView) headerView.findViewById(R.id.arrow_iv);//箭头坐标 lastTimeTv=(TextView) headerView.findViewById(R.id.lastTime_tv);//时间坐标 progressBar=(ProgressBar) headerView.findViewById(R.id.refrush_pb);//进度条 titleTv=(TextView) headerView.findViewById(R.id.title_tv);//刷新状态显示 //2.3隐藏刷新头布局 ,通过headView.setPadding(0,top,0,0); top=-headViewHeight 则刚好隐藏头布局 //控件只有在显示的时候才会分配宽和高,假如要在未显示前使用控件的宽和高,需要先测量,再取得测量的宽和高 headerView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); headerViewHeight = headerView.getMeasuredHeight(); hideHeadView(); //2.4 初始化刷新头的动画 initAnimation(); } private void initAnimation() { upAnimation=createRotateAnimation(0,-180); downAnimation=createRotateAnimation(-180,-360); } //创建旋转动画 private RotateAnimation createRotateAnimation(int fromDegrees, int toDegrees) { RotateAnimation ro=new RotateAnimation(fromDegrees, toDegrees, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); ro.setFillAfter(true);//动画停止停在最后一帧 ro.setDuration(500); return ro; } //隐藏头布局 private void hideHeadView() { setHeaderViewPadding(-headerViewHeight); } //设置头布局的paddingTop public void setHeaderViewPadding(int paddingTop){ headerView.setPadding(0, paddingTop, 0, 0); } private void initFooterView() {
//尾部 footerView = View.inflate(getContext(), R.layout.foot_view, null); //添加脚布局到ListView this.addFooterView(footerView); //测量 footerView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); footerHeight = footerView.getMeasuredHeight(); //隐藏脚布局 footerView.setPadding(0, -footerHeight, 0, 0); //设置滚屏监听 this.setOnScrollListener(new OnScrollListener() { /** * 当滚动状态发生改变时回调 * 当处于空闲状态,且当前最后一个显示的item是列表中最后一个条目,则显示脚部视图 * OnScrollListener的三种状态: * SCROLL_STATE_FLING:快速滑动状态,惯性滑动状态,手指没有接触控件 * SCROLL_STATE_TOUCH_SCROLL:触摸滑动状态 * SCROLL_STATE_IDLE:空闲状态 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if(scrollState==OnScrollListener.SCROLL_STATE_IDLE&&getLastVisiblePosition()==getCount()-1&&!isLoadMore){ //显示脚部视图 footerView.setPadding(0, 0, 0, 0); isLoadMore=true; //把列表条目指向最后一个 setSelection(getCount()-1); if(mListener!=null){ mListener.onLoadMore(); } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); } //事件触摸处理 @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN://按下去 downY = ev.getY(); //取得按下去的y的位置 break; case MotionEvent.ACTION_MOVE://滑动 float moveY = ev.getY(); float dy=moveY-downY; //y方向的偏移量 //当向下滑动,即 moveY-downY>0 ,且listview第一个可见的item是listview的第一个列表条目 ,才滑出 刷新头部 if(dy>0&&getFirstVisiblePosition()==0){ isPullToRrefresh=true; int paddingTop=(int) (-headerViewHeight+dy); // -60 +20=-40 //设置头布局的paddingTop setHeaderViewPadding(paddingTop); //当paddingTop小于0(刷新头部没有完全可见),且当前不是下拉刷新状态 if(paddingTop<0&& currentSate!=STATE_PULL_TO_REFRESH){ currentSate=STATE_PULL_TO_REFRESH; updateHeadViewState(); }else if(paddingTop>=0&& currentSate!=STATE_RELEASE_TO_REFRESH){ currentSate=STATE_RELEASE_TO_REFRESH; updateHeadViewState(); } return true; } break; case MotionEvent.ACTION_UP://抬起 if(isPullToRrefresh){ //处理下拉刷新的操作 if(currentSate==STATE_PULL_TO_REFRESH){ //当手指抬起时,且当前的状态是下拉刷新,则隐藏刷新头布局 hideHeadView(); }else if(currentSate==STATE_RELEASE_TO_REFRESH){ currentSate=STATE_REFRESHING; updateHeadViewState(); //调用下拉刷新回调方法 if(mListener!=null){ mListener.onRefreshing(); } } } //本次下拉操作结束 isPullToRrefresh=false; break; } return super.onTouchEvent(ev); } //头布局视图状态更新 private void updateHeadViewState() { switch (currentSate) { case STATE_PULL_TO_REFRESH: //下拉刷新状态 titleTv.setText("下拉刷新"); progressBar.setVisibility(View.INVISIBLE); arrowIv.clearAnimation(); arrowIv.startAnimation(downAnimation); break; case STATE_RELEASE_TO_REFRESH: //释放刷新状态 titleTv.setText("释放刷新"); progressBar.setVisibility(View.INVISIBLE); arrowIv.clearAnimation(); arrowIv.startAnimation(upAnimation); break; case STATE_REFRESHING: titleTv.setText("正在刷新..."); //正在刷新状态 arrowIv.clearAnimation(); arrowIv.setVisibility(View.INVISIBLE); progressBar.setVisibility(View.VISIBLE); setHeaderViewPadding(0); //显示刷新头部 /* if(mOnRefreshListener!=null){ mOnRefreshListener.onRefreshing(); }*/ break; default: break; } } //设置监听接口,实现下拉刷新和加载更多的事件回调 public static interface OnRefreshListener { //下拉刷新回调的方法 void onRefreshing(); //加载更多的回调方法 void onLoadMore(); } private OnRefreshListener mListener; public void setOnRefreshListener(OnRefreshListener l){ mListener=l; } //刷新完成 public void setRefreshComplete() { currentSate=STATE_PULL_TO_REFRESH; hideHeadView(); titleTv.setText("下拉刷新"); progressBar.setVisibility(View.INVISIBLE); arrowIv.setVisibility(View.VISIBLE); SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); lastTimeTv.setText( dateFormat.format(new Date())); } //加载更多完成 public void loadMoreComplete() { //隐藏脚布局 footerView.setPadding(0, -footerHeight, 0, 0); isLoadMore=false; } }