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

smallguang 8年前發布 | 9K 次閱讀 Android開發 移動開發

來自: 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




 本文由用戶 smallguang 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!