另外一个相同项目的地址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()方法等,因此我又自己动手把这个控件丰富了一下,使其能够最大限度的接近原始代码,而且可正常使用。修改后代码如下:
  1. package com.kince.android.pulltozoomlistview;
  2. import android.app.Activity;
  3. import android.content.Context;
  4. import android.os.SystemClock;
  5. import android.util.AttributeSet;
  6. import android.util.DisplayMetrics;
  7. import android.util.Log;
  8. import android.view.MotionEvent;
  9. import android.view.ViewGroup;
  10. import android.view.animation.Interpolator;
  11. import android.widget.AbsListView;
  12. import android.widget.FrameLayout;
  13. import android.widget.ImageView;
  14. import android.widget.ListView;
  15. public class PullToZoomListView extends ListView implements
  16. AbsListView.OnScrollListener {
  17. private static final int INVALID_VALUE = -1;
  18. private static final String TAG = "PullToZoomListView";
  19. private static final Interpolator sInterpolator = new Interpolator() {
  20. public float getInterpolation(float paramAnonymousFloat) {
  21. float f = paramAnonymousFloat - 1.0F;
  22. return 1.0F + f * (f * (f * (f * f)));
  23. }
  24. };
  25. int mActivePointerId = -1;
  26. private FrameLayout mHeaderContainer;
  27. private int mHeaderHeight;
  28. private ImageView mHeaderImage;
  29. float mLastMotionY = -1.0F;
  30. float mLastScale = -1.0F;
  31. float mMaxScale = -1.0F;
  32. private AbsListView.OnScrollListener mOnScrollListener;
  33. private ScalingRunnalable mScalingRunnalable;
  34. private int mScreenHeight;
  35. private ImageView mShadow;
  36. private boolean mScrollable = true;
  37. private boolean mShowHeaderImage = true;
  38. private boolean mZoomable = true;
  39. public PullToZoomListView(Context paramContext) {
  40. super(paramContext);
  41. init(paramContext);
  42. }
  43. public PullToZoomListView(Context paramContext,
  44. AttributeSet paramAttributeSet) {
  45. super(paramContext, paramAttributeSet);
  46. init(paramContext);
  47. }
  48. public PullToZoomListView(Context paramContext,
  49. AttributeSet paramAttributeSet, int paramInt) {
  50. super(paramContext, paramAttributeSet, paramInt);
  51. init(paramContext);
  52. }
  53. private void endScraling() {
  54. if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight)
  55. Log.d("mmm", "endScraling");
  56. this.mScalingRunnalable.startAnimation(200L);
  57. }
  58. private void init(Context paramContext) {
  59. DisplayMetrics localDisplayMetrics = new DisplayMetrics();
  60. ((Activity) paramContext).getWindowManager().getDefaultDisplay()
  61. .getMetrics(localDisplayMetrics);
  62. this.mScreenHeight = localDisplayMetrics.heightPixels;
  63. this.mHeaderContainer = new FrameLayout(paramContext);
  64. this.mHeaderImage = new ImageView(paramContext);
  65. int i = localDisplayMetrics.widthPixels;
  66. setHeaderViewSize(i, (int) (9.0F * (i / 16.0F)));
  67. this.mShadow = new ImageView(paramContext);
  68. FrameLayout.LayoutParams localLayoutParams = new FrameLayout.LayoutParams(
  69. -1, -2);
  70. localLayoutParams.gravity = 80;
  71. this.mShadow.setLayoutParams(localLayoutParams);
  72. this.mHeaderContainer.addView(this.mHeaderImage);
  73. this.mHeaderContainer.addView(this.mShadow);
  74. addHeaderView(this.mHeaderContainer);
  75. this.mScalingRunnalable = new ScalingRunnalable();
  76. super.setOnScrollListener(this);
  77. }
  78. private void onSecondaryPointerUp(MotionEvent paramMotionEvent) {
  79. int i = (paramMotionEvent.getAction()) >> 8;
  80. Log.d("onSecondaryPointerUp", i + "");
  81. if (paramMotionEvent.getPointerId(i) == this.mActivePointerId)
  82. if (i != 0) {
  83. this.mLastMotionY = paramMotionEvent.getY(1);
  84. this.mActivePointerId = paramMotionEvent.getPointerId(0);
  85. return;
  86. }
  87. }
  88. private void reset() {
  89. this.mActivePointerId = -1;
  90. this.mLastMotionY = -1.0F;
  91. this.mMaxScale = -1.0F;
  92. this.mLastScale = -1.0F;
  93. }
  94. public ImageView getHeaderView() {
  95. return this.mHeaderImage;
  96. }
  97. public void hideHeaderImage() {
  98. this.mShowHeaderImage = false;
  99. this.mZoomable = false;
  100. this.mScrollable = false;
  101. removeHeaderView(this.mHeaderContainer);
  102. }
  103. public boolean isScrollable() {
  104. return this.mScrollable;
  105. }
  106. public boolean isZoomable() {
  107. return this.mZoomable;
  108. }
  109. public boolean onInterceptTouchEvent(MotionEvent paramMotionEvent) {
  110. if (!this.mZoomable) {
  111. return super.onInterceptTouchEvent(paramMotionEvent);
  112. }
  113. switch (paramMotionEvent.getAction() & MotionEvent.ACTION_MASK) {
  114. case MotionEvent.ACTION_DOWN:
  115. this.mActivePointerId = paramMotionEvent.getPointerId(0);
  116. this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);
  117. break;
  118. case MotionEvent.ACTION_UP:
  119. reset();
  120. break;
  121. case MotionEvent.ACTION_POINTER_DOWN:
  122. this.mActivePointerId = paramMotionEvent
  123. .getPointerId(paramMotionEvent.getActionIndex());
  124. break;
  125. case MotionEvent.ACTION_POINTER_UP:
  126. onSecondaryPointerUp(paramMotionEvent);
  127. break;
  128. }
  129. return super.onInterceptTouchEvent(paramMotionEvent);
  130. }
  131. protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2,
  132. int paramInt3, int paramInt4) {
  133. super.onLayout(paramBoolean, paramInt1, paramInt2, paramInt3, paramInt4);
  134. if (this.mHeaderHeight == 0)
  135. this.mHeaderHeight = this.mHeaderContainer.getHeight();
  136. }
  137. @Override
  138. public void onScroll(AbsListView paramAbsListView, int paramInt1,
  139. int paramInt2, int paramInt3) {
  140. if (this.mScrollable) {
  141. Log.d(TAG, "onScroll");
  142. float f = this.mHeaderHeight - this.mHeaderContainer.getBottom();
  143. Log.d("onScroll", "f|" + f);
  144. if ((f > 0.0F) && (f < this.mHeaderHeight)) {
  145. Log.d("onScroll", "1");
  146. int i = (int) (0.65D * f);
  147. this.mHeaderImage.scrollTo(0, -i);
  148. } else if (this.mHeaderImage.getScrollY() != 0) {
  149. Log.d("onScroll", "2");
  150. this.mHeaderImage.scrollTo(0, 0);
  151. }
  152. }
  153. if (this.mOnScrollListener != null) {
  154. this.mOnScrollListener.onScroll(paramAbsListView, paramInt1,
  155. paramInt2, paramInt3);
  156. }
  157. }
  158. public void onScrollStateChanged(AbsListView paramAbsListView, int paramInt) {
  159. if (this.mOnScrollListener != null)
  160. this.mOnScrollListener.onScrollStateChanged(paramAbsListView,
  161. paramInt);
  162. }
  163. public boolean onTouchEvent(MotionEvent ev) {
  164. Log.d(TAG, "" + (0xFF & ev.getAction()));
  165. if (!this.mZoomable) {
  166. Log.i("zoom", "zoom");
  167. return super.onTouchEvent(ev);
  168. }
  169. switch (ev.getAction() & MotionEvent.ACTION_MASK) {
  170. case MotionEvent.ACTION_OUTSIDE:
  171. case MotionEvent.ACTION_DOWN:
  172. if (!this.mScalingRunnalable.mIsFinished) {
  173. this.mScalingRunnalable.abortAnimation();
  174. }
  175. this.mLastMotionY = ev.getY();
  176. this.mActivePointerId = ev.getPointerId(0);
  177. this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);
  178. this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);
  179. break;
  180. case MotionEvent.ACTION_MOVE:
  181. Log.d("onTouchEvent", "mActivePointerId" + mActivePointerId);
  182. int j = ev.findPointerIndex(this.mActivePointerId);
  183. if (j == -1) {
  184. Log.e("PullToZoomListView", "Invalid pointerId="
  185. + this.mActivePointerId + " in onTouchEvent");
  186. } else {
  187. if (this.mLastMotionY == -1.0F)
  188. this.mLastMotionY = ev.getY(j);
  189. if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) {
  190. ViewGroup.LayoutParams localLayoutParams = this.mHeaderContainer
  191. .getLayoutParams();
  192. float f = ((ev.getY(j) - this.mLastMotionY + this.mHeaderContainer
  193. .getBottom()) / this.mHeaderHeight - this.mLastScale)
  194. / 2.0F + this.mLastScale;
  195. if ((this.mLastScale <= 1.0D) && (f < this.mLastScale)) {
  196. localLayoutParams.height = this.mHeaderHeight;
  197. this.mHeaderContainer
  198. .setLayoutParams(localLayoutParams);
  199. }
  200. this.mLastScale = Math.min(Math.max(f, 1.0F),
  201. this.mMaxScale);
  202. localLayoutParams.height = ((int) (this.mHeaderHeight * this.mLastScale));
  203. if (localLayoutParams.height < this.mScreenHeight)
  204. this.mHeaderContainer
  205. .setLayoutParams(localLayoutParams);
  206. this.mLastMotionY = ev.getY(j);
  207. }
  208. this.mLastMotionY = ev.getY(j);
  209. }
  210. break;
  211. case MotionEvent.ACTION_UP:
  212. reset();
  213. endScraling();
  214. break;
  215. case MotionEvent.ACTION_CANCEL:
  216. break;
  217. case MotionEvent.ACTION_POINTER_DOWN:
  218. int i = ev.getActionIndex();
  219. this.mLastMotionY = ev.getY(i);
  220. this.mActivePointerId = ev.getPointerId(i);
  221. break;
  222. case MotionEvent.ACTION_POINTER_UP:
  223. onSecondaryPointerUp(ev);
  224. this.mLastMotionY = ev.getY(ev
  225. .findPointerIndex(this.mActivePointerId));
  226. break;
  227. }
  228. return super.onTouchEvent(ev);
  229. }
  230. public void setHeaderViewSize(int paramInt1, int paramInt2) {
  231. if (!this.mShowHeaderImage) {
  232. return;
  233. }
  234. Object localObject = this.mHeaderContainer.getLayoutParams();
  235. if (localObject == null)
  236. localObject = new AbsListView.LayoutParams(paramInt1, paramInt2);
  237. ((ViewGroup.LayoutParams) localObject).width = paramInt1;
  238. ((ViewGroup.LayoutParams) localObject).height = paramInt2;
  239. this.mHeaderContainer
  240. .setLayoutParams((ViewGroup.LayoutParams) localObject);
  241. this.mHeaderHeight = paramInt2;
  242. }
  243. public void setOnScrollListener(
  244. AbsListView.OnScrollListener paramOnScrollListener) {
  245. this.mOnScrollListener = paramOnScrollListener;
  246. }
  247. public void setScrollable(boolean paramBoolean) {
  248. if (!this.mShowHeaderImage) {
  249. return;
  250. }
  251. this.mScrollable = paramBoolean;
  252. }
  253. public void setShadow(int paramInt) {
  254. if (!this.mShowHeaderImage) {
  255. return;
  256. }
  257. this.mShadow.setBackgroundResource(paramInt);
  258. }
  259. public void setZoomable(boolean paramBoolean) {
  260. if (!this.mShowHeaderImage) {
  261. return;
  262. }
  263. this.mZoomable = paramBoolean;
  264. }
  265. class ScalingRunnalable implements Runnable {
  266. long mDuration;
  267. boolean mIsFinished = true;
  268. float mScale;
  269. long mStartTime;
  270. ScalingRunnalable() {
  271. }
  272. public void abortAnimation() {
  273. this.mIsFinished = true;
  274. }
  275. public boolean isFinished() {
  276. return this.mIsFinished;
  277. }
  278. public void run() {
  279. float f2;
  280. ViewGroup.LayoutParams localLayoutParams;
  281. if ((!this.mIsFinished) && (this.mScale > 1.0D)) {
  282. float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) this.mStartTime)
  283. / (float) this.mDuration;
  284. f2 = this.mScale - (this.mScale - 1.0F)
  285. * PullToZoomListView.sInterpolator.getInterpolation(f1);
  286. localLayoutParams = PullToZoomListView.this.mHeaderContainer
  287. .getLayoutParams();
  288. if (f2 > 1.0F) {
  289. Log.d("mmm", "f2>1.0");
  290. localLayoutParams.height = PullToZoomListView.this.mHeaderHeight;
  291. localLayoutParams.height = ((int) (f2 * PullToZoomListView.this.mHeaderHeight));
  292. PullToZoomListView.this.mHeaderContainer
  293. .setLayoutParams(localLayoutParams);
  294. PullToZoomListView.this.post(this);
  295. return;
  296. }
  297. this.mIsFinished = true;
  298. }
  299. }
  300. public void startAnimation(long paramLong) {
  301. this.mStartTime = SystemClock.currentThreadTimeMillis();
  302. this.mDuration = paramLong;
  303. this.mScale = ((float) (PullToZoomListView.this.mHeaderContainer
  304. .getBottom()) / PullToZoomListView.this.mHeaderHeight);
  305. this.mIsFinished = false;
  306. PullToZoomListView.this.post(this);
  307. }
  308. }
  309. }

