package com.cheng.animationstudy.customview.meituan; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.widget.AbsListView; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import com.cheng.animationstudy.R; import com.cheng.utils.Logger; import com.cheng.utils.ViewFinder; import java.text.SimpleDateFormat; /** * 美团下拉刷新ListView */ public class MeiTuanListView extends ListView implements AbsListView.OnScrollListener { /** * 首先我们要定义好几个状态,下拉刷新有这样几个状态: * DONE:隐藏的状态 * PULL_TO_REFRESH:下拉刷新的状态 * RELEASE_TO_REFRESH:松开刷新的状态 * REFRESHING:正在刷新的状态 */ 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 int RATIO = 3; private LinearLayout mHeaderView; private TextView mPullToRefreshTV; private MeiTuanRefreshFirstStepView mMeiTuanRefreshFirstStepView; private MeiTuanRefreshSecondStepView mMeiTuanRefreshSecondStepView; private MeiTuanRefreshThirdStepView mMeiTuanRefreshThirdStepView; private FrameLayout mAnimContainerFL; private OnMeiTuanRefreshListener mOnMeiTuanRefreshListener; private AnimationDrawable mSecondAnim; private AnimationDrawable mThirdAnim; private Animation mAnimation; private int mHeaderViewHeight; private float mStartY; private float mOffsetY; private int mState; private int mFirstVisibleItem; private boolean mIsRecord; private boolean mIsEnd; private boolean mIsRefreable; private SimpleDateFormat mSimpleDateFormat; public interface OnMeiTuanRefreshListener{ void onRefresh(); } public MeiTuanListView(Context context) { super(context); init(context); } public MeiTuanListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public MeiTuanListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { Logger.TAG = "MeiTuanListView"; setOverScrollMode(View.OVER_SCROLL_NEVER); setOnScrollListener(this); this.mHeaderView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.meituan_header, null, false); this.mMeiTuanRefreshFirstStepView = ViewFinder.findViewById(mHeaderView, R.id.first_view); this.mMeiTuanRefreshSecondStepView = ViewFinder.findViewById(mHeaderView, R.id.second_view); this.mMeiTuanRefreshThirdStepView = ViewFinder.findViewById(mHeaderView, R.id.third_view); this.mPullToRefreshTV = ViewFinder.findViewById(mHeaderView, R.id.sdi_pulltorefresh_tv); this.mMeiTuanRefreshSecondStepView.setBackgroundResource(R.drawable.sdd_meituan_pulltorefreshsecondanim); this.mMeiTuanRefreshThirdStepView.setBackgroundResource(R.drawable.sdd_meituan_pulltorefreshthirdanim); this.mSecondAnim = (AnimationDrawable) this.mMeiTuanRefreshSecondStepView.getBackground(); this.mThirdAnim = (AnimationDrawable) this.mMeiTuanRefreshThirdStepView.getBackground(); measureView(mHeaderView); addHeaderView(mHeaderView); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); Logger.i("MeiTuanPullToRefresh--HeaderViewHeight : " + mHeaderViewHeight); this.mState = DONE; this.mIsEnd = true; this.mIsRefreable = false; } public void setOnMeiTuanRefreshListener(OnMeiTuanRefreshListener onMeiTuanRefreshListener) { this.mOnMeiTuanRefreshListener = onMeiTuanRefreshListener; this.mIsRefreable = true; } public void setOnRefreshComplete() { mIsEnd = true; mState = DONE; changeHeaderByState(mState); } @Override public boolean onTouchEvent(MotionEvent ev) { if (!mIsEnd) return super.onTouchEvent(ev); if (!mIsRefreable) return super.onTouchEvent(ev); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: if (mFirstVisibleItem==0 && !mIsRecord) { mIsRecord = true; mStartY = ev.getY(); } break; case MotionEvent.ACTION_MOVE: float tempY = ev.getY(); if (mFirstVisibleItem==0 && !mIsRecord) { mIsRecord = true; mStartY = tempY; } if (mState!=REFRESHING && mIsRecord) { mOffsetY = tempY - mStartY; float currentHeight = (-mHeaderViewHeight+mOffsetY/3); float currentProgress = 1+currentHeight/mHeaderViewHeight; if (currentProgress >= 1) { currentProgress = 1; } if (mState==RELEASE_TO_REFRESH && mIsRecord) { setSelection(0); if (-mHeaderViewHeight+mOffsetY/RATIO < 0) { mState = PULL_TO_REFRESH; changeHeaderByState(mState); } else if (mOffsetY <= 0) { mState = DONE; changeHeaderByState(mState); } } if (mState==PULL_TO_REFRESH && mIsRecord) { setSelection(0); if (-mHeaderViewHeight+mOffsetY/RATIO >= 0) { mState = RELEASE_TO_REFRESH; changeHeaderByState(mState); } else if (mOffsetY <= 0) { mState = DONE; changeHeaderByState(mState); } } if (mState==DONE && mIsRecord) { if (mOffsetY >= 0) { mState = PULL_TO_REFRESH; } } if (mState == PULL_TO_REFRESH) { mHeaderView.setPadding(0, (int)(-mHeaderViewHeight+mOffsetY/RATIO), 0, 0); mMeiTuanRefreshFirstStepView.setCurrentProgress(currentProgress); mMeiTuanRefreshFirstStepView.postInvalidate(); } if (mState == RELEASE_TO_REFRESH) { mHeaderView.setPadding(0, (int)(-mHeaderViewHeight+mOffsetY/RATIO), 0, 0); mMeiTuanRefreshFirstStepView.setCurrentProgress(currentProgress); mMeiTuanRefreshFirstStepView.postInvalidate(); } } break; case MotionEvent.ACTION_UP: if (mState == PULL_TO_REFRESH) { this.smoothScrollBy((int)(-mHeaderViewHeight+mOffsetY/RATIO)+mHeaderViewHeight, 500); changeHeaderByState(mState); } if (mState == RELEASE_TO_REFRESH) { this.smoothScrollBy((int)(-mHeaderViewHeight+mOffsetY/RATIO), 500); mState = REFRESHING; mOnMeiTuanRefreshListener.onRefresh(); changeHeaderByState(mState); } mIsRecord = false; break; } return super.onTouchEvent(ev); } private void changeHeaderByState(int state) { switch (state) { case DONE: mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); mMeiTuanRefreshFirstStepView.setVisibility(VISIBLE); mMeiTuanRefreshSecondStepView.setVisibility(GONE); mMeiTuanRefreshThirdStepView.setVisibility(GONE); mSecondAnim.stop(); mThirdAnim.stop(); break; case RELEASE_TO_REFRESH: mPullToRefreshTV.setText("放开刷新"); mMeiTuanRefreshFirstStepView.setVisibility(GONE); mMeiTuanRefreshSecondStepView.setVisibility(VISIBLE); mMeiTuanRefreshThirdStepView.setVisibility(GONE); mSecondAnim.start(); mThirdAnim.stop(); break; case PULL_TO_REFRESH: mPullToRefreshTV.setText("下拉刷新"); mMeiTuanRefreshFirstStepView.setVisibility(VISIBLE); mMeiTuanRefreshSecondStepView.setVisibility(GONE); mMeiTuanRefreshThirdStepView.setVisibility(GONE); mSecondAnim.stop(); mThirdAnim.stop(); mState = DONE; break; case REFRESHING: mPullToRefreshTV.setText("正在刷新"); mMeiTuanRefreshFirstStepView.setVisibility(GONE); mMeiTuanRefreshSecondStepView.setVisibility(GONE); mMeiTuanRefreshThirdStepView.setVisibility(VISIBLE); mSecondAnim.stop(); mThirdAnim.start(); break; } } 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 childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0+0, lp.width); int lpHeight = lp.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.mFirstVisibleItem = firstVisibleItem; } }