原理:
生成一个FrameLayout,然后装载ImageView,在手指按下的时候设置初始值mLastMotionY、mActivePointerId、mMaxScale、mLastScale;在手指移动的时候动态的改变localLayoutParams.height;当手指抬起时,生成一个Runnable,设置缩放动画,更改localLayoutParams.height
public class PullToZoomListView extends ListView implements AbsListView.OnScrollListener { private static final int INVALID_VALUE = -1; private static final String TAG = "PullToZoomListView"; private static final Interpolator sInterpolator = new Interpolator() {//缩放减速 public float getInterpolation(float paramAnonymousFloat) { float f = paramAnonymousFloat - 1.0F;//下拉-1 - 0 Log.e("getInterpolation", "f | " + f); return 1.0F + f * (f * (f * (f * f))); } }; int mActivePointerId = -1; private FrameLayout mHeaderContainer;//头部布局容器 private int mHeaderHeight;//头部控件的高度 private ImageView mHeaderImage; float mLastMotionY = -1.0F;//监听手指在Y轴的移动 float mLastScale = -1.0F;//下拉:-1 - 2 float mMaxScale = -1.0F;//3 private AbsListView.OnScrollListener mOnScrollListener; private ScalingRunnalable mScalingRunnalable;//缩放动画 private int mScreenHeight;//屏幕高度 private ImageView mShadow; public PullToZoomListView(Context paramContext) { super(paramContext); init(paramContext); } public PullToZoomListView(Context paramContext, AttributeSet paramAttributeSet) { super(paramContext, paramAttributeSet); init(paramContext); } public PullToZoomListView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) { super(paramContext, paramAttributeSet, paramInt); init(paramContext); } //手抬起时,设置缩放动画时间 private void endScraling() { Log.e(TAG, "endScraling"); if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) this.mScalingRunnalable.startAnimation(200L); } private void init(Context paramContext) { DisplayMetrics localDisplayMetrics = new DisplayMetrics(); ((Activity) paramContext).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics); this.mScreenHeight = localDisplayMetrics.heightPixels; this.mHeaderContainer = new FrameLayout(paramContext);//生成一个FrameLayout,装载下面两个ImageView this.mHeaderImage = new ImageView(paramContext); int i = localDisplayMetrics.widthPixels;//屏幕宽度(像素) setHeaderViewSize(i, (int) (9.0F * (i / 16.0F))); this.mShadow = new ImageView(paramContext); FrameLayout.LayoutParams localLayoutParams = new FrameLayout.LayoutParams(-1, -2); localLayoutParams.gravity = 80; this.mShadow.setLayoutParams(localLayoutParams); this.mHeaderContainer.addView(this.mHeaderImage); this.mHeaderContainer.addView(this.mShadow); addHeaderView(this.mHeaderContainer); this.mScalingRunnalable = new ScalingRunnalable();//生成一个缩放动画 super.setOnScrollListener(this); } private void onSecondaryPointerUp(MotionEvent paramMotionEvent) { Log.e(TAG, "onSecondaryPointerUp = " + paramMotionEvent.getAction() + ""); int i = (paramMotionEvent.getAction()) >> 8; if (paramMotionEvent.getPointerId(i) == this.mActivePointerId) if (i != 0) { int j = 1; this.mLastMotionY = paramMotionEvent.getY(0); this.mActivePointerId = paramMotionEvent.getPointerId(0); return; } } //重置参数 private void reset() { this.mActivePointerId = -1; this.mLastMotionY = -1.0F; this.mMaxScale = -1.0F; this.mLastScale = -1.0F; } public ImageView getHeaderView() { return this.mHeaderImage; } 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); Log.e(TAG, "onLayout " + " mHeaderHeight == " + this.mHeaderContainer.getHeight()); if (this.mHeaderHeight == 0) this.mHeaderHeight = this.mHeaderContainer.getHeight(); } //正在滚动 ; 上滑启动 @Override public void onScroll(AbsListView paramAbsListView, int paramInt1, int paramInt2, int paramInt3) { Log.e(TAG, "onScroll"); float f = this.mHeaderHeight - this.mHeaderContainer.getBottom(); Log.e(TAG, "f|" + f + " mHeaderContainer.getBottom()|" + this.mHeaderContainer.getBottom()); if ((f > 0.0F) && (f < this.mHeaderHeight)) { Log.e(TAG, "1"); int i = (int) (0.65D * f); this.mHeaderImage.scrollTo(0, -i); } else if (this.mHeaderImage.getScrollY() != 0) { Log.e(TAG, "2"); this.mHeaderImage.scrollTo(0, 0); } if (this.mOnScrollListener != null) { this.mOnScrollListener.onScroll(paramAbsListView, paramInt1, paramInt2, paramInt3); } } //滚动状态完成后 public void onScrollStateChanged(AbsListView paramAbsListView, int paramInt) { if (this.mOnScrollListener != null) this.mOnScrollListener.onScrollStateChanged(paramAbsListView, paramInt); } public boolean onTouchEvent(MotionEvent ev) { Log.e(TAG, "MotionEvent == " + (0xFF & ev.getAction())); switch (ev.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_OUTSIDE: case MotionEvent.ACTION_DOWN: if (!this.mScalingRunnalable.mIsFinished) { this.mScalingRunnalable.abortAnimation(); } this.mLastMotionY = ev.getY();//触摸时y轴的点 this.mActivePointerId = ev.getPointerId(0);//0 this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);//屏幕高度/头部控件的高度 this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);//504/504 break; case MotionEvent.ACTION_MOVE: int j = ev.findPointerIndex(this.mActivePointerId);//id永远为0 if (j == -1) { Log.e(TAG, "Invalid pointerId=" + this.mActivePointerId + " in onTouchEvent"); } else { if (this.mLastMotionY == -1.0F) this.mLastMotionY = ev.getY(j); if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) {//mHeaderHeight = 405 ViewGroup.LayoutParams localLayoutParams = this.mHeaderContainer.getLayoutParams(); float f = ((ev.getY(j) - this.mLastMotionY + this.mHeaderContainer.getBottom()) / this.mHeaderHeight - this.mLastScale) / 2.0F + this.mLastScale; //下拉:f从1.0 - 2.0 Log.e(TAG, "localLayoutParams.height = " + localLayoutParams.height + " localLayoutParams.width = " + localLayoutParams.width + " f = " + f); if ((this.mLastScale <= 1.0D) && (f < this.mLastScale)) { localLayoutParams.height = this.mHeaderHeight; this.mHeaderContainer.setLayoutParams(localLayoutParams); return super.onTouchEvent(ev); } this.mLastScale = Math.min(Math.max(f, 1.0F), this.mMaxScale); localLayoutParams.height = ((int) (this.mHeaderHeight * this.mLastScale)); if (localLayoutParams.height < this.mScreenHeight) this.mHeaderContainer.setLayoutParams(localLayoutParams); this.mLastMotionY = ev.getY(j); return true; } this.mLastMotionY = ev.getY(j); } break; case MotionEvent.ACTION_UP: reset(); endScraling(); break; case MotionEvent.ACTION_CANCEL: int i = ev.getActionIndex(); this.mLastMotionY = ev.getY(i); this.mActivePointerId = ev.getPointerId(i); break; case MotionEvent.ACTION_POINTER_DOWN://设置多点触控 onSecondaryPointerUp(ev); this.mLastMotionY = ev.getY(ev.findPointerIndex(this.mActivePointerId)); break; case MotionEvent.ACTION_POINTER_UP: } return super.onTouchEvent(ev); } //初始化头部控件的布局 public void setHeaderViewSize(int paramInt1, int paramInt2) { Object localObject = this.mHeaderContainer.getLayoutParams(); if (localObject == null) localObject = new AbsListView.LayoutParams(paramInt1, paramInt2); ((ViewGroup.LayoutParams) localObject).width = paramInt1; ((ViewGroup.LayoutParams) localObject).height = paramInt2; this.mHeaderContainer.setLayoutParams((ViewGroup.LayoutParams) localObject); this.mHeaderHeight = paramInt2; } //设置接收通知的侦听器,监听列表每次滚动。 public void setOnScrollListener( AbsListView.OnScrollListener paramOnScrollListener) { this.mOnScrollListener = paramOnScrollListener; } //用线程缩放 class ScalingRunnalable implements Runnable { long mDuration;//持续时间 boolean mIsFinished = true;//动画是否结束 float mScale;//缩放比例 long mStartTime;//开始时间 ScalingRunnalable() { } //终止动画 public void abortAnimation() { this.mIsFinished = true; } //开始缩放 public void run() { float f2; ViewGroup.LayoutParams localLayoutParams; if ((!this.mIsFinished) && (this.mScale > 1.0D)) { float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) this.mStartTime) / (float) this.mDuration; Log.e(TAG, "mTime" + " mStartTime = " + mStartTime + " ---- SystemClock.currentThreadTimeMillis() = " + SystemClock.currentThreadTimeMillis()); f2 = this.mScale - (this.mScale - 1.0F) * PullToZoomListView.sInterpolator.getInterpolation(f1); localLayoutParams = PullToZoomListView.this.mHeaderContainer.getLayoutParams(); if (f2 > 1.0F) {//下拉:f2==2 - 1 f1==0 - 1 Log.e(TAG, "f2>1.0" + " f2 = " + f2 + " ---- f1 = " + f1); localLayoutParams.height = ((int) (f2 * PullToZoomListView.this.mHeaderHeight));//动态的控制这个参数,改变图片的变化 PullToZoomListView.this.mHeaderContainer.setLayoutParams(localLayoutParams); PullToZoomListView.this.post(this); return; } this.mIsFinished = true; } } //记录抬起手指的坐标,设置动画属性 public void startAnimation(long paramLong) { this.mStartTime = SystemClock.currentThreadTimeMillis(); this.mDuration = paramLong; this.mScale = ((float) (PullToZoomListView.this.mHeaderContainer.getBottom()) / PullToZoomListView.this.mHeaderHeight); Log.e("startAnimation", "--------------->mScale + " + mScale + "\nBottom = " + PullToZoomListView.this.mHeaderContainer.getBottom() + "\nmHeaderHeight = " + PullToZoomListView.this.mHeaderHeight); this.mIsFinished = false; PullToZoomListView.this.post(this); } } }