最近看須要作一款下拉刷新的效果,因爲須要和Ios界面保持一致,因此這用安卓的方式實現了ios下的下拉刷新的粘蟲效果。java
最新的安卓手機版本的QQ也有這種相似的效果,就是拖動未讀信息的那個紅色圓圈,拖動近距離的是就有這種粘蟲的效果。ios
下面是安卓版本的嘟嘟App的效果截圖,後面會簡單的介紹下的實現原理優化
原理:this
以下圖所示,在沒有進行下拉的是,顯示的是A圖,其實是一個圓形,當進行向下的拖動的時候,圓形會進行拉伸,這裏簡單用模擬下圓形被用力拉伸的效果。.net
一、被拉伸的圓形,實際上分爲3部分,上面的部分(是個半圓,稍微大點,簡稱爲大圓),中間部分(是一個拉伸的部分,有2條平滑的曲線),下面部分(也是一個半圓,較小,成爲小圓)code
二、當滑動的距離愈來愈大的時候,模擬的力就越大,那麼圓就拉伸越厲害。這樣咱們能夠把上面的大圓和下面的小圓變的愈來愈小。中間部分,變成的愈來愈長。blog
三、拖動過程理解,那麼簡述下繪製的流程,從1點開始繪製,1~2是一個四分之一的圓形,2~3是一個曲線,咱們能夠用貝塞爾曲線來繪製,具體貝塞爾是什麼東西,能夠自行百度,這裏不作解釋。3~4是一個半圓,4~5和2~3同樣,也是一個貝塞爾曲線。5~1和1~2同樣也是四分之一的圓形。ip
上面就是簡單原理,可能實際效果仍是須要優化的,不過原理再次,後面只須要慢慢優化便可,當初實現這個功能也是費了2天時間。get
下面是代碼片斷,具體完整代碼,後面會給出下載連接。it
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