高仿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




posted @ 2014-11-01 00:05  鬼脚八  阅读(302)  评论(0编辑  收藏  举报