另外一个相同项目的地址https://github.com/Frank-Zhu/PullZoomView

转自http://blog.csdn.net/wangjinyu501/article/details/38367669

之前看过一篇文章,链接是:可以下拉缩放HeaderView的ListView:PullToZoomInListView。说的就是PullToZoomListView,不过这篇文章有个地方需要勘误,就是PullToZoomListView这个控件虽然是github上一个开源项目。不过最美应用并不是使用这个开源项目,而是这个开源项目反编译了最美应用的代码。
  但是,它这个代码是有问题的,当手指在屏幕上滑动的时候会引发onItemClick事件。看了一下它的代码,发现是因为在onTouchEvent()方法中的MotionEvent.ACTION_MOVE中return true了,这样就会消费这个事件,所以导致了onItemClick事件。之后我也反编译了一下最美应用,看了一下这个控件的实现,发现还有其他问题,比如少了一些变量,没有重写onInterceptTouchEvent()方法等,因此我又自己动手把这个控件丰富了一下,使其能够最大限度的接近原始代码,而且可正常使用。修改后代码如下:
[html] view plain copy
 
  1. package com.kince.android.pulltozoomlistview;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Context;  
  5. import android.os.SystemClock;  
  6. import android.util.AttributeSet;  
  7. import android.util.DisplayMetrics;  
  8. import android.util.Log;  
  9. import android.view.MotionEvent;  
  10. import android.view.ViewGroup;  
  11. import android.view.animation.Interpolator;  
  12. import android.widget.AbsListView;  
  13. import android.widget.FrameLayout;  
  14. import android.widget.ImageView;  
  15. import android.widget.ListView;  
  16.   
  17. public class PullToZoomListView extends ListView implements  
  18.         AbsListView.OnScrollListener {  
  19.   
  20.     private static final int INVALID_VALUE = -1;  
  21.     private static final String TAG = "PullToZoomListView";  
  22.     private static final Interpolator sInterpolator = new Interpolator() {  
  23.         public float getInterpolation(float paramAnonymousFloat) {  
  24.             float f = paramAnonymousFloat - 1.0F;  
  25.             return 1.0F + f * (f * (f * (f * f)));  
  26.         }  
  27.     };  
  28.     int mActivePointerId = -1;  
  29.     private FrameLayout mHeaderContainer;  
  30.     private int mHeaderHeight;  
  31.     private ImageView mHeaderImage;  
  32.     float mLastMotionY = -1.0F;  
  33.     float mLastScale = -1.0F;  
  34.     float mMaxScale = -1.0F;  
  35.     private AbsListView.OnScrollListener mOnScrollListener;  
  36.     private ScalingRunnalable mScalingRunnalable;  
  37.     private int mScreenHeight;  
  38.     private ImageView mShadow;  
  39.   
  40.     private boolean mScrollable = true;  
  41.     private boolean mShowHeaderImage = true;  
  42.     private boolean mZoomable = true;  
  43.   
  44.     public PullToZoomListView(Context paramContext) {  
  45.         super(paramContext);  
  46.         init(paramContext);  
  47.     }  
  48.   
  49.     public PullToZoomListView(Context paramContext,  
  50.             AttributeSet paramAttributeSet) {  
  51.         super(paramContext, paramAttributeSet);  
  52.         init(paramContext);  
  53.     }  
  54.   
  55.     public PullToZoomListView(Context paramContext,  
  56.             AttributeSet paramAttributeSet, int paramInt) {  
  57.         super(paramContext, paramAttributeSet, paramInt);  
  58.         init(paramContext);  
  59.     }  
  60.   
  61.     private void endScraling() {  
  62.         if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight)  
  63.             Log.d("mmm", "endScraling");  
  64.         this.mScalingRunnalable.startAnimation(200L);  
  65.     }  
  66.   
  67.     private void init(Context paramContext) {  
  68.         DisplayMetrics localDisplayMetrics = new DisplayMetrics();  
  69.         ((Activity) paramContext).getWindowManager().getDefaultDisplay()  
  70.                 .getMetrics(localDisplayMetrics);  
  71.         this.mScreenHeight = localDisplayMetrics.heightPixels;  
  72.         this.mHeaderContainer = new FrameLayout(paramContext);  
  73.         this.mHeaderImage = new ImageView(paramContext);  
  74.         int i = localDisplayMetrics.widthPixels;  
  75.         setHeaderViewSize(i, (int) (9.0F * (i / 16.0F)));  
  76.         this.mShadow = new ImageView(paramContext);  
  77.         FrameLayout.LayoutParams localLayoutParams = new FrameLayout.LayoutParams(  
  78.                 -1, -2);  
  79.         localLayoutParams.gravity = 80;  
  80.         this.mShadow.setLayoutParams(localLayoutParams);  
  81.         this.mHeaderContainer.addView(this.mHeaderImage);  
  82.         this.mHeaderContainer.addView(this.mShadow);  
  83.         addHeaderView(this.mHeaderContainer);  
  84.         this.mScalingRunnalable = new ScalingRunnalable();  
  85.         super.setOnScrollListener(this);  
  86.     }  
  87.   
  88.     private void onSecondaryPointerUp(MotionEvent paramMotionEvent) {  
  89.         int i = (paramMotionEvent.getAction()) >> 8;  
  90.         Log.d("onSecondaryPointerUp", i + "");  
  91.         if (paramMotionEvent.getPointerId(i) == this.mActivePointerId)  
  92.             if (i != 0) {  
  93.                 this.mLastMotionY = paramMotionEvent.getY(1);  
  94.                 this.mActivePointerId = paramMotionEvent.getPointerId(0);  
  95.                 return;  
  96.             }  
  97.     }  
  98.   
  99.     private void reset() {  
  100.         this.mActivePointerId = -1;  
  101.         this.mLastMotionY = -1.0F;  
  102.         this.mMaxScale = -1.0F;  
  103.         this.mLastScale = -1.0F;  
  104.     }  
  105.   
  106.     public ImageView getHeaderView() {  
  107.         return this.mHeaderImage;  
  108.     }  
  109.   
  110.     public void hideHeaderImage() {  
  111.         this.mShowHeaderImage = false;  
  112.         this.mZoomable = false;  
  113.         this.mScrollable = false;  
  114.         removeHeaderView(this.mHeaderContainer);  
  115.     }  
  116.   
  117.     public boolean isScrollable() {  
  118.         return this.mScrollable;  
  119.     }  
  120.   
  121.     public boolean isZoomable() {  
  122.         return this.mZoomable;  
  123.     }  
  124.   
  125.     public boolean onInterceptTouchEvent(MotionEvent paramMotionEvent) {  
  126.         if (!this.mZoomable) {  
  127.             return super.onInterceptTouchEvent(paramMotionEvent);  
  128.         }  
  129.         switch (paramMotionEvent.getAction() & MotionEvent.ACTION_MASK) {  
  130.         case MotionEvent.ACTION_DOWN:  
  131.   
  132.             this.mActivePointerId = paramMotionEvent.getPointerId(0);  
  133.             this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);  
  134.             break;  
  135.   
  136.         case MotionEvent.ACTION_UP:  
  137.             reset();  
  138.             break;  
  139.   
  140.         case MotionEvent.ACTION_POINTER_DOWN:  
  141.             this.mActivePointerId = paramMotionEvent  
  142.                     .getPointerId(paramMotionEvent.getActionIndex());  
  143.             break;  
  144.   
  145.         case MotionEvent.ACTION_POINTER_UP:  
  146.             onSecondaryPointerUp(paramMotionEvent);  
  147.             break;  
  148.         }  
  149.         return super.onInterceptTouchEvent(paramMotionEvent);  
  150.     }  
  151.   
  152.     protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2,  
  153.             int paramInt3, int paramInt4) {  
  154.         super.onLayout(paramBoolean, paramInt1, paramInt2, paramInt3, paramInt4);  
  155.         if (this.mHeaderHeight == 0)  
  156.             this.mHeaderHeight = this.mHeaderContainer.getHeight();  
  157.     }  
  158.   
  159.     @Override  
  160.     public void onScroll(AbsListView paramAbsListView, int paramInt1,  
  161.             int paramInt2, int paramInt3) {  
  162.         if (this.mScrollable) {  
  163.             Log.d(TAG, "onScroll");  
  164.             float f = this.mHeaderHeight - this.mHeaderContainer.getBottom();  
  165.             Log.d("onScroll", "f|" + f);  
  166.             if ((f > 0.0F) && (f this.mHeaderHeight)) {  
  167.                 Log.d("onScroll", "1");  
  168.                 int i = (int) (0.65D * f);  
  169.                 this.mHeaderImage.scrollTo(0, -i);  
  170.             } else if (this.mHeaderImage.getScrollY() != 0) {  
  171.                 Log.d("onScroll", "2");  
  172.                 this.mHeaderImage.scrollTo(0, 0);  
  173.             }  
  174.         }  
  175.   
  176.         if (this.mOnScrollListener != null) {  
  177.             this.mOnScrollListener.onScroll(paramAbsListView, paramInt1,  
  178.                     paramInt2, paramInt3);  
  179.         }  
  180.     }  
  181.   
  182.     public void onScrollStateChanged(AbsListView paramAbsListView, int paramInt) {  
  183.         if (this.mOnScrollListener != null)  
  184.             this.mOnScrollListener.onScrollStateChanged(paramAbsListView,  
  185.                     paramInt);  
  186.     }  
  187.   
  188.     public boolean onTouchEvent(MotionEvent ev) {  
  189.   
  190.         Log.d(TAG, "" + (0xFF & ev.getAction()));  
  191.         if (!this.mZoomable) {  
  192.             Log.i("zoom", "zoom");  
  193.             return super.onTouchEvent(ev);  
  194.         }  
  195.         switch (ev.getAction() & MotionEvent.ACTION_MASK) {  
  196.         case MotionEvent.ACTION_OUTSIDE:  
  197.         case MotionEvent.ACTION_DOWN:  
  198.             if (!this.mScalingRunnalable.mIsFinished) {  
  199.                 this.mScalingRunnalable.abortAnimation();  
  200.             }  
  201.             this.mLastMotionY = ev.getY();  
  202.             this.mActivePointerId = ev.getPointerId(0);  
  203.             this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);  
  204.             this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);  
  205.             break;  
  206.         case MotionEvent.ACTION_MOVE:  
  207.             Log.d("onTouchEvent", "mActivePointerId" + mActivePointerId);  
  208.             int j = ev.findPointerIndex(this.mActivePointerId);  
  209.             if (j == -1) {  
  210.                 Log.e("PullToZoomListView", "Invalid pointerId="  
  211.                         + this.mActivePointerId + " in onTouchEvent");  
  212.             } else {  
  213.                 if (this.mLastMotionY == -1.0F)  
  214.                     this.mLastMotionY = ev.getY(j);  
  215.                 if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) {  
  216.                     ViewGroup.LayoutParams localLayoutParams = this.mHeaderContainer  
  217.                             .getLayoutParams();  
  218.                     float f = ((ev.getY(j) - this.mLastMotionY + this.mHeaderContainer  
  219.                             .getBottom()) / this.mHeaderHeight - this.mLastScale)  
  220.                             / 2.0F + this.mLastScale;  
  221.                     if ((this.mLastScale <= 1.0D) && (f this.mLastScale)) {  
  222.                         localLayoutParams.height = this.mHeaderHeight;  
  223.                         this.mHeaderContainer  
  224.                                 .setLayoutParams(localLayoutParams);  
  225.                     }  
  226.                     this.mLastScale = Math.min(Math.max(f, 1.0F),  
  227.                             this.mMaxScale);  
  228.                     localLayoutParams.height = ((int) (this.mHeaderHeight * this.mLastScale));  
  229.                     if (localLayoutParams.height this.mScreenHeight)  
  230.                         this.mHeaderContainer  
  231.                                 .setLayoutParams(localLayoutParams);  
  232.                     this.mLastMotionY = ev.getY(j);  
  233.                 }  
  234.                 this.mLastMotionY = ev.getY(j);  
  235.             }  
  236.             break;  
  237.         case MotionEvent.ACTION_UP:  
  238.             reset();  
  239.             endScraling();  
  240.             break;  
  241.         case MotionEvent.ACTION_CANCEL:  
  242.   
  243.             break;  
  244.         case MotionEvent.ACTION_POINTER_DOWN:  
  245.             int i = ev.getActionIndex();  
  246.             this.mLastMotionY = ev.getY(i);  
  247.             this.mActivePointerId = ev.getPointerId(i);  
  248.             break;  
  249.         case MotionEvent.ACTION_POINTER_UP:  
  250.             onSecondaryPointerUp(ev);  
  251.             this.mLastMotionY = ev.getY(ev  
  252.                     .findPointerIndex(this.mActivePointerId));  
  253.             break;  
  254.         }  
  255.         return super.onTouchEvent(ev);  
  256.     }  
  257.   
  258.     public void setHeaderViewSize(int paramInt1, int paramInt2) {  
  259.         if (!this.mShowHeaderImage) {  
  260.             return;  
  261.         }  
  262.         Object localObject = this.mHeaderContainer.getLayoutParams();  
  263.         if (localObject == null)  
  264.             localObject = new AbsListView.LayoutParams(paramInt1, paramInt2);  
  265.         ((ViewGroup.LayoutParams) localObject).width = paramInt1;  
  266.         ((ViewGroup.LayoutParams) localObject).height = paramInt2;  
  267.         this.mHeaderContainer  
  268.                 .setLayoutParams((ViewGroup.LayoutParams) localObject);  
  269.         this.mHeaderHeight = paramInt2;  
  270.     }  
  271.   
  272.     public void setOnScrollListener(  
  273.             AbsListView.OnScrollListener paramOnScrollListener) {  
  274.         this.mOnScrollListener = paramOnScrollListener;  
  275.     }  
  276.   
  277.     public void setScrollable(boolean paramBoolean) {  
  278.         if (!this.mShowHeaderImage) {  
  279.             return;  
  280.         }  
  281.         this.mScrollable = paramBoolean;  
  282.     }  
  283.   
  284.     public void setShadow(int paramInt) {  
  285.         if (!this.mShowHeaderImage) {  
  286.             return;  
  287.         }  
  288.         this.mShadow.setBackgroundResource(paramInt);  
  289.     }  
  290.   
  291.     public void setZoomable(boolean paramBoolean) {  
  292.         if (!this.mShowHeaderImage) {  
  293.             return;  
  294.         }  
  295.         this.mZoomable = paramBoolean;  
  296.     }  
  297.   
  298.     class ScalingRunnalable implements Runnable {  
  299.         long mDuration;  
  300.         boolean mIsFinished = true;  
  301.         float mScale;  
  302.         long mStartTime;  
  303.   
  304.         ScalingRunnalable() {  
  305.         }  
  306.   
  307.         public void abortAnimation() {  
  308.             this.mIsFinished = true;  
  309.         }  
  310.   
  311.         public boolean isFinished() {  
  312.             return this.mIsFinished;  
  313.         }  
  314.   
  315.         public void run() {  
  316.             float f2;  
  317.             ViewGroup.LayoutParams localLayoutParams;  
  318.             if ((!this.mIsFinished) && (this.mScale > 1.0D)) {  
  319.                 float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) this.mStartTime)  
  320.                         / (float) this.mDuration;  
  321.                 f2 = this.mScale - (this.mScale - 1.0F)  
  322.                         * PullToZoomListView.sInterpolator.getInterpolation(f1);  
  323.                 localLayoutParams = PullToZoomListView.this.mHeaderContainer  
  324.                         .getLayoutParams();  
  325.                 if (f2 > 1.0F) {  
  326.                     Log.d("mmm", "f2>1.0");  
  327.                     localLayoutParams.height = PullToZoomListView.this.mHeaderHeight;  
  328.                     localLayoutParams.height = ((int) (f2 * PullToZoomListView.this.mHeaderHeight));  
  329.                     PullToZoomListView.this.mHeaderContainer  
  330.                             .setLayoutParams(localLayoutParams);  
  331.                     PullToZoomListView.this.post(this);  
  332.                     return;  
  333.                 }  
  334.                 this.mIsFinished = true;  
  335.             }  
  336.         }  
  337.   
  338.         public void startAnimation(long paramLong) {  
  339.             this.mStartTime = SystemClock.currentThreadTimeMillis();  
  340.             this.mDuration = paramLong;  
  341.             this.mScale = ((float) (PullToZoomListView.this.mHeaderContainer  
  342.                     .getBottom()) / PullToZoomListView.this.mHeaderHeight);  
  343.             this.mIsFinished = false;  
  344.             PullToZoomListView.this.post(this);  
  345.         }  
  346.     }  
  347. }  
  关于这个控件的实现原理可以自行参考上面链接的文章,里面已经有较为细致的讲解,此处不再赘述。实现的效果就是下面这样:
  github链接:https://github.com/wangjinyu501/PullToZoomListView/,如果您发现了任何问题,可以在文章下面留言,我会第一时间处理,欢迎交流。