zoukankan      html  css  js  c++  java
  • Android PullToZoomListView实现放大回弹效果

    版本号:1.0 
    日期:2014.8.4
    版权:© 2014 kince 转载注明出处

      之前看过一篇文章,链接是:能够下拉缩放HeaderView的ListView:PullToZoomInListView

    说的就是PullToZoomListView。只是这篇文章有个地方须要勘误,就是PullToZoomListView这个控件尽管是github上一个开源项目。只是最美应用并非使用这个开源项目,而是这个开源项目反编译了最美应用的代码。

      可是,它这个代码是有问题的,当手指在屏幕上滑动的时候会引发onItemClick事件。

    看了一下它的代码。发现是由于在onTouchEvent()方法中的MotionEvent.ACTION_MOVE中return true了,这样就会消费这个事件。所以导致了onItemClick事件。

    之后我也反编译了一下最美应用,看了一下这个控件的实现,发现还有其它问题,比方少了一些变量,没有重写onInterceptTouchEvent()方法等。因此我又自己动手把这个控件丰富了一下。使其能够最大限度的接近原始代码。并且可正常使用。改动后代码例如以下:

    package com.kince.android.pulltozoomlistview;
    
    import android.app.Activity;
    import android.content.Context;
    import android.os.SystemClock;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.ViewGroup;
    import android.view.animation.Interpolator;
    import android.widget.AbsListView;
    import android.widget.FrameLayout;
    import android.widget.ImageView;
    import android.widget.ListView;
    
    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;
    			return 1.0F + f * (f * (f * (f * f)));
    		}
    	};
    	int mActivePointerId = -1;
    	private FrameLayout mHeaderContainer;
    	private int mHeaderHeight;
    	private ImageView mHeaderImage;
    	float mLastMotionY = -1.0F;
    	float mLastScale = -1.0F;
    	float mMaxScale = -1.0F;
    	private AbsListView.OnScrollListener mOnScrollListener;
    	private ScalingRunnalable mScalingRunnalable;
    	private int mScreenHeight;
    	private ImageView mShadow;
    
    	private boolean mScrollable = true;
    	private boolean mShowHeaderImage = true;
    	private boolean mZoomable = true;
    
    	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() {
    		if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight)
    			Log.d("mmm", "endScraling");
    		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);
    		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) {
    		int i = (paramMotionEvent.getAction()) >> 8;
    		Log.d("onSecondaryPointerUp", i + "");
    		if (paramMotionEvent.getPointerId(i) == this.mActivePointerId)
    			if (i != 0) {
    				this.mLastMotionY = paramMotionEvent.getY(1);
    				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 void hideHeaderImage() {
    		this.mShowHeaderImage = false;
    		this.mZoomable = false;
    		this.mScrollable = false;
    		removeHeaderView(this.mHeaderContainer);
    	}
    
    	public boolean isScrollable() {
    		return this.mScrollable;
    	}
    
    	public boolean isZoomable() {
    		return this.mZoomable;
    	}
    
    	public boolean onInterceptTouchEvent(MotionEvent paramMotionEvent) {
    		if (!this.mZoomable) {
    			return super.onInterceptTouchEvent(paramMotionEvent);
    		}
    		switch (paramMotionEvent.getAction() & MotionEvent.ACTION_MASK) {
    		case MotionEvent.ACTION_DOWN:
    
    			this.mActivePointerId = paramMotionEvent.getPointerId(0);
    			this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);
    			break;
    
    		case MotionEvent.ACTION_UP:
    			reset();
    			break;
    
    		case MotionEvent.ACTION_POINTER_DOWN:
    			this.mActivePointerId = paramMotionEvent
    					.getPointerId(paramMotionEvent.getActionIndex());
    			break;
    
    		case MotionEvent.ACTION_POINTER_UP:
    			onSecondaryPointerUp(paramMotionEvent);
    			break;
    		}
    		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 (this.mHeaderHeight == 0)
    			this.mHeaderHeight = this.mHeaderContainer.getHeight();
    	}
    
    	@Override
    	public void onScroll(AbsListView paramAbsListView, int paramInt1,
    			int paramInt2, int paramInt3) {
    		if (this.mScrollable) {
    			Log.d(TAG, "onScroll");
    			float f = this.mHeaderHeight - this.mHeaderContainer.getBottom();
    			Log.d("onScroll", "f|" + f);
    			if ((f > 0.0F) && (f < this.mHeaderHeight)) {
    				Log.d("onScroll", "1");
    				int i = (int) (0.65D * f);
    				this.mHeaderImage.scrollTo(0, -i);
    			} else if (this.mHeaderImage.getScrollY() != 0) {
    				Log.d("onScroll", "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.d(TAG, "" + (0xFF & ev.getAction()));
    		if (!this.mZoomable) {
    			Log.i("zoom", "zoom");
    			return super.onTouchEvent(ev);
    		}
    		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();
    			this.mActivePointerId = ev.getPointerId(0);
    			this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);
    			this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);
    			break;
    		case MotionEvent.ACTION_MOVE:
    			Log.d("onTouchEvent", "mActivePointerId" + mActivePointerId);
    			int j = ev.findPointerIndex(this.mActivePointerId);
    			if (j == -1) {
    				Log.e("PullToZoomListView", "Invalid pointerId="
    						+ this.mActivePointerId + " in onTouchEvent");
    			} else {
    				if (this.mLastMotionY == -1.0F)
    					this.mLastMotionY = ev.getY(j);
    				if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) {
    					ViewGroup.LayoutParams localLayoutParams = this.mHeaderContainer
    							.getLayoutParams();
    					float f = ((ev.getY(j) - this.mLastMotionY + this.mHeaderContainer
    							.getBottom()) / this.mHeaderHeight - this.mLastScale)
    							/ 2.0F + this.mLastScale;
    					if ((this.mLastScale <= 1.0D) && (f < this.mLastScale)) {
    						localLayoutParams.height = this.mHeaderHeight;
    						this.mHeaderContainer
    								.setLayoutParams(localLayoutParams);
    					}
    					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);
    				}
    				this.mLastMotionY = ev.getY(j);
    			}
    			break;
    		case MotionEvent.ACTION_UP:
    			reset();
    			endScraling();
    			break;
    		case MotionEvent.ACTION_CANCEL:
    
    			break;
    		case MotionEvent.ACTION_POINTER_DOWN:
    			int i = ev.getActionIndex();
    			this.mLastMotionY = ev.getY(i);
    			this.mActivePointerId = ev.getPointerId(i);
    			break;
    		case MotionEvent.ACTION_POINTER_UP:
    			onSecondaryPointerUp(ev);
    			this.mLastMotionY = ev.getY(ev
    					.findPointerIndex(this.mActivePointerId));
    			break;
    		}
    		return super.onTouchEvent(ev);
    	}
    
    	public void setHeaderViewSize(int paramInt1, int paramInt2) {
    		if (!this.mShowHeaderImage) {
    			return;
    		}
    		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;
    	}
    
    	public void setScrollable(boolean paramBoolean) {
    		if (!this.mShowHeaderImage) {
    			return;
    		}
    		this.mScrollable = paramBoolean;
    	}
    
    	public void setShadow(int paramInt) {
    		if (!this.mShowHeaderImage) {
    			return;
    		}
    		this.mShadow.setBackgroundResource(paramInt);
    	}
    
    	public void setZoomable(boolean paramBoolean) {
    		if (!this.mShowHeaderImage) {
    			return;
    		}
    		this.mZoomable = paramBoolean;
    	}
    
    	class ScalingRunnalable implements Runnable {
    		long mDuration;
    		boolean mIsFinished = true;
    		float mScale;
    		long mStartTime;
    
    		ScalingRunnalable() {
    		}
    
    		public void abortAnimation() {
    			this.mIsFinished = true;
    		}
    
    		public boolean isFinished() {
    			return this.mIsFinished;
    		}
    
    		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;
    				f2 = this.mScale - (this.mScale - 1.0F)
    						* PullToZoomListView.sInterpolator.getInterpolation(f1);
    				localLayoutParams = PullToZoomListView.this.mHeaderContainer
    						.getLayoutParams();
    				if (f2 > 1.0F) {
    					Log.d("mmm", "f2>1.0");
    					localLayoutParams.height = PullToZoomListView.this.mHeaderHeight;
    					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);
    			this.mIsFinished = false;
    			PullToZoomListView.this.post(this);
    		}
    	}
    }
    
      关于这个控件的实现原理能够自行參考上面链接的文章,里面已经有较为仔细的解说,此处不再赘述。

    实现的效果就是以下这样:

      github链接:https://github.com/wangjinyu501/PullToZoomListView/,假设您发现了不论什么问题。能够在文章以下留言,我会第一时间处理。欢迎交流。


  • 相关阅读:
    176. Second Highest Salary
    175. Combine Two Tables
    172. Factorial Trailing Zeroes
    171. Excel Sheet Column Number
    169. Majority Element
    168. Excel Sheet Column Title
    167. Two Sum II
    160. Intersection of Two Linked Lists
    个人博客记录
    <meta>标签
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/6700780.html
Copyright © 2011-2022 走看看