PTRListView.java
public class PTRListView extends ListView implements OnScrollListener { private static final int PULL_TO_REFRESH = 0; private static final int RELEASE_TO_REFRESH = 1; private static final int REFRESHING = 2; private static final int DONE = 3; // view组件 private View mHeadView; private ImageView iv_header_ptrl_arrow; private ProgressBar pb_header_ptrl; private TextView tv_header_ptrl_tips; private TextView tv_header_ptrl_time; private int mHeadcontentHeight; private OnRefreshListener mOnRefreshListener; private Animation mPositiveAnimation; private Animation mNegativeAnimation; private int mFirstVisibleIndex; private int mState = DONE; private boolean mIsRecored = false; // 记录startY 起始位置 在整个滑动过程中,只记录一次 private float mStartY; private boolean mIsUndo; public PTRListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public PTRListView(Context context) { super(context); init(context); } private void init(Context context) { // 要在adapter中使用 ,在当前使用会报错 空指针 // View headView = View.inflate(context, R.layout.header_ptrl, null); LayoutInflater inflater = LayoutInflater.from(context); mHeadView = inflater.inflate(R.layout.header_ptrl, null); iv_header_ptrl_arrow = (ImageView) mHeadView.findViewById(R.id.iv_header_ptrl_arrow); pb_header_ptrl = (ProgressBar) mHeadView.findViewById(R.id.pb_header_ptrl); tv_header_ptrl_tips = (TextView) mHeadView.findViewById(R.id.tv_header_ptrl_tips); tv_header_ptrl_time = (TextView) mHeadView.findViewById(R.id.tv_header_ptrl_time); iv_header_ptrl_arrow.setMinimumWidth(70); iv_header_ptrl_arrow.setMinimumHeight(50); measureView(mHeadView); mHeadcontentHeight = mHeadView.getMeasuredHeight(); // 设置headView 与界面上边距的距离 mHeadView.setPadding(0, -1 * mHeadcontentHeight, 0, 0); // 重绘 mHeadView.invalidate(); addHeaderView(mHeadView); setHeaderDividersEnabled(false); mPositiveAnimation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mPositiveAnimation.setDuration(300); mPositiveAnimation.setFillAfter(true); mPositiveAnimation.setInterpolator(new LinearInterpolator());// 设置动画速度 /* * Interpolator 定义了动画的变化速度,可以实现匀速、正加速、负加速、无规则变加速等; * * AccelerateDecelerateInterpolator,延迟减速,在动作执行到中间的时候才执行该特效。 AccelerateInterpolator, 会使慢慢以(float)的参数降低速度。 LinearInterpolator,平稳不变的 DecelerateInterpolator,在中间加速,两头慢 CycleInterpolator,曲线运动特效,要传递float型的参数。 */ mNegativeAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mNegativeAnimation.setDuration(300); mNegativeAnimation.setFillAfter(true); mNegativeAnimation.setInterpolator(new LinearInterpolator()); } // 测量Headview 宽高 private void measureView(View child) { ViewGroup.LayoutParams lp = child.getLayoutParams(); if (lp == null) { lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childMeasureWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width); int childMeasureHeight; if (lp.height > 0) { childMeasureHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);// 适合、匹配 } else { childMeasureHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);// 未指定 } child.measure(childMeasureWidth, childMeasureHeight); } @SuppressWarnings("deprecation") public void onRefreshComplete() { mState = DONE; changeHeadViewState(); tv_header_ptrl_time.setText("最近更新: " + new Date().toLocaleString()); } @Override public boolean onTouchEvent(MotionEvent event) { if (mOnRefreshListener != null) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (mFirstVisibleIndex == 0 && !mIsRecored) { mStartY = event.getY(); mIsRecored = true; } break; case MotionEvent.ACTION_MOVE: if (mIsRecored) { float tempY = event.getY(); if (mState == DONE) { if (tempY - mStartY > 0) { // 刷新完成 --》 进入 下拉刷新 mState = PULL_TO_REFRESH; mHeadView.setPadding(0, (int) (-1 * mHeadcontentHeight + tempY - mStartY), 0, 0); changeHeadViewState(); } } else if (mState == PULL_TO_REFRESH) { if (tempY - mStartY >= mHeadcontentHeight) { // 下啦刷新 --》 松开刷新 mState = RELEASE_TO_REFRESH; changeHeadViewState(); } else if (tempY - mStartY <= 0) { // 下啦刷新 --》 回到 刷新完成 mState = DONE; changeHeadViewState(); } else { mHeadView.setPadding(0, (int) (-1 * mHeadcontentHeight + tempY - mStartY), 0, 0); } } else if (mState == RELEASE_TO_REFRESH) { if (tempY - mStartY < mHeadcontentHeight) { // 松开刷新 --》回到下拉刷新 mState = PULL_TO_REFRESH; mIsUndo = true;// 从松开刷新 回到的下拉刷新 changeHeadViewState(); } } } break; case MotionEvent.ACTION_UP: if (mIsRecored) { mIsRecored = false; if (mState == PULL_TO_REFRESH) { mState = DONE; changeHeadViewState(); } else if (mState == RELEASE_TO_REFRESH) { mState = REFRESHING; changeHeadViewState(); } } break; } } return super.onTouchEvent(event); } // headview状态改变 private void changeHeadViewState() { switch (mState) { case PULL_TO_REFRESH: iv_header_ptrl_arrow.setVisibility(View.VISIBLE); pb_header_ptrl.setVisibility(View.GONE); tv_header_ptrl_tips.setText("下拉刷新"); iv_header_ptrl_arrow.clearAnimation(); // 从松开刷新 回到 下拉刷新,即不执行刷新操作 if (mIsUndo) { iv_header_ptrl_arrow.startAnimation(mPositiveAnimation); mIsUndo = false; } break; case RELEASE_TO_REFRESH: iv_header_ptrl_arrow.setVisibility(View.VISIBLE); pb_header_ptrl.setVisibility(View.GONE); tv_header_ptrl_tips.setText("松开刷新"); iv_header_ptrl_arrow.clearAnimation(); iv_header_ptrl_arrow.startAnimation(mNegativeAnimation); mHeadView.setPadding(0, 0, 0, 0); break; case REFRESHING: iv_header_ptrl_arrow.setVisibility(View.GONE); pb_header_ptrl.setVisibility(View.VISIBLE); tv_header_ptrl_tips.setText("正在刷新中..."); iv_header_ptrl_arrow.clearAnimation(); mHeadView.setPadding(0, 0, 0, 0); onRefresh(); break; case DONE: iv_header_ptrl_arrow.setVisibility(View.VISIBLE); pb_header_ptrl.setVisibility(View.GONE); tv_header_ptrl_tips.setText("下拉刷新"); iv_header_ptrl_arrow.clearAnimation(); mHeadView.setPadding(0, -1 * mHeadcontentHeight, 0, 0); break; } } // 刷新 得到服务器的数据 private void onRefresh() { if (mOnRefreshListener != null) { mOnRefreshListener.onRefresh(); } } // 提供一个对外访问的刷新方法 public void setOnRefreshListener(OnRefreshListener listener) { mOnRefreshListener = listener; } public interface OnRefreshListener { public void onRefresh(); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstVisibleIndex = firstVisibleItem; } }
header_ptrl.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/rl_header_ptrl_margin" > <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" > <ImageView android:id="@+id/iv_header_ptrl_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/arrow" /> <ProgressBar android:id="@+id/pb_header_ptrl" style="?android:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" /> </FrameLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:orientation="vertical" > <TextView android:id="@+id/tv_header_ptrl_tips" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginBottom="@dimen/tv_header_ptrl_tips_marginBottom" android:text="下拉刷新" android:textSize="@dimen/textSize_normal" /> <TextView android:id="@+id/tv_header_ptrl_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="上一次修改的时间" android:textSize="@dimen/textSize_normal" /> </LinearLayout> </RelativeLayout> </LinearLayout>