高仿IOS下拉刷新的粘虫效果
最近看需要做一款下拉刷新的效果,由于需要和Ios界面保持一致,所以这用安卓的方式实现了ios下的下拉刷新的粘虫效果。
最新的安卓手机版本的QQ也有这种类似的效果,就是拖动未读信息的那个红色圆圈,拖动近距离的是就有这种粘虫的效果。
下面是安卓版本的嘟嘟App的效果截图,后面会简单的介绍下的实现原理
原理:
如下图所示,在没有进行下拉的是,显示的是A图,实际上是一个圆形,当进行向下的拖动的时候,圆形会进行拉伸,这里简单用模拟下圆形被用力拉伸的效果。
1、被拉伸的圆形,实际上分为3部分,上面的部分(是个半圆,稍微大点,简称为大圆),中间部分(是一个拉伸的部分,有2条平滑的曲线),下面部分(也是一个半圆,较小,成为小圆)
2、当滑动的距离越来越大的时候,模拟的力就越大,那么圆就拉伸越厉害。这样我们可以把上面的大圆和下面的小圆变的越来越小。中间部分,变成的越来越长。
3、拖动过程理解,那么简述下绘制的流程,从1点开始绘制,1~2是一个四分之一的圆形,2~3是一个曲线,我们可以用贝塞尔曲线来绘制,具体贝塞尔是什么东西,可以自行百度,这里不做解释。3~4是一个半圆,4~5和2~3一样,也是一个贝塞尔曲线。5~1和1~2一样也是四分之一的圆形。
上面就是简单原理,可能实际效果还是需要优化的,不过原理再次,后面只需要慢慢优化即可,当初实现这个功能也是费了2天时间。
下面是代码片段,具体完整代码,后面会给出下载链接。
public class RefreshView extends View { static final int BEZIER_OFFSET = McDimenUtil.dp2Px(15);// 贝塞尔曲线的偏移值 static final int R = McDimenUtil.dp2Px(30); // 圆球的半径 static final int Y_OFFSET = McDimenUtil.dp2Px(60); // 竖直方向最大的偏移值 int currentX; int currentY; private boolean isReFreshed; private int offsetY; private OnPullRefreshCallback onPullRefreshCallback; private Paint paint; private Path path; int startX; int startY; public RefreshView(Context paramContext) { super(paramContext); init(); } public RefreshView(Context paramContext, AttributeSet paramAttributeSet) { super(paramContext, paramAttributeSet); init(); } public RefreshView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) { super(paramContext, paramAttributeSet, paramInt); init(); } static void addBcr(Path paramPath, int x1, int y1, int x2, int y2, float rate) { int i = (int) (rate * BEZIER_OFFSET); int cx = (x2 + x1) / 2 - i; // 控制点xs int cy = (y2 + y1) / 2 - i; // 控制点y paramPath.quadTo(cx, cy, x2, y2); } static void addBcr2(Path paramPath, int x1, int y1, int x2, int y2, float rate) { int i = (int) (rate * BEZIER_OFFSET); int cx = (x2 + x1) / 2 + i; // 控制点xs int cy = (y2 + y1) / 2 - i; // 控制点y paramPath.quadTo(cx, cy, x2, y2); } public void draw(Canvas paramCanvas) { super.draw(paramCanvas); update(paramCanvas); } void init() { this.path = new Path(); this.paint = new Paint(); this.paint.setAntiAlias(true); this.paint.setColor(Color.parseColor("#2baaff")); } public boolean onTouchEvent(MotionEvent event) { currentX = (int) event.getX(); currentY = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = currentX; startY = currentY; break; case MotionEvent.ACTION_MOVE: // 计算偏移值,然后重新绘制 setOffsetY(currentY - startY); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // 重置界面 setOffsetY(0); startX = startY = currentY = currentX = 0; break; } return true; // super.onTouchEvent(event); } public boolean isReFreshed() { return this.isReFreshed; } public void setOffsetY(int offset) { this.offsetY = offset; if (offsetY >= 0) { invalidate(); } } void update(Canvas paramCanvas) { this.path.reset(); int width = getWidth(); int height = getHeight(); float rate = 1.0F * this.offsetY / height; int r = (int) (R * (1.0F - rate)); // 圆球的半径,动态改变的,当拖拉的时候,r的会根据距离改变,进行变化 this.path.moveTo(width / 2, 0.0F);// 移动到(width/2 , 0)这个点 this.path.arcTo(new RectF(width / 2 - r, 0.0F, r + width / 2, r * 2), -90.0F, 90.0F); // 根据半径r,换出一个四分之一的圆形 int m = (int) (9.0F * (rate * r));// 算出底部的校园与上面的大圆的圆心的距离 if ((m > Y_OFFSET) && (this.onPullRefreshCallback != null)) { // 如果这个距离超过了限制,则可以出发回调 this.onPullRefreshCallback.onCallback(); this.isReFreshed = true; invalidate(); // return; } this.isReFreshed = false; int x2 = (int) (r + width / 2 - rate * r); // 小圆的水平的直径右边的点x坐标 int y = r + m; // 小圆的圆心坐标,y坐标 int x1 = (int) (width / 2 - r + rate * r);// 小圆的水平的直径左边的点x坐标 // 绘制一个贝塞尔曲线 addBcr(this.path, r + width / 2, r, x2, y, rate); int r2 = (x2 - x1) / 2; // 小圆的半径 // 绘制一个半圆 this.path.arcTo(new RectF(x1, y - r2, x2, y + r2), 0.0F, 180.0F); // 绘制一个贝塞尔曲线 addBcr2(this.path, x1, y, width / 2 - r, r, rate); // 在绘制上面的一个四分之一园 this.path.arcTo(new RectF(width / 2 - r, 0.0F, r + width / 2, r * 2), 180.0F, 90.0F); this.path.setFillType(Path.FillType.WINDING); paramCanvas.drawPath(this.path, this.paint); } public void setOnPullRefreshCallback(OnPullRefreshCallback callback) { this.onPullRefreshCallback = callback; } public static abstract interface OnPullRefreshCallback { public abstract void onCallback(); } }
运行效果:
完整代码下载地址:
http://download.csdn.net/detail/xia215266092/8107081