package com.inuker.bluetooth.view; import android.content.Context; import android.os.CountDownTimer; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import com.inuker.bluetooth.R; import com.inuker.bluetooth.StringUtils; public class PullHeadView extends ViewGroup{ private final static int UPDATING_IDLE = 0; private final static int UPDATING_READY = 1; private final static int UPDATING_ON_GOING = 2; private final static int UPDATING_FINISH = 3; private final static int MAX_PULL_HEIGHT_DP = 200; private final static int MAX_DURATION = 350; private final static int INVALID_STATE = -1; private final static String SP_NAME = "pull_list_update_time"; private int mHeight; private int mDistance; private int mInitHeight; private boolean mImediateUpdate = false; private int mUpdateHeight; private boolean mCanUpdate; private int mNextState = INVALID_STATE; int mUpdatingStatus = UPDATING_IDLE; private int mMaxPullHeight; private Context mContext; private String mTimeTag; private ProgressBar pbRefreshing; private ImageView ivArrow; private TextView tvRefreshText; private TextView tvRefreshTime; private RotateAnimation mFlipAnimation; private RotateAnimation mReverseFlipAnimation; private String strRefreshing; private String strPullRefresh; private String strReleaseRefresh; private CloseTimer mUpdateTimer; private CloseTimer mResettimer; private OnStateListerer mListener; private static final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float t) { t -= 1.0f; return t * t * t + 1.0f; } }; public PullHeadView(Context context) { super(context); initView(context); } public PullHeadView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public PullHeadView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context); } private void initView(Context context){ this.mContext = context; mMaxPullHeight = (int) (context.getResources().getDisplayMetrics().density* MAX_PULL_HEIGHT_DP + 0.5f); LayoutInflater.from(context).inflate(R.layout.view_layout_list_head, this, true); pbRefreshing = (ProgressBar) findViewById(R.id.pb_refresh); ivArrow = (ImageView) findViewById(R.id.imageview_refresh); tvRefreshText = (TextView) findViewById(R.id.tv_pull_to_refresh_text); tvRefreshTime = (TextView) findViewById(R.id.tv_pull_to_refresh_time); strPullRefresh = mContext.getString(R.string.pull_to_refresh_pull_label); strReleaseRefresh = mContext.getString(R.string.release_to_refresh_pull_label); strRefreshing = mContext.getString(R.string.string_refreshing); applyPullHeadViewTheme(); /** * 初始化动画 */ mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(200); mFlipAnimation.setFillAfter(true); /** * 反转动画 */ mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mReverseFlipAnimation.setInterpolator(new LinearInterpolator()); mReverseFlipAnimation.setDuration(200); mReverseFlipAnimation.setFillAfter(true); } public void showInitState() { ivArrow.setVisibility(View.VISIBLE); pbRefreshing.setVisibility(View.GONE); tvRefreshText.setText(strPullRefresh); } public void showInitTime(){ String lastTime = getLastTimeLable(); /* SLog.v(TAG, "lastTime:"+lastTime); */ if (lastTime != null) { tvRefreshTime.setVisibility(View.VISIBLE); tvRefreshTime.setText(lastTime); } else { tvRefreshTime.setVisibility(View.GONE); } } public void showRefreshingState() { ivArrow.clearAnimation(); ivArrow.setVisibility(View.GONE); pbRefreshing.setVisibility(View.VISIBLE); tvRefreshText.setText(strRefreshing); } /** * 只有它在从release to refresh是才需要显示动画 * */ public void showPullState() { ivArrow.setVisibility(View.VISIBLE); pbRefreshing.setVisibility(View.GONE); tvRefreshText.setText(strPullRefresh); ivArrow.clearAnimation(); ivArrow.startAnimation(mReverseFlipAnimation); } public void showReleaseState() { ivArrow.setVisibility(View.VISIBLE); pbRefreshing.setVisibility(View.GONE); tvRefreshText.setText(strReleaseRefresh); ivArrow.clearAnimation(); ivArrow.startAnimation(mFlipAnimation); } public void setStateListener(OnStateListerer listener){ mListener = listener; } public void setTimeTag(String timeTag) { this.mTimeTag = timeTag; } /** * 初始化上次更新时间 */ private String getLastTimeLable() { if(mTimeTag != null){ long time = mContext.getSharedPreferences(SP_NAME, 0).getLong(mTimeTag, 0); String format = mContext.getString(R.string.string_update_time, StringUtils.getTimeDisplayNameNormal(time)); return format; }else { return "最后更新于:刚刚"; } } /** * 刷新成功后调用 */ public void updateLastTimeLable() { if (mTimeTag != null && mTimeTag.length() > 0) { long curTime = System.currentTimeMillis(); mContext.getSharedPreferences(SP_NAME, 0).edit() .putLong(mTimeTag, curTime).commit(); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final View childView = getChildView(); if (childView == null) { return; } final int childViewWidth = childView.getMeasuredWidth(); final int childViewHeight = childView.getMeasuredHeight(); final int measuredHeight = getMeasuredHeight(); childView.layout(0, measuredHeight - childViewHeight, childViewWidth, measuredHeight); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); if (mHeight < 0) { mHeight = 0; } setMeasuredDimension(width, mHeight); final View childView = getChildView(); if (childView != null) { childView.measure(widthMeasureSpec, heightMeasureSpec); mUpdateHeight = childView.getMeasuredHeight(); /* SLog.v(TAG, "mUpdateHeight"+mUpdateHeight); */ } } public void startUpdate() { mUpdatingStatus = UPDATING_READY; mInitHeight = mHeight; mDistance = mInitHeight - mUpdateHeight; if (mDistance < 0) { mDistance = mInitHeight; } int duration = (int) (mDistance * 3); duration = duration > MAX_DURATION ? MAX_DURATION : duration; /* Log.d(VIEW_LOG_TAG, "duration:" + duration); */ mUpdateTimer = new CloseTimer(duration); mUpdateTimer.startTimer(); } public void reset(int nextState) { if(mUpdatingStatus == UPDATING_ON_GOING){ mUpdatingStatus = UPDATING_FINISH; } mDistance = mInitHeight = mHeight; int duration = (int) (mDistance * 4); duration = duration > MAX_DURATION ? MAX_DURATION : duration; mNextState = nextState; if(mUpdateTimer != null){ mUpdateTimer.cancel(); } mResettimer = new CloseTimer(duration); mResettimer.startTimer(); } public boolean isUpdateNeeded() { if (mImediateUpdate) { mImediateUpdate = false; return true; } final int distance = mHeight - mUpdateHeight; boolean needUpdate = distance >= 0; return needUpdate; } public void moveToUpdateHeight() { showInitTime(); setHeaderHeight(mUpdateHeight); mImediateUpdate = true; } private class CloseTimer extends CountDownTimer { private long mStart; private float mDurationReciprocal; private static final int COUNT_DOWN_INTERVAL = 15; public CloseTimer(long millisInFuture) { super(millisInFuture, COUNT_DOWN_INTERVAL); mDurationReciprocal = 1.0f / millisInFuture; } public void startTimer() { mStart = AnimationUtils.currentAnimationTimeMillis(); start(); } @Override public void onFinish() { if (mNextState != INVALID_STATE) { mNextState = INVALID_STATE; if(mListener != null){ mListener.onReset(); } } setHeaderHeight((int) (mInitHeight - mDistance)); } @Override public void onTick(long millisUntilFinished) { final int interval = (int) (AnimationUtils .currentAnimationTimeMillis() - mStart); float x = interval * mDurationReciprocal; x = sInterpolator.getInterpolation(x); setHeaderHeight((int) (mInitHeight - mDistance * x)); } } private View getChildView() { final int childCount = getChildCount(); if (childCount != 1) { return null; } return getChildAt(0); } public void setHeaderHeight(int height) { /* SLog.v(TAG, "mHeight:"+mHeight+"height:"+height); */ if (mHeight == height && height == 0) { showInitTime(); return; } if (height > mMaxPullHeight) { return; } mHeight = height; if (mUpdatingStatus != UPDATING_IDLE) { if (mUpdatingStatus == UPDATING_READY) { showRefreshingState(); mUpdatingStatus = UPDATING_ON_GOING; } } else { if ((height < mUpdateHeight) && mCanUpdate) { showPullState(); mCanUpdate = false; } else if ((height >= mUpdateHeight) && !mCanUpdate) { showReleaseState(); mCanUpdate = true; } } requestLayout(); if (height == 0) { mUpdatingStatus = UPDATING_IDLE; mCanUpdate = false; showInitState(); } } public void applyPullHeadViewTheme() { ivArrow.setImageResource(R.drawable.refresh_arrow); tvRefreshText.setTextColor(mContext.getResources().getColor(R.color.pull_to_refresh_tips_color)); tvRefreshTime.setTextColor(mContext.getResources().getColor(R.color.pull_to_refresh_time_color)); } /** * 状态重置 * */ public interface OnStateListerer { public void onReset(); } }