package com.marshalchen.common.uimodule.pulltozoomview; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.os.SystemClock; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.FrameLayout; import android.widget.ListView; import com.marshalchen.common.uimodule.R; /** * Author: ZhuWenWu * Version V1.0 * Date: 2014/9/1 10:50. * Description: * Modification History: * Date Author Version Description * ----------------------------------------------------------------------------------- * 2014/9/1 ZhuWenWu 1.0 1.0 * Why & What is modified: */ public class PullToZoomListView extends ListView implements AbsListView.OnScrollListener { private static final int INVALID_VALUE = -1; private static final String TAG = PullToZoomListView.class.getSimpleName(); private static final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float paramAnonymousFloat) { float f = paramAnonymousFloat - 1.0F; return 1.0F + f * (f * (f * (f * f))); } }; private FrameLayout mHeaderContainer; private View mHeaderView; private OnScrollListener mOnScrollListener; private ScalingRunnable mScalingRunnable; private int mScreenHeight; private int mScreenWidth; private int mHeaderHeight; private int mActivePointerId = -1; private float mLastMotionY = -1.0F; private float mLastScale = -1.0F; private float mMaxScale = -1.0F; private boolean isParallax = true; private boolean isHideHeader = false; private boolean isEnableZoom = true; public PullToZoomListView(Context paramContext) { this(paramContext, null); } public PullToZoomListView(Context paramContext, AttributeSet paramAttributeSet) { this(paramContext, paramAttributeSet, 0); } public PullToZoomListView(Context paramContext, AttributeSet attrs, int paramInt) { super(paramContext, attrs, paramInt); init(attrs); } private void endScaling() { if (mHeaderContainer.getBottom() >= mHeaderHeight) Log.d(TAG, "endScaling"); mScalingRunnable.startAnimation(200L); } private void init(AttributeSet attrs) { mHeaderContainer = new FrameLayout(getContext()); if (attrs != null) { LayoutInflater mLayoutInflater = LayoutInflater.from(getContext()); //初始化状态View TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PullToZoomListView); int headViewResId = a.getResourceId(R.styleable.PullToZoomListView_listHeadView, 0); if (headViewResId > 0) { mHeaderView = mLayoutInflater.inflate(headViewResId, null, false); mHeaderContainer.addView(mHeaderView); isHideHeader = false; } else { isHideHeader = true; } isParallax = a.getBoolean(R.styleable.PullToZoomListView_isHeadParallax, true); a.recycle(); } DisplayMetrics localDisplayMetrics = new DisplayMetrics(); ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics); mScreenHeight = localDisplayMetrics.heightPixels; mScreenWidth = localDisplayMetrics.widthPixels; if (mHeaderView != null) { setHeaderViewSize(mScreenWidth, (int) (9.0F * (mScreenWidth / 16.0F))); addHeaderView(mHeaderContainer); } mScalingRunnable = new ScalingRunnable(); super.setOnScrollListener(this); } public void setParallax(boolean isParallax) { this.isParallax = isParallax; } public void setEnableZoom(boolean isEnableZoom) { this.isEnableZoom = isEnableZoom; } public void setHeaderView(View headerView) { if (mHeaderView != null) { removeHeaderView(mHeaderContainer); } this.mHeaderView = headerView; updateHeaderView(headerView); } private void updateHeaderView(View headerView) { if (headerView != null) { mHeaderContainer.removeAllViews(); mHeaderContainer.addView(mHeaderView); setHeaderViewSize(mScreenWidth, (int) (9.0F * (mScreenWidth / 16.0F))); mHeaderHeight = mHeaderContainer.getHeight(); addHeaderView(mHeaderContainer); } } public View getHeaderView() { return mHeaderView; } public boolean isHideHeader() { return isHideHeader; } public void showHeadView() { if (mHeaderView != null && isHideHeader) { isHideHeader = false; updateHeaderView(mHeaderView); } } public void hideHeadView() { if (mHeaderView != null && !isHideHeader) { isHideHeader = true; removeHeaderView(mHeaderContainer); } } private void onSecondaryPointerUp(MotionEvent paramMotionEvent) { int i = (paramMotionEvent.getAction()) >> 8; if (paramMotionEvent.getPointerId(i) == mActivePointerId) if (i != 0) { mLastMotionY = paramMotionEvent.getY(0); mActivePointerId = paramMotionEvent.getPointerId(0); } } private void reset() { mActivePointerId = -1; mLastMotionY = -1.0F; mMaxScale = -1.0F; mLastScale = -1.0F; } public boolean onInterceptTouchEvent(MotionEvent paramMotionEvent) { return super.onInterceptTouchEvent(paramMotionEvent); } protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2, int paramInt3, int paramInt4) { super.onLayout(paramBoolean, paramInt1, paramInt2, paramInt3, paramInt4); if (mHeaderHeight == 0 && mHeaderView != null) { mHeaderHeight = mHeaderContainer.getHeight(); } } @Override public void onScroll(AbsListView paramAbsListView, int paramInt1, int paramInt2, int paramInt3) { Log.d(TAG, "onScroll"); if (mHeaderView != null && !isHideHeader && isEnableZoom) { float f = mHeaderHeight - mHeaderContainer.getBottom(); Log.d(TAG, "f = " + f); if (isParallax) { if ((f > 0.0F) && (f < mHeaderHeight)) { int i = (int) (0.65D * f); mHeaderContainer.scrollTo(0, -i); } else if (mHeaderContainer.getScrollY() != 0) { mHeaderContainer.scrollTo(0, 0); } } } if (mOnScrollListener != null) { mOnScrollListener.onScroll(paramAbsListView, paramInt1, paramInt2, paramInt3); } } public void onScrollStateChanged(AbsListView paramAbsListView, int paramInt) { if (mOnScrollListener != null) { mOnScrollListener.onScrollStateChanged(paramAbsListView, paramInt); } } public boolean onTouchEvent(MotionEvent paramMotionEvent) { Log.d(TAG, "action = " + (0xFF & paramMotionEvent.getAction())); if (mHeaderView != null && !isHideHeader && isEnableZoom) { switch (0xFF & paramMotionEvent.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_OUTSIDE: if (!mScalingRunnable.isFinished()) { mScalingRunnable.abortAnimation(); } mLastMotionY = paramMotionEvent.getY(); mActivePointerId = paramMotionEvent.getPointerId(0); mMaxScale = (mScreenHeight / mHeaderHeight); mLastScale = (mHeaderContainer.getBottom() / mHeaderHeight); break; case MotionEvent.ACTION_MOVE: Log.d(TAG, "mActivePointerId" + mActivePointerId); int j = paramMotionEvent.findPointerIndex(mActivePointerId); if (j == INVALID_VALUE) { Log.e("PullToZoomListView", "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); } else { if (mLastMotionY == -1.0F) { mLastMotionY = paramMotionEvent.getY(j); } if (mHeaderContainer.getBottom() >= mHeaderHeight) { ViewGroup.LayoutParams localLayoutParams = mHeaderContainer.getLayoutParams(); float f = ((paramMotionEvent.getY(j) - mLastMotionY + mHeaderContainer.getBottom()) / mHeaderHeight - mLastScale) / 2.0F + mLastScale; if ((mLastScale <= 1.0D) && (f < mLastScale)) { localLayoutParams.height = mHeaderHeight; mHeaderContainer.setLayoutParams(localLayoutParams); return super.onTouchEvent(paramMotionEvent); } mLastScale = Math.min(Math.max(f, 1.0F), mMaxScale); localLayoutParams.height = ((int) (mHeaderHeight * mLastScale)); if (localLayoutParams.height < mScreenHeight) { mHeaderContainer.setLayoutParams(localLayoutParams); } mLastMotionY = paramMotionEvent.getY(j); return true; } mLastMotionY = paramMotionEvent.getY(j); } break; case MotionEvent.ACTION_UP: reset(); endScaling(); break; case MotionEvent.ACTION_CANCEL: int i = paramMotionEvent.getActionIndex(); mLastMotionY = paramMotionEvent.getY(i); mActivePointerId = paramMotionEvent.getPointerId(i); break; case MotionEvent.ACTION_POINTER_DOWN: onSecondaryPointerUp(paramMotionEvent); mLastMotionY = paramMotionEvent.getY(paramMotionEvent.findPointerIndex(mActivePointerId)); break; } } return super.onTouchEvent(paramMotionEvent); } public void setHeaderViewSize(int paramInt1, int paramInt2) { if (mHeaderView != null) { Object localObject = mHeaderContainer.getLayoutParams(); if (localObject == null) { localObject = new LayoutParams(paramInt1, paramInt2); } ((ViewGroup.LayoutParams) localObject).width = paramInt1; ((ViewGroup.LayoutParams) localObject).height = paramInt2; mHeaderContainer.setLayoutParams((ViewGroup.LayoutParams) localObject); mHeaderHeight = paramInt2; } } public void setOnScrollListener(OnScrollListener paramOnScrollListener) { mOnScrollListener = paramOnScrollListener; } class ScalingRunnable implements Runnable { protected long mDuration; protected boolean mIsFinished = true; protected float mScale; protected long mStartTime; ScalingRunnable() { } public void abortAnimation() { mIsFinished = true; } public boolean isFinished() { return mIsFinished; } public void run() { if (mHeaderView != null) { float f2; ViewGroup.LayoutParams localLayoutParams; if ((!mIsFinished) && (mScale > 1.0D)) { float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) mStartTime) / (float) mDuration; f2 = mScale - (mScale - 1.0F) * PullToZoomListView.sInterpolator.getInterpolation(f1); localLayoutParams = mHeaderContainer.getLayoutParams(); if (f2 > 1.0F) { Log.d(TAG, "f2>1.0"); localLayoutParams.height = ((int) (f2 * mHeaderHeight)); mHeaderContainer.setLayoutParams(localLayoutParams); post(this); return; } mIsFinished = true; } } } public void startAnimation(long paramLong) { if (mHeaderView != null) { mStartTime = SystemClock.currentThreadTimeMillis(); mDuration = paramLong; mScale = ((float) (mHeaderContainer.getBottom()) / mHeaderHeight); mIsFinished = false; post(this); } } } }