listView 的headerView下拉刷新比较流行,比如商品列表中对品牌的介绍可以用到这个功能,体验感受了下PullToZoomInListView,
感觉比较好的效果,就拿来分析下。
PullToZoomInListView 是自定义了一个ListView,headerVIew的图片支持缩放,效果比较有美感,动画也比较流畅,是一个比较好的
项目。
其实PullToZoomListView的实现原理很简单
主要是在case MotionEvent.ACTION_MOVE:代码段中判断向下滑动的偏移量,根据这个来改变listview headerView内容
区域的高度,并且在手指放开的那一刻,停止缩放,启用一个动画来使HeaderView平滑的恢复到放大之前的状态。
1、首先初始化画布局init(Context paramContext),通过setHeaderViewSize(i, (int) (9.0F * (i / 16.0F)))设置header图片的大小,启动ScalingRunnalable线程;
2、初始化完成后,注册一个OnScrollListener监听,当滚动之后可以恢复到初始化的状态
public void onScroll(AbsListView paramAbsListView, int paramInt1, int paramInt2, int paramInt3) { Log.d("mmm", "onScroll"); float f = this.mHeaderHeight - this.mHeaderContainer.getBottom(); Log.d("mmm", "f|" + f); if ((f > 0.0F) && (f < this.mHeaderHeight)) { Log.d("mmm", "1"); int i = (int) (0.65D * f); this.mHeaderImage.scrollTo(0, -i); } else if (this.mHeaderImage.getScrollY() != 0) { Log.d("mmm", "2"); this.mHeaderImage.scrollTo(0, 0); } if (this.mOnScrollListener != null) { this.mOnScrollListener.onScroll(paramAbsListView, paramInt1, paramInt2, paramInt3); } }
3、拉动时候缩放,主要是在 MotionEvent.ACTION_MOVE:代码段中判断向下滑动的偏移量,
this.mHeaderContainer.getBottom() >= this.mHeaderHeight,图片下移,放大图片
case MotionEvent.ACTION_MOVE: Log.d("mmm", "mActivePointerId" + mActivePointerId); int j = paramMotionEvent.findPointerIndex(this.mActivePointerId); if (j == -1) { Log.e("PullToZoomListView", "Invalid pointerId=" + this.mActivePointerId + " in onTouchEvent"); } else { if (this.mLastMotionY == -1.0F) this.mLastMotionY = paramMotionEvent.getY(j); if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) { ViewGroup.LayoutParams localLayoutParams = this.mHeaderContainer .getLayoutParams(); float f = ((paramMotionEvent.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); return super.onTouchEvent(paramMotionEvent); } 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 = paramMotionEvent.getY(j); return true; } this.mLastMotionY = paramMotionEvent.getY(j); } break;
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; } }
难点不在于如何改变mHeaderContainer的高度吧,难在如何让一个View按一定的时间曲线改变属性,这其实是属性动画该做的事,但是这里没有用属性动画,显然作者对View的动画很熟悉,我觉得这里作者用自己的方法实现了属性动画, ScalingRunnalable在startAnimation中调用了PullToZoomListView.this.post(this);不懂得可以搜索View.post(Runnable),post调用ScalingRunnalable的run方法,而ScalingRunnalable run方法中再次调用了post,就这样不断的更新UI,直到达到一定的条件退出这个循环(这里这个条件是if((!this.mIsFinished) && (this.mScale > 1.0D))),这里的关键点是每次执行run的时候mHeaderContainer的高度究竟该变化多少,
1
2
|
f2 = this .mScale - ( this .mScale
- 1 .0F) * PullToZoomListView.sInterpolator.getInterpolation(f1); |
中PullToZoomListView.sInterpolator.getInterpolation(f1)给了我们答案
public float getInterpolation(float paramAnonymousFloat) { float f = paramAnonymousFloat - 1.0F; return 1.0F + f * (f * (f * (f * f))); }
ListView的headerView下拉刷新PullToZoomInListView分析
原文:http://blog.csdn.net/honjane/article/details/44653597