关于这个控件的实现原理可以自行参考上面链接的文章,里面已经有较为细致的讲解,此处不再赘述。实现的效果就是下面这样:

  github链接:https://github.com/wangjinyu501/PullToZoomListView/,如果您发现了任何问题,可以在文章下面留言,我会第一时间处理,欢迎交流。

Android PullToZoomListView实现放大回弹效果的更多相关文章

  1. Android -- 自定义ScrollView实现放大回弹效果

    1,刚刚在别人开源的项目中看到了一个挺不错的用户体验,效果图如下: 2,那下面我们就来实现一下,首先看一下布局,由于一般只是我们包含头像的那部分方法,所以这里我们要把布局分成两部分,对应的布局文件效果 ...

  2. 顶部图片放大回弹效果Scrollview ---- 各应用中常见的自定义View 解析

    原理并不难.  代码量也不大.  非常简洁 .  先来个效果图 再上一波代码. public class SpecialScrollView extends ScrollView implements ...

  3. Android仿IOS回弹效果 ScrollView回弹 总结

    Android仿IOS回弹效果  ScrollView回弹 总结 应项目中的需求  须要仿IOS 下拉回弹的效果 , 我在网上搜了非常多 大多数都是拿scrollview 改吧改吧 试了一些  发现总 ...

  4. Android 图片的放大缩小拖拉

    package com.example.ImageView; import android.annotation.SuppressLint; import android.content.Contex ...

  5. ScrollView的阻尼回弹效果实现(仿qq空间)

    玩过新浪微博,qq空间等手机客户端的童鞋,都应该清楚,在主界面向下滑动时,会有一个阻尼回弹效果,看起来挺不错,接下来我们就来实现一下这种效果,下拉后回弹刷新界面,先看效果图: 这个是编辑器里面的界面效 ...

  6. ScrollView的顶部下拉和底部上拉回弹效果

    要实现ScrollView的回弹效果,需要对其进行触摸事件处理.先来看一下简单的效果: 根据Android的View事件分发处理机制,下面对dispatchTouchEvent进行详细分析: 在加载布 ...

  7. Android RecyclerView 实现支付宝首页效果

    Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...

  8. 移动端页面 弹出框滚动,底部body锁定,不滚动 / 微信网页禁止回弹效果

    需求:页面有弹出层菜单,当弹出层菜单超出屏幕可视区域时,不能滚动.加上滚动后,底部body的滚动事件如何禁止,加上了overflow:hidden;还是不可用. 如下图:地区弹出框可以滚动,而底部的b ...

  9. 在android中用跑马灯的效果显示textview

    大家好,在我们通常的android project中,通常需要用到textview这一个布局文件,并且对于这一个显示布局所需要的文本文字内容. 下面我们就来介绍一种方法来实现在android中用跑马灯 ...

随机推荐

  1. 基于jquery封装的颜色下拉选择框

    应同事要求,花了半个小时,写了一个简单的选择颜色的下拉框控件,可以控制输入框指示结果颜色 也贴出来,说不定哪天有用 if (typeof jQuery === 'undefined') { throw ...

  2. js 让浏览器全屏模式的方法launchFullscreen

    浏览器全屏模式的启动函数requestFullscreen仍然需要附带各浏览器的js方言前缀 // 判断各种浏览器,找到正确的方法 function launchFullscreen(element) ...

  3. js 遇到问题

    1)obj.style.attr 和obj.style[attr]区别: 2)window.onload一个页面只能出现一次: 3)border-radious实现 实心和空心圆 要点:宽度高度一样大 ...

  4. SAP 创建物料主数据分类视图特性

    1.CL01创建物料分类 2.去CT04中去创建特性值 创建完成之后保存, 3.创建物料的分类视图,选择相应的特性值

  5. Noip2016提高组 玩具谜题toy

    Day 1 T1 题目大意 一些naive的玩具小人把小南的眼镜藏起来,但小南有一份too simple的小纸条,告诉小南眼镜在第一个小人往哪数第几个的往哪数的第几个的往哪数第几个的往哪数的第几个的往 ...

  6. jQuery Mobile 导航栏

    jQuery Mobile 导航栏 导航栏由一组水平排列的链接构成,通常位于页眉或页脚内部. 默认地,导航栏中的链接会自动转换为按钮(无需 data-role="button"). ...

  7. java多线程详解(3)-线程的互斥与同步

    前言:前一篇文章主要描述了多线程中访成员变量与局部变量问题,我们知道访成员变量有线程安全问题,在多线程程序中 我们可以通过使用synchronized关键字完成线程的同步,能够解决部分线程安全问题 在 ...

  8. Linux下NFS服务器的搭建与配置

    一.NFS服务简介 NFS 就是 Network FileSystem 的缩写,最早之前是由sun 这家公司所发展出来的. 它最大的功能就是可以透过网络,让不同的机器.不同的操作系统.可以彼此分享个别 ...

  9. 小米Git

    这个题目的意思其实就是要分别从根节点开始遍历(dfs)到给定的两个点,然后从得出的路径中获取最早相同的点即为结果.   class Solution { public: /** * 返回git树上两点 ...

  10. javascript学习第五课this、call、apply

    js函数与其它 高级语言相比有一个特点.没有返回值,一个简单函数就是function关键字+函数名字构成 this 对象是在运行中基于函数的执行环境绑定的,在全局函数中,this等于window,而当 ...