高仿IOS下拉刷新的粘虫效果

smallguang 8年前

来自: http://blog.csdn.net//guijiaoba/article/details/40663815


最近看需要做一款下拉刷新的效果,由于需要和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