package com.telehot.quan.ui.view; import java.text.SimpleDateFormat; import java.util.Date; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import com.telehot.quan.R; /** * 下拉刷新的listview * @author 燕潇洒 */ public class PullToRefreshListView extends ListView implements OnScrollListener { private static final int STATE_PULL_TO_REFRESH = 1; private static final int STATE_RELEASE_TO_REFRESH = 2; private static final int STATE_REFRESHING = 3; private int mCurrentState = STATE_PULL_TO_REFRESH;// 当前刷新状态 private View mHeaderView; private int mHeaderViewHeight; private int startY = -1; private TextView tvTitle; private TextView tvTime; private ImageView ivArrow; private RotateAnimation animUp; private RotateAnimation animDown; private ProgressBar pbProgress; public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initHeaderView(); // initFooterView(); } public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); initHeaderView(); // initFooterView(); } public PullToRefreshListView(Context context) { super(context); initHeaderView(); // initFooterView(); } /** * 初始化头布局 */ private void initHeaderView() { mHeaderView = View.inflate(getContext(), R.layout.pull_to_refresh_header, null); this.addHeaderView(mHeaderView); tvTitle = (TextView) mHeaderView.findViewById(R.id.tv_title); tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time); ivArrow = (ImageView) mHeaderView.findViewById(R.id.iv_arrow); pbProgress = (ProgressBar) mHeaderView.findViewById(R.id.pb_loading); // 隐藏头布局 mHeaderView.measure(0, 0); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); //燕潇洒,初始化时,不要隐藏这个高度,因为当前项目中首页的标题栏为自定义,正好挡住了头部的高度 // mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); mHeaderView.setPadding(0, 0, 0, 0); initAnim(); setCurrentTime(); } /** * 初始化脚布局 */ private void initFooterView() { mFooterView = View.inflate(getContext(), R.layout.pull_to_refresh_footer, null); this.addFooterView(mFooterView); mFooterView.measure(0, 0); mFooterViewHeight = mFooterView.getMeasuredHeight(); mFooterView.setPadding(0, -mFooterViewHeight, 0, 0); this.setOnScrollListener(this);// 滑动监听 } // 设置刷新时间 private void setCurrentTime() { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = format.format(new Date()); tvTime.setText(time); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: startY = (int) ev.getY(); break; case MotionEvent.ACTION_MOVE: if (startY == -1) {// 当用户按住头条新闻的viewpager进行下拉时,ACTION_DOWN会被viewpager消费掉, // 导致startY没有赋值,此处需要重新获取一下 startY = (int) ev.getY(); } if (mCurrentState == STATE_REFRESHING) { // 如果是正在刷新, 跳出循环 break; } int endY = (int) ev.getY(); int dy = endY - startY; int firstVisiblePosition = getFirstVisiblePosition();// 当前显示的第一个item的位置 // 必须下拉,并且当前显示的是第一个item if (dy > 0 && firstVisiblePosition == 0) { int padding = dy - mHeaderViewHeight;// 计算当前下拉控件的padding值 mHeaderView.setPadding(0, padding, 0, 0); if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH) { // 改为松开刷新 mCurrentState = STATE_RELEASE_TO_REFRESH; refreshState(); } else if (padding < 0 && mCurrentState != STATE_PULL_TO_REFRESH) { // 改为下拉刷新 mCurrentState = STATE_PULL_TO_REFRESH; refreshState(); } return true; } break; case MotionEvent.ACTION_UP: startY = -1; if (mCurrentState == STATE_RELEASE_TO_REFRESH) { mCurrentState = STATE_REFRESHING; refreshState(); // 完整展示头布局 mHeaderView.setPadding(0, 0, 0, 0); // 4. 进行回调 if (mListener != null) { mListener.onRefresh(); } } else if (mCurrentState == STATE_PULL_TO_REFRESH) { // 隐藏头布局 // mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); mHeaderView.setPadding(0, 0, 0, 0);//变成0,解决向上滑动再回到顶部时,又被隐藏了一定的高度//燕潇洒 } break; default: break; } return super.onTouchEvent(ev); } /** * 初始化箭头动画 */ private void initAnim() { animUp = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animUp.setDuration(200); animUp.setFillAfter(true); animDown = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animDown.setDuration(200); animDown.setFillAfter(true); } /** * 根据当前状态刷新界面 */ private void refreshState() { switch (mCurrentState) { case STATE_PULL_TO_REFRESH: tvTitle.setText("下拉刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); ivArrow.startAnimation(animDown); break; case STATE_RELEASE_TO_REFRESH: tvTitle.setText("松开刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); ivArrow.startAnimation(animUp); break; case STATE_REFRESHING: tvTitle.setText("正在刷新..."); ivArrow.clearAnimation();// 清除箭头动画,否则无法隐藏 pbProgress.setVisibility(View.VISIBLE); ivArrow.setVisibility(View.INVISIBLE); break; default: break; } } /** * 刷新结束,收起控件 */ public void onRefreshComplete(boolean success) { if(!isLoadMore) { mHeaderView.setPadding(0, 0, 0, 0); // mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); mCurrentState = STATE_PULL_TO_REFRESH; tvTitle.setText("下拉刷新"); pbProgress.setVisibility(View.INVISIBLE); ivArrow.setVisibility(View.VISIBLE); if (success) {// 只有刷新成功之后才更新时间 setCurrentTime(); } }else { //加载更多 // mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);//隐藏布局 mFooterView.setPadding(0, 0, 0, 0);//隐藏布局 isLoadMore = false; } } // 3. 定义成员变量,接收监听对象 private OnRefreshListener mListener; private View mFooterView; private int mFooterViewHeight; /** * 2. 暴露接口,设置监听 */ public void setOnRefreshListener(OnRefreshListener listener) { mListener = listener; } /** * 1. 下拉刷新的回调接口 * * @author Kevin * @date 2015-10-21 */ public interface OnRefreshListener { public void onRefresh(); //下拉加载更多 public void onLoadMore(); } private boolean isLoadMore;// 标记是否正在加载更多 // 滑动状态发生变化 @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE) {// 空闲状态 int lastVisiblePosition = getLastVisiblePosition(); if (lastVisiblePosition == getCount() - 1 && !isLoadMore) {// 当前显示的是最后一个item并且没有正在加载更多 // 到底了 System.out.println("加载更多..."); isLoadMore = true; mFooterView.setPadding(0, 0, 0, 0);// 显示加载更多的布局 setSelection(getCount() - 1);// 将listview显示在最后一个item上, // 从而加载更多会直接展示出来, 无需手动滑动 //通知主界面加载下一页数据 if(mListener!=null) { mListener.onLoadMore(); } } } } // 滑动过程回调 @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }
上面的代码,由于我的标题栏是自己写的一个布局,所以如果是系统的标题栏的话,请将设置padding的方法,解绑,我已经将padding设置成了0,。下面是head和footor
//header布局 <?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:background="@color/black" android:orientation="horizontal" > <FrameLayout android:layout_weight="1" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" > <ImageView android:visibility="gone" android:id="@+id/iv_arrow" android:layout_width="@dimen/dynamic_height5" android:layout_height="@dimen/dynamic_height5" android:layout_gravity="center" android:src="@mipmap/rainbow_ic" /> <ProgressBar android:id="@+id/pb_loading" android:layout_gravity="center" android:layout_width="@dimen/dynamic_height5" android:layout_height="@dimen/dynamic_height5" android:indeterminateDrawable="@mipmap/rainbow_ic" android:background="@drawable/loadding_shape_layout" android:layout_centerInParent="true" android:visibility="invisible" /> </FrameLayout> <LinearLayout android:visibility="gone" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下拉刷新" android:textColor="@color/red" android:textSize="@dimen/item_text_size" /> <TextView android:id="@+id/tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/dynamic_margin" android:text="2015-10-21 09:00" android:textColor="@color/black" android:textSize="@dimen/item_text_size" /> </LinearLayout> </LinearLayout>footot布局
<?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="match_parent" android:gravity="center" android:background="@color/black" android:orientation="horizontal" > <ProgressBar android:id="@+id/pb_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:indeterminateDrawable="@mipmap/rainbow_ic" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:text="加载中..." android:textColor="#f00" android:textSize="18sp" /> </LinearLayout